Bij het opstarten van de FH portal website, moet de gebruiker zich inloggen om naar de dienstenpagina te kunnen gaan. ADFS zorgt er voor dat hij zich maar één keer hoeft te authenticeren (SSO) en daarmee wordt zijn autorisatie bepaald en vastgesteld voor alle overige pagina's die de gebruiker nodig heeft.
Zie ook als voorbeeld XEL
solution (Helper
en SelfServiceControl
)
Dit zijn de drie mogelijke workflows (scenario's) van het autorisatieproces
- Klant logt op de FH portal aan
- Hij gaat naar
Mijn FloraHolland
pagina en kiest voor bijv.Start webdiensten | Beeldbank voor kwekers
- Als hij een koper of een aanvoerder is, wordt de Beeldbank applicatie gestart
en krijgt hij de
Overzicht Foto's
pagina te zien
- FH medewerker logt in op bijv.
pub_sys2.floraholland.com
onder zijn eigen FH login - Vervolgens kiest hij de klant waarvoor hij
Beeldbank
dienst wil bekijken. - Als deze klant meer dan één relaties heeft, kiest de medewerker de gewenste relatie
en krijgt hij de
Overzicht Foto's
pagina te zien.
Als ontwikkelaar draai je de solution met F5
of Ctrl+F5
en dan begin je als het ware
net als FH medewerker maar dan gelijk bij punt 2.
Er zijn drie mogelijke workflows (scenario's) van het autorisatieproces
- Klant logt op de FH portal aan
- Hij gaat naar
Mijn FloraHolland
pagina en kiest voor bijv.Start webdiensten | Beeldbank voor kwekers
AM applicatie
wordt gestart enDefault.aspx.cs
in zijnSetUser()
methode ontvangtApplicationCookie
uitHttpContext.Current
via
var ac = CookieHelper.GetApplicationCookie();
ApplicationCookie
ziet er als volgt uit:
[Serializable]
public class ApplicationCookie
{
public string IsoLang { get; set; }
public string ApplicationUser { get; set; }
public string ApplicationUrl { get; set; }
}
- Deze cookie heeft in
Web.config
als naam"fh.userlang"
. - De waarde van
ApplicationUser
wordtgeselecteerdeUserID
. Deze waarde heeft een vast formaat als'fh018503'
of'vba07248'
. - Op grond van de
geselecteerdeUserID
kan nu bijv. de naam van de klant opgehaald worden uitld_lkl
(ld_ccn_cld_klt
enld_ccn_klt
). - Daarnaast wordt een ADFS claim ontvangen via STS server. In deze claim zitten
klantnummer
en deklantrolcode
die nodig zijn om te bepalen of deze gebruiker deBeeldbank
applicatie mag gebruiken.
- De authorisatieprocedure voor een FH medewerker is vrijwel identiek aan die
van de externe klant. De
geselecteerdeUserID
hoortin dit geval bij de geselecteerde klant en niet bij de medewerker zelf. - Echter de ADFS claim bevat in dit geval wel de gegevens over de medewerker zelf en niet die van de geselecteerde klant.
- Als je de solution met
F5
ofCtrl+F5
start, wordt je via een default cookie geautoriseerd.
Hier is meer informatie te vinden over de ASP.NET Page Life Cycle
We onderscheiden twee Portal rollen:
Klant
: externe logins via InternetMedewerker
: interne logins op Intranet
Bij het aanloggen op de portal wordt een Portal cookie aangemaakt danwel bijgehouden. Daarin staat de gewenste taal voor de portal website vastgelegd, de loginnaam van de gebruiker en nog een paar andere gegevens die voor ons niet interessant zijn.
- SYS:
pub_sys2.floraholland.com
metsy018503/Fl0r@Holland!
(testklant 99999) - ACC:
pub_acc2.floraholland.com
metac018503/ecbeheer3
(testklant 99999)
Script om wachtwoord uit te lezen:
use el_dbs
select * from asl_ovk
where asl_ovk_usr_idf = 'fh018503'
- server:
BA03T
- oude situatie
- website:
MyFloraHolland-sys-intern
- domein:
ECPRD
- website:
- nieuwe situatie
- website:
MyFloraHolland-sys
- domein:
EC
- website:
Het is de bedoeling dat de oude domein en website uitgefaseerd worden en alle websites gemigreerd zijn naar de nieuwe domein en website
- De applicatie maakt gebruik van web controls
Default.Master
webpagina is het standaard template voor alle applicatie webpagina's. Dit template bestaat uit een aantal ContentPlaceHolder elementen die later worden dynamisch vervangen door de gewenste content.
Informatie wordt tussentijds opgeslagen in de state objects van deze types:
HttpContext
om de cookies te lezenStateManager
wordt gebruikt bij de ADFS autorisatie van de gebruiker maar ook om zijn gebruikersinformatie, bijv. zijn taalvoorkeur of dat hij meerdere klantrelaties heeft, vast te houdenSession
wordt gebruikt om de data van de specifieke schermen vast te houden- NB: Het lijkt erop dat er geen gebruik wordt gemaakt van Application State, so de data is enkel beschikbaar binnen één enkel browser scherm.
DaVinci.Process.TaskStates
worden gebruikt om de acties vd gebruiker bij te houden, bijv. toevoegen of wijzigen van een foto (klopt dat?)
Beeldbank applicatie maakt gebruik van de Application Cookie. Configuratie ervan is
in web.config
te vinden als de volgende setting:
<FhAppSettings>
<All>
<!-- Cookie met user en language -->
<add key="appCookieName" value="fh.userlang" />
</All>
...
</FhAppSettings>
De class die verantwoordelijk is voor navigatie tussen verchillende pagina's is FhNavigation.
Voorbeeld: FhNavigator.NavigateTo(FhSession.Startpage);
- bij het opstarten van Beeldbank in ACC de eeste pagina is OverzichtPartijBeelden.aspx
- bij het opstarten van Beeldbank in LOC de eeste pagina is Main.aspx
Bij het runnen van de solution wordt als eerst deze link in de browser geladen: http://localhost:50587/
Application_Start()
IdentityConfig.Initialize()
FederatedAuthentication.FederationConfigurationCreated
event handler wordt gekoppeld om de data uit de web.config te lezen- Per omgeving wordt een reeks properties gezet op
FederationConfiguration.WsFederationConfiguration
op basis van web.config:- Issuer
- Realm
- Reply
- RequireHttps
- Informatie over de verbinding naar de am_dbs database in DEV wordt vastgelegd
- Sessie naar de ADFS authority wordt gestart via http://localhost:8000/STS/Issue
- Een Literal wordt toegevoegd aan Controls.
Session_Start()
- In de HttpApplicationState wordt gekeken of er tot dusver exceptions aanwezig zijn. Zo ja, einde oefening.
- Zo niet, de sessie wordt opgeschoond d.m.v.
Session.Clear()
- De startpagina wordt bepaald als
`FhSession.Startpage = AmPageLocation.Default;`
En dat is `Default.aspx`.
-
Page_Load -> SetUser()
-
In de uit de HttpContext gelezen cookie zie ik twee keys:
csrftoken
enFedAuth
Echter geenfh.userlang
die in deweb.config
staat gedefineerd. De applicatioCookie blijft daardoor null. -
Daardoor kunnen we de geselecteerdeUserID niet bepalen en wordt de ADFS autorisatie geprobeerd op basis van de claims identity in de HttpContext:
Authoriser.Current = ADFSWebGebruiker.FindByUserid(HttpContext.Current.User.Identity.Name);
Daar staat
name=vba11397
vermeld. Deze claim was uitgegeven door SelfSTS:http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name: vba11397
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/rol: klant
-
De Navigator wordt opgeschoond d.m.v.
Navigator.Clear()
-
De Navigator start VbaWelkom task:
Navigator.Run(WebTasks.VbaWelkom);
- Checkt of
web.config
een dummy authorisatie bevat (ADDummyInfo
setting). - Als deze bestaat, wordt hij overgenomen. Zo niet, dan op line 301 staat een TODO -- naar de database vermeld doch niet geimplementeerd.
- Dan wordt een gebruiker aangemaakt en teruggeven met de volgende waarden:
public const int DEFAULT_KLANTNUMMER = 0;
public const int DEFAULT_KLANTROLKODE = 3;
public const int DEFAULT_KLANTRELATIENUMMER = 17007;
var adfs = new ADFSWebGebruiker(userid, "", "", "", DEFAULT_KLANTNUMMER,
DEFAULT_KLANTROLKODE, DEFAULT_KLANTRELATIENUMMER, "", null);
Altijd FALSE
Page_Load -> Authorizer.LoggedOn()
- LoggedOn = false
LoggedOn is true indien gebruiker object gezet is EN
ger_inforelatie
is gezet. Bij vba11397ger_inforelatie = null
en dus false. - Bij false wordt
VbaAanmelden
webtask geroepen. Maar we komen inExceptionLabel.asmx
terecht.
Execute()
- Er wordt
bool VbaWebADFSAuthoriser.Login(string login, string password)
geroepen die altijd TRUE teruggeeft. var username = ADFSWebGebruiker.GetWebUserName(this.Page);
TaskStates.New
- Gebruiker is leeg => niet geauthoriseerd
- Geen wachtwoord => niet geauthoriseerd
- Dus geauthoriseerd
- Is informatierelatie niet van belang? Klaar.
- Dus wel van belang. Is gebruiker een FH medewerker?
- ja wel -> kijk in de config of impersonatie gewenst is (TODO: uitzoeken)
- nee -> geen impersonatiescherm tonen en klaar.
- ja -> toon dan impersonatiescherm en laat de klantrelatie kiezen. M.a.w. roep 'VbaSelecteerKlantrelatie' zonder parameters en klaar.
- nee hoor -> roep 'VbaSelecteerInformatierelatie' aan met klantrolkode en klantrelatienummer
- ja wel -> kijk in de config of impersonatie gewenst is (TODO: uitzoeken)
-
Execute()
-
Bij een nieuwe webtask wordt geprobeerd om de
Informatierelatie
setting uit web.config op te halen met als waardeon
ofoff
. -
Als dat wenselijk is, worden relaties geraadpleegd:
relaties = Relatie.Zoek(KlantRelatierol, KlantRelatienummer);
Daarvan afhankelijk wordt status gezet:
StateManager.User[AmStates.MeerdereRelaties] = relaties.Length > 1;
Dan wordt gekeken naar het aantal.
- geen -> logout en ga terug naar login
- één -> deze wordt gekozen en teruggegeven
- meer dan één -> vul en toon een combo met relaties
Application_BeginRequest()
- Er wordt ge-faked dat we aangelogd zijn (LoggedOn = true)
- De controle gaat door naar 'VbaAanmelden'
Execute()
- LastStatus = New
- Zie de logica hierboven
- Door naar
VbaSelecteerInformatierelatie
Execute()
- ???
- Nogmaals door naar
VbaAanmelden.asax
Execute()
- LastStatus = OK werd ergens gezet (was New) TODO: where?
- Nu is LastTask = VbaSelecteerInformatierelatie
- Dan mogen we verder want AD autorisatie is gelukt.
Page_Load()
- Deze keer LoggedOn = true
Navigator.Run(AmTasks.Main);
Een aantal zaken kunnen bij XEL
of VBI
solution bekeken worden hoe het moet. Daar is ADFS al geimplementeerd en werkt.
Sommige gegevens zoals de klantnaam worden via een join op ld_ccn_klt_act
en ld_ccn_klt
gelezen.
- AD FS op Windows Server 2012
De onderstaande snippet moet op elke webpagina van de BeeldBank applicatie geplaatst worden
direct naar de open <body>
tag.
<!-- Google Tag Manager -->
<noscript>
<iframe src="//www.googletagmanager.com/ns.html?id=GTM-T2KW75" height="0" width="0" style="display: none; visibility: hidden"></iframe>
</noscript>
<script type="text/javascript">
(function (w, d, s, l, i) {
w[l] = w[l] || []; w[l].push({ 'gtm.start': new Date().getTime(), event: 'gtm.js' });
var f = d.getElementsByTagName(s)[0], j = d.createElement(s), dl = l != 'dataLayer' ? '&l=' + l : '';
j.async = true; j.src = '//www.googletagmanager.com/gtm.js?id=' + i + dl; f.parentNode.insertBefore(j, f);
})(window, document, 'script', 'dataLayer', 'GTM-T2KW75');
</script>
<!-- End Google Tag Manager -->
- ASP.NET 2.0 AJAX Extensions 1.0 geinstalleerd
AjaxControlToolkit
uit Visual SourceSafe opgehaald ($/Ontwikkeling/AZ/AjaxControlToolkit
)- GTM script aan
Default.Master
toegevoegd - ADFSWebGebruiker class gefixt (properties)
- WebGebruiker overal door ADFSWebGebruiker vervangen
- View CldKlantAccount in am_dbs toegevoegd (kopieconform VbiMain)
- Class CldKlantAccount aan Vba.Am.Business toegevoegd met getters
- Class TableCldKlantAccount aan Vba.Am.Data toegevoegd met getters
- View CldKlant in am_dbs toegevoegd (kopieconform VbiMain)
- Class CldKlant aan Vba.Am.Business toegevoegd met getters
- Class TableCldKlant aan Vba.Am.Data toegevoegd met getters
- Testproject Vba.Am.Web.Test toegevoegd voor unit tests
- Alle links moeten werken
- Testen met een klantnummer derde (role = 5)
- Deep links (direct opvoeren in de browser) moeten niet mogelijk zijn en de klant moet terug naar de portal login pagina gestuurd worden.
- Op alle pagina's moet het GTM script aanwezig zijn
namespace Vba.Az.DaVinci.Web.Process
{
public class WebTasks : Tasks
{
public static WebTasks VbaAanmelden;
public static WebTasks VbaInfo;
public static WebTasks VbaMessageNotAutorised;
public static WebTasks VbaOnderhoudenhtmltekst;
public static WebTasks VbaSelecteerInformatierelatie;
public static WebTasks VbaSelecteerKlantrelatie;
public static WebTasks VbaVragen;
public static WebTasks VbaWelkom;
public static WebTasks VbaZoekenhtmltekst;
}
}
namespace Vba.Am.Web
{
public class AmTasks : DaVinci.Process.Tasks
{
// Klant
public static AmTasks Main = new AmTasks("Main", "Tasks/Am/Main");
public static AmTasks OverzichtPartijBeelden = new AmTasks("OverzichtPartijBeelden", "Tasks/Am/OverzichtPartijBeelden");
public static AmTasks DetailsPartijBeeld = new AmTasks("DetailsPartijBeeld", "Tasks/Am/DetailsPartijBeeld");
public static AmTasks ToevoegenProductcode = new AmTasks("ToevoegenProductcode", "Tasks/Am/ToevoegenProductcode");
public static AmTasks SelecteerProductKenmerk = new AmTasks("SelecteerProductKenmerk", "Tasks/Am/SelecteerProductKenmerk");
public static AmTasks SelecteerProductKenmerkWaarde = new AmTasks("SelecteerProductKenmerkWaarde", "Tasks/Am/SelecteerProductKenmerkWaarde");
public static AmTasks ToevoegenFoto = new AmTasks("ToevoegenFoto", "Tasks/Am/ToevoegenFoto");
public static AmTasks ToevoegenFotoMelding = new AmTasks("ToevoegenFotoMelding", "Tasks/Am/ToevoegenFotoMelding");
public static AmTasks ToevoegenKenmerk = new AmTasks("ToevoegenKenmerk", "Tasks/Am/ToevoegenKenmerk");
public static AmTasks FotoResizedVoorbeeld = new AmTasks("FotoResizedVoorbeeld", "Tasks/Am/FotoResizedVoorbeeld");
public static AmTasks Bloemenklok = new AmTasks("Bloemenklok", "Tasks/Am/Bloemenklok");
public static AmTasks Plantenklok = new AmTasks("Plantenklok", "Tasks/Am/Plantenklok");
// Personeel
public static AmTasks OverzichtAanvoerder = new AmTasks("OverzichtAanvoerder", "Tasks/Personeel/OverzichtAanvoerder");
public static AmTasks OverzichtPlanten = new AmTasks("OverzichtPlanten", "Tasks/Personeel/OverzichtPlanten");
public static AmTasks OverzichtBloemen = new AmTasks("OverzichtBloemen", "Tasks/Personeel/OverzichtBloemen");
public static AmTasks OverzichtMutaties = new AmTasks("OverzichtMutaties", "Tasks/Personeel/OverzichtMutaties");
public static AmTasks OverzichtBeelden = new AmTasks("OverzichtBeelden", "Tasks/Personeel/OverzichtBeelden");
public static AmTasks BeeldcontroleAanvoerder = new AmTasks("BeeldcontroleAanvoerder", "Tasks/Personeel/BeeldcontroleAanvoerder");
public static AmTasks BeeldcontroleAanvoerderWijzigen = new AmTasks("BeeldcontroleAanvoerderWijzigen", "Tasks/Personeel/BeeldcontroleAanvoerderWijzigen");
public static AmTasks OverzichtBeeldenPerProductcode = new AmTasks("OverzichtBeeldenPerProductcode", "Tasks/Personeel/OverzichtBeeldenPerProductcode");
// Support
public static AmTasks VraagAntwoord = new AmTasks("VraagAntwoord", "Tasks/Am/VraagAntwoord");
public static AmTasks Servicedesk = new AmTasks("Servicedesk", "Tasks/Am/Servicedesk");
//Diversen/algemeen:
public static AmTasks ToExcel = new AmTasks("ToExcel", "Tasks/Public/ToExcel");
}
}
Het gaat om het punt tussen window en de functienaam.
// verberg bepaalde elementen vooraf aan het printen
function window.onbeforeprint()
{
updatePrintLayout('none');
}
// maak elementen weer zichtbaar na het printen
function window.onafterprint()
{
updatePrintLayout('');
}
Het syntax is niet correct omdat onbeforeprint en onafterprint zijn windows properties. Hieronder heb ik mijn fix geplaatst.
// verberg bepaalde elementen vooraf aan het printen
window.onbeforeprint = function()
{
updatePrintLayout('none');
}
// maak elementen weer zichtbaar na het printen
window.onafterprint = function()
{
updatePrintLayout('');
}
Main.aspx erft van Default.Master.aspx. In Default.Master.cs staat het volgende
ScriptManager.RegisterClientScriptInclude(this.Page, this.GetType(), "VbaScripts", Global.UrlSitePrefix + "Vba/Scripts/VbaScripts.js");
ScriptManager.RegisterStartupScript(this.Page, this.GetType(), "HandleTimeout", "AddTimeoutHandling();", true);
AddTimeoutHandling
wordt in VbaScripts
gedefineerd en vervolgens in HandleTimeout
geroepen.
Waarom komt dat niet naar de afgeleide pagina Main.aspx?
Overigens is dit het geval op alle pagina's, ook in ACC. Deze fouten zijn dus oer oud. :)
TODO
- Wat is het verschil tussen KlantRelatie en InformatieRelatie?
- InformatieRelatie is een klant waarvoor de KlantRelatie mag beelden inzien.
- KlantRelatie is een klant die bijv. door de FH medewerker geselecteerd worden om testdoeleinden of om te helpen
- nog geen volgende vraag
- Vba.Am.Web/Autorisation/ADFSWebGebruiker.cs
- Vba.Am.Web/Autorisation/VbaWebADFSAuthoriser.cs
- Vba.Am.Web/Default.aspx.cs
- Vba.Am.Web/Global.asax.cs
- Vba.Am.Web/IdentityConfig.cs
- Vba.Am.Web/Vba/TasksAlgemeen/VbaAanmelden.aspx.cs
- Vba.Am.Web/Vba/TasksAlgemeen/VbaSelecteerInformatierelatie.aspx.cs
- Vba.Am.Web/Vba/UserControls/MenuAlgemeen.ascx.cs
- Vba.Am.Web/Vba/UserControls/MenuOverige.ascx.cs
- Vba.Am.Web/Vba/UserControls/MenuSpecifiek.ascx.cs
- Vba.Am.Web/Vba/UserControls/SelecteerKlantRelatie.ascx.cs
- Vba.Am.Web/Web.config
- Vba.Am.Web/App_GlobalResources/vba.resources.vbastrings.en.resx
- Vba.Am.Web/App_GlobalResources/vba.resources.vbastrings.nl.resx
- Vba.Am.Web/Autorisation/ADFSWebGebruiker.aspx.cs
- Vba.Am.Web/Autorisation/VbaWebADFSAuthoriser.aspx.cs
- Vba.Am.Web/Tasks/Am/DetailsPartijBeeld.ascx.cs
- Vba.Am.Web/Tasks/Am/OverzichtPartijBeelden.ascx.cs
- Vba.Am.Web/Utilities.cs
- Vba.Am.Web/Vba/TasksAdmin/VbaOnderhoudenhtmltekst.aspx.cs
- Vba.Am.Web/Vba/TasksAlgemeen/VbaSelecteeerInformatieRelatie.aspx.cs
- Vba.Am.Web/Vba/UserControls/Info.ascx.cs
- Vba.Am.Web/Vba/UserControls/Vragen.ascx.cs