From 4461c7b810ea3a5116557868e25ad89105663760 Mon Sep 17 00:00:00 2001 From: Joscha <34318751+josxha@users.noreply.github.com> Date: Sun, 7 Jan 2024 00:21:58 +0100 Subject: [PATCH] add profile page --- .../Controllers/HomeController.cs | 20 ++++- .../Controllers/WellknownController.cs | 2 +- KratosSelfService/KratosSelfService.csproj | 5 ++ KratosSelfService/Models/models.cs | 8 +- .../Resources/CustomTranslator.de.resx | 3 + .../Resources/CustomTranslator.resx | 3 + .../Services/IdentitySchemaService.cs | 51 +++++++++++ KratosSelfService/Startup.cs | 1 + .../Views/Home/{Home.cshtml => Links.cshtml} | 2 +- KratosSelfService/Views/Home/Profile.cshtml | 51 +++++++++++ .../Views/Sessions/Sessions.cshtml | 27 +++++- .../Views/Settings/Settings.cshtml | 4 +- .../wwwroot/img/Missing_avatar.svg | 90 +++++++++++++++++++ OryUI.sln.DotSettings | 3 +- OryUI.sln.DotSettings.user | 3 + 15 files changed, 261 insertions(+), 12 deletions(-) create mode 100644 KratosSelfService/Services/IdentitySchemaService.cs rename KratosSelfService/Views/Home/{Home.cshtml => Links.cshtml} (92%) create mode 100644 KratosSelfService/Views/Home/Profile.cshtml create mode 100644 KratosSelfService/wwwroot/img/Missing_avatar.svg create mode 100644 OryUI.sln.DotSettings.user diff --git a/KratosSelfService/Controllers/HomeController.cs b/KratosSelfService/Controllers/HomeController.cs index 49c3044..5acf530 100644 --- a/KratosSelfService/Controllers/HomeController.cs +++ b/KratosSelfService/Controllers/HomeController.cs @@ -1,15 +1,20 @@ +using KratosSelfService.Models; +using KratosSelfService.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Ory.Kratos.Client.Model; namespace KratosSelfService.Controllers; -public class HomeController(ILogger logger) : Controller +public class HomeController(IdentitySchemaService schemaService) : Controller { [HttpGet("")] - [AllowAnonymous] - public IActionResult Home() + public async Task Home() { - return View("Home"); + var session = (KratosSession)HttpContext.Items[typeof(KratosSession)]!; + var schema = await schemaService.FetchSchema(session.Identity.SchemaId, + session.Identity.SchemaUrl); + return View("Profile", new ProfileModel(session, IdentitySchemaService.GetTraits(schema))); } [HttpGet("welcome")] @@ -19,4 +24,11 @@ public IActionResult Welcome() // this endpoint exists for parity reason to the ory kratos self service ui return Redirect("~/"); } + + [HttpGet("links")] + [AllowAnonymous] + public IActionResult Links() + { + return View("Links"); + } } \ No newline at end of file diff --git a/KratosSelfService/Controllers/WellknownController.cs b/KratosSelfService/Controllers/WellknownController.cs index cac937f..d82902f 100644 --- a/KratosSelfService/Controllers/WellknownController.cs +++ b/KratosSelfService/Controllers/WellknownController.cs @@ -4,7 +4,7 @@ namespace KratosSelfService.Controllers; -public class WellknownController(ILogger logger, ApiService api) : Controller +public class WellknownController(ApiService api) : Controller { [HttpGet("/.well-known/ory/webauthn.js")] [AllowAnonymous] diff --git a/KratosSelfService/KratosSelfService.csproj b/KratosSelfService/KratosSelfService.csproj index 161c405..6905363 100644 --- a/KratosSelfService/KratosSelfService.csproj +++ b/KratosSelfService/KratosSelfService.csproj @@ -69,6 +69,7 @@ + @@ -82,4 +83,8 @@ + + + + diff --git a/KratosSelfService/Models/models.cs b/KratosSelfService/Models/models.cs index 7008696..24f49b3 100644 --- a/KratosSelfService/Models/models.cs +++ b/KratosSelfService/Models/models.cs @@ -1,4 +1,5 @@ -using Ory.Hydra.Client.Model; +using Newtonsoft.Json.Schema; +using Ory.Hydra.Client.Model; using Ory.Kratos.Client.Model; namespace KratosSelfService.Models; @@ -14,6 +15,11 @@ public record LoginModel( string? logoutUrl ); +public record ProfileModel( + KratosSession session, + Dictionary, JSchema> traitSchemas +); + public record LogoutModel( string logoutChallenge ); diff --git a/KratosSelfService/Resources/CustomTranslator.de.resx b/KratosSelfService/Resources/CustomTranslator.de.resx index 071ac87..21ff9fd 100644 --- a/KratosSelfService/Resources/CustomTranslator.de.resx +++ b/KratosSelfService/Resources/CustomTranslator.de.resx @@ -118,4 +118,7 @@ Authentifizierungsmethoden + + Hallo + \ No newline at end of file diff --git a/KratosSelfService/Resources/CustomTranslator.resx b/KratosSelfService/Resources/CustomTranslator.resx index be60424..49f442c 100644 --- a/KratosSelfService/Resources/CustomTranslator.resx +++ b/KratosSelfService/Resources/CustomTranslator.resx @@ -69,4 +69,7 @@ Authentication methods + + Hello + \ No newline at end of file diff --git a/KratosSelfService/Services/IdentitySchemaService.cs b/KratosSelfService/Services/IdentitySchemaService.cs new file mode 100644 index 0000000..d492870 --- /dev/null +++ b/KratosSelfService/Services/IdentitySchemaService.cs @@ -0,0 +1,51 @@ +using Newtonsoft.Json.Schema; + +namespace KratosSelfService.Services; + +public class IdentitySchemaService +{ + private readonly HttpClient _httpClient = new(); + private readonly Dictionary _schemaCache = new(); + + public async Task FetchSchema(string schemaId, string schemaUri) + { + // check if schema object is cached + if (_schemaCache.TryGetValue(schemaId, out var schema)) + return schema; + var response = await _httpClient.GetStringAsync(schemaUri); + // request and cache new schema object + _schemaCache[schemaId] = JSchema.Parse(response); + return _schemaCache[schemaId]; + } + + public static Dictionary, JSchema> GetTraits(JSchema schema) + { + var traits = schema.Properties["traits"].Properties; + return FlattenTraits(traits, []); + } + + private static Dictionary, JSchema> FlattenTraits(IDictionary traits, + IReadOnlyCollection pathSections) + { + var map = new Dictionary, JSchema>(); + foreach (var (traitKey, trait) in traits) + { + var newPathSections = new List(pathSections) { traitKey }; + switch (trait.Type) + { + case JSchemaType.Object: + foreach (var entry in FlattenTraits(trait.Properties, newPathSections)) + map[entry.Key] = entry.Value; + break; + case JSchemaType.Array: + // TODO support arrays + break; + default: // string, etc. + map[newPathSections] = trait; + break; + } + } + + return map; + } +} \ No newline at end of file diff --git a/KratosSelfService/Startup.cs b/KratosSelfService/Startup.cs index f8681b9..75279d8 100644 --- a/KratosSelfService/Startup.cs +++ b/KratosSelfService/Startup.cs @@ -42,6 +42,7 @@ public void ConfigureServices(IServiceCollection services) // own services services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); } public void Configure(WebApplication app) diff --git a/KratosSelfService/Views/Home/Home.cshtml b/KratosSelfService/Views/Home/Links.cshtml similarity index 92% rename from KratosSelfService/Views/Home/Home.cshtml rename to KratosSelfService/Views/Home/Links.cshtml index 0b45bbb..bf41d54 100644 --- a/KratosSelfService/Views/Home/Home.cshtml +++ b/KratosSelfService/Views/Home/Links.cshtml @@ -1,5 +1,5 @@ @{ - ViewData["Title"] = CustomTranslator.Get("Welcome"); + ViewData["Title"] = CustomTranslator.Get("Links"); Layout = "_CardLayout"; } diff --git a/KratosSelfService/Views/Home/Profile.cshtml b/KratosSelfService/Views/Home/Profile.cshtml new file mode 100644 index 0000000..6b73e0f --- /dev/null +++ b/KratosSelfService/Views/Home/Profile.cshtml @@ -0,0 +1,51 @@ +@using OryAdmin.Extensions +@using Newtonsoft.Json.Linq +@model ProfileModel +@{ + ViewData["Title"] = "View Profile"; + Layout = "_Layout"; + var traits = (JObject)Model.session.Identity.Traits; +} + +
+
+
+
+
+
+

+ @CustomTranslator.Get("Hello") @traits.GetTraitValueFromPath(["name", "first"]) +

+
+
+
+ Profile picture +
+
+
+ + + @foreach (var (schemaPathSections, schema) in Model.traitSchemas) + { + + + + + } + +
@schema.Title@traits.GetTraitValueFromPath(schemaPathSections)
+
+
+
+ +
+
+
+
+
+
\ No newline at end of file diff --git a/KratosSelfService/Views/Sessions/Sessions.cshtml b/KratosSelfService/Views/Sessions/Sessions.cshtml index 2555b8b..52eb05d 100644 --- a/KratosSelfService/Views/Sessions/Sessions.cshtml +++ b/KratosSelfService/Views/Sessions/Sessions.cshtml @@ -1,7 +1,9 @@ -@model SessionsModel +@using UAParser +@model SessionsModel @{ ViewData["Title"] = CustomTranslator.Get("sessions.title"); var totalSessions = Model.OtherSessions.Count + 1; + var uaParser = Parser.GetDefault(); }
@@ -21,7 +23,17 @@ - @string.Join(", ", Model.CurrentSession.Devices.Select(device => device.IpAddress)) + + @foreach (var device in Model.CurrentSession.Devices) + { + var info = uaParser.Parse(device.UserAgent); +

+ @info.Device.Family on @info.OS + @device.IpAddress + @(string.IsNullOrWhiteSpace(device.Location) ? "" : $"({device.Location})") +

+ } + @Model.CurrentSession.AuthenticatedAt @Model.CurrentSession.ExpiresAt @@ -53,6 +65,17 @@ @foreach (var session in Model.OtherSessions) { + + @foreach (var device in session.Devices) + { + var info = uaParser.Parse(device.UserAgent); +

+ @info.Device.Family on @info.OS + @device.IpAddress + @(string.IsNullOrWhiteSpace(device.Location) ? "" : $"({device.Location})") +

+ } + @string.Join(", ", session.Devices.Select(device => device.IpAddress)) @session.AuthenticatedAt @session.ExpiresAt diff --git a/KratosSelfService/Views/Settings/Settings.cshtml b/KratosSelfService/Views/Settings/Settings.cshtml index a116d65..f3118c0 100644 --- a/KratosSelfService/Views/Settings/Settings.cshtml +++ b/KratosSelfService/Views/Settings/Settings.cshtml @@ -35,7 +35,7 @@ } - +
diff --git a/KratosSelfService/wwwroot/img/Missing_avatar.svg b/KratosSelfService/wwwroot/img/Missing_avatar.svg new file mode 100644 index 0000000..4bd61b8 --- /dev/null +++ b/KratosSelfService/wwwroot/img/Missing_avatar.svg @@ -0,0 +1,90 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/OryUI.sln.DotSettings b/OryUI.sln.DotSettings index 90f6839..9ccb9cc 100644 --- a/OryUI.sln.DotSettings +++ b/OryUI.sln.DotSettings @@ -1,2 +1,3 @@  - True \ No newline at end of file + True + True \ No newline at end of file diff --git a/OryUI.sln.DotSettings.user b/OryUI.sln.DotSettings.user new file mode 100644 index 0000000..aa66f8b --- /dev/null +++ b/OryUI.sln.DotSettings.user @@ -0,0 +1,3 @@ + + True + True \ No newline at end of file