Merge pull request 'Extract IDP configuration to external JSON file' (#1) from claude/update-set-up-logic into main
This commit was merged in pull request #1.
This commit is contained in:
+125
-87
@@ -1,9 +1,10 @@
|
||||
// Copyright (c) SimpleIdServer. All rights reserved.
|
||||
// Copyright (c) SimpleIdServer. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Text.Json;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using SimpleIdServer.IdServer.Builders;
|
||||
@@ -14,83 +15,48 @@ using SimpleIdServer.IdServer.Stores;
|
||||
|
||||
var corsPolicyName = "AllowAll";
|
||||
|
||||
// ── Load configuration ────────────────────────────────────────────────────────
|
||||
var configPath = Path.Combine(AppContext.BaseDirectory, "config", "idp-config.json");
|
||||
if (!File.Exists(configPath))
|
||||
throw new FileNotFoundException(
|
||||
$"IDP configuration file not found at '{configPath}'. " +
|
||||
"Mount your config file to /app/config/idp-config.json when running in Docker.");
|
||||
|
||||
var idpConfig = JsonSerializer.Deserialize<IdpConfig>(
|
||||
File.ReadAllText(configPath),
|
||||
new JsonSerializerOptions { PropertyNameCaseInsensitive = true })
|
||||
?? throw new InvalidOperationException("Failed to deserialize IDP configuration.");
|
||||
|
||||
// ── Build realm ───────────────────────────────────────────────────────────────
|
||||
var realm = RealmBuilder.CreateMaster().Build();
|
||||
|
||||
//api.Audience = "urn:bighand:api:bi:portal";
|
||||
|
||||
var users = new List<User>
|
||||
{
|
||||
UserBuilder
|
||||
.Create("administrator", "password", "Administrator")
|
||||
.SetEmail("adm@mail.com")
|
||||
.SetFirstname("Administrator")
|
||||
.AddRole("BI.PORTAL_ADMIN")
|
||||
.AddRole("BI.TENANT_ADMIN")
|
||||
.AddClaim("tid", "cbaa13c2-e95b-470a-bbcb-18911d5a6025")
|
||||
.AddClaim("aud","urn:bighand:api:bi:portal")
|
||||
.AddConsent("master","212C9DB96C2A4B6DA0AFDB2222F6EEAA.bighand.com","bi.portal")
|
||||
.SetEmailVerified(true)
|
||||
.Build(),
|
||||
|
||||
};
|
||||
|
||||
var rUser = new RealmUser
|
||||
{
|
||||
Realm = realm,
|
||||
User = users[0],
|
||||
};
|
||||
|
||||
users[0].Realms.Add(rUser);
|
||||
|
||||
|
||||
// ── Build API resource ────────────────────────────────────────────────────────
|
||||
var api = new ApiResource
|
||||
{
|
||||
Realms = { realm },
|
||||
Name = "BI Portal API",
|
||||
Audience = "urn:bighand:api:bi:portal"
|
||||
Name = idpConfig.Api.Name,
|
||||
Audience = idpConfig.Api.Audience
|
||||
};
|
||||
|
||||
//var scopes = new List<Scope> { ScopeBuilder.CreateRoleScope(clients[0], "bi.portal", "").Build() };
|
||||
var biScope = new Scope()
|
||||
// ── Build custom scope with claim mappers ─────────────────────────────────────
|
||||
var biScope = new Scope
|
||||
{
|
||||
Realms = { realm },
|
||||
ApiResources = { api },
|
||||
Protocol = ScopeProtocols.OAUTH,
|
||||
Type = ScopeTypes.APIRESOURCE,
|
||||
Name = "bi.portal",
|
||||
Description = "BI Portal Scope",
|
||||
ClaimMappers =
|
||||
Name = idpConfig.Scope.Name,
|
||||
Description = idpConfig.Scope.Description,
|
||||
ClaimMappers = idpConfig.Scope.ClaimMappers.Select(cm => new ScopeClaimMapper
|
||||
{
|
||||
new ScopeClaimMapper()
|
||||
{
|
||||
IncludeInAccessToken = true,
|
||||
TokenClaimJsonType = TokenClaimJsonTypes.STRING,
|
||||
TargetClaimPath = "roles",
|
||||
MapperType = MappingRuleTypes.USERATTRIBUTE,
|
||||
IsMultiValued=true,
|
||||
SourceUserAttribute = "role",
|
||||
SourceUserProperty = "role",
|
||||
},
|
||||
new ScopeClaimMapper()
|
||||
{
|
||||
IncludeInAccessToken = true,
|
||||
TokenClaimJsonType = TokenClaimJsonTypes.STRING,
|
||||
TargetClaimPath = "tid",
|
||||
MapperType = MappingRuleTypes.USERATTRIBUTE,
|
||||
SourceUserAttribute = "tid",
|
||||
SourceUserProperty = "tid",
|
||||
},
|
||||
new ScopeClaimMapper()
|
||||
{
|
||||
IncludeInAccessToken = true,
|
||||
TokenClaimJsonType = TokenClaimJsonTypes.STRING,
|
||||
TargetClaimPath = "upn",
|
||||
MapperType = MappingRuleTypes.USERATTRIBUTE,
|
||||
SourceUserAttribute = "email",
|
||||
SourceUserProperty = "email",
|
||||
},
|
||||
|
||||
},
|
||||
IncludeInAccessToken = true,
|
||||
TokenClaimJsonType = Enum.Parse<TokenClaimJsonTypes>(cm.TokenClaimJsonType, ignoreCase: true),
|
||||
TargetClaimPath = cm.TargetClaimPath,
|
||||
MapperType = MappingRuleTypes.USERATTRIBUTE,
|
||||
IsMultiValued = cm.IsMultiValued,
|
||||
SourceUserAttribute = cm.SourceUserAttribute,
|
||||
SourceUserProperty = cm.SourceUserAttribute,
|
||||
}).ToList(),
|
||||
};
|
||||
|
||||
api.Scopes.Add(biScope);
|
||||
@@ -99,41 +65,69 @@ api.Realms.Add(realm);
|
||||
var scopes = new List<Scope>
|
||||
{
|
||||
new Scope("openid") { Realms = { realm } },
|
||||
new Scope("profile"){ Realms = { realm } },
|
||||
new Scope("offline_access"){ Realms = { realm } },
|
||||
new Scope("profile") { Realms = { realm } },
|
||||
new Scope("offline_access") { Realms = { realm } },
|
||||
biScope
|
||||
};
|
||||
var clients = new List<Client>
|
||||
|
||||
// ── Build client ──────────────────────────────────────────────────────────────
|
||||
var client = ClientBuilder
|
||||
.BuildUserAgentClient(
|
||||
idpConfig.Client.ClientId,
|
||||
null,
|
||||
realm,
|
||||
idpConfig.Client.RedirectUris.ToArray())
|
||||
.SetClientName(idpConfig.Client.Name)
|
||||
.AddScope(scopes.ToArray())
|
||||
.AddRefreshToken()
|
||||
.Build();
|
||||
|
||||
client.IsPublic = idpConfig.Client.IsPublic;
|
||||
client.Realms.Add(realm);
|
||||
|
||||
var clients = new List<Client> { client };
|
||||
|
||||
// ── Build users ───────────────────────────────────────────────────────────────
|
||||
var users = idpConfig.Users.Select(u =>
|
||||
{
|
||||
ClientBuilder
|
||||
.BuildUserAgentClient("212C9DB96C2A4B6DA0AFDB2222F6EEAA.bighand.com", null, realm, new[] { "http://localhost:4200/loggedin" })
|
||||
.SetClientName("BI Portal")
|
||||
.AddScope(scopes.ToArray())
|
||||
.AddRefreshToken()
|
||||
.Build(),
|
||||
};
|
||||
var userBuilder = UserBuilder
|
||||
.Create(u.Login, u.Password, u.Name)
|
||||
.SetEmail(u.Email)
|
||||
.SetFirstname(u.Firstname)
|
||||
.SetEmailVerified(u.EmailVerified);
|
||||
|
||||
foreach (var role in u.Roles)
|
||||
userBuilder = userBuilder.AddRole(role);
|
||||
|
||||
clients[0].IsPublic = true;
|
||||
clients[0].Realms.Add(realm);
|
||||
foreach (var claim in u.Claims)
|
||||
userBuilder = userBuilder.AddClaim(claim.Key, claim.Value);
|
||||
|
||||
realm.Clients.Add(clients[0]);
|
||||
foreach (var consent in u.Consents)
|
||||
userBuilder = userBuilder.AddConsent(consent.Realm, consent.ClientId, consent.Scope);
|
||||
|
||||
return userBuilder.Build();
|
||||
}).ToList();
|
||||
|
||||
// ── Wire up realm relationships ───────────────────────────────────────────────
|
||||
foreach (var user in users)
|
||||
{
|
||||
var rUser = new RealmUser { Realm = realm, User = user };
|
||||
user.Realms.Add(rUser);
|
||||
realm.Users.Add(rUser);
|
||||
}
|
||||
|
||||
realm.Clients.Add(client);
|
||||
realm.ApiResources.Add(api);
|
||||
realm.Users.Add(rUser);
|
||||
scopes.ForEach(s => realm.Scopes.Add(s));
|
||||
|
||||
|
||||
// ── Configure services ────────────────────────────────────────────────────────
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.Services.AddCors(options =>
|
||||
{
|
||||
options.AddPolicy(
|
||||
name: corsPolicyName,
|
||||
policy =>
|
||||
{
|
||||
policy.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader();
|
||||
}
|
||||
);
|
||||
policy => policy.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
|
||||
});
|
||||
|
||||
builder
|
||||
@@ -146,7 +140,6 @@ builder
|
||||
.AddInMemoryLanguages(DefaultLanguages.All)
|
||||
.AddPwdAuthentication(true);
|
||||
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
using (var sc = app.Services.CreateScope())
|
||||
@@ -160,3 +153,48 @@ app.UseSid();
|
||||
app.UseCors(corsPolicyName);
|
||||
|
||||
await app.RunAsync();
|
||||
|
||||
// ── Configuration model ───────────────────────────────────────────────────────
|
||||
|
||||
record IdpConfig(
|
||||
ApiConfig Api,
|
||||
ScopeConfig Scope,
|
||||
ClientConfig Client,
|
||||
List<UserConfig> Users);
|
||||
|
||||
record ApiConfig(
|
||||
string Name,
|
||||
string Audience);
|
||||
|
||||
record ScopeConfig(
|
||||
string Name,
|
||||
string Description,
|
||||
List<ClaimMapperConfig> ClaimMappers);
|
||||
|
||||
record ClaimMapperConfig(
|
||||
string TargetClaimPath,
|
||||
string SourceUserAttribute,
|
||||
string TokenClaimJsonType,
|
||||
bool IsMultiValued = false);
|
||||
|
||||
record ClientConfig(
|
||||
string ClientId,
|
||||
string Name,
|
||||
List<string> RedirectUris,
|
||||
bool IsPublic = true);
|
||||
|
||||
record UserConfig(
|
||||
string Login,
|
||||
string Password,
|
||||
string Name,
|
||||
string Email,
|
||||
string Firstname,
|
||||
bool EmailVerified,
|
||||
List<string> Roles,
|
||||
Dictionary<string, string> Claims,
|
||||
List<ConsentConfig> Consents);
|
||||
|
||||
record ConsentConfig(
|
||||
string Realm,
|
||||
string ClientId,
|
||||
string Scope);
|
||||
|
||||
@@ -10,6 +10,11 @@
|
||||
<PackageReference Include="SimpleIdServer.IdServer" Version="6.0.*-*" />
|
||||
<PackageReference Include="Microsoft.Web.LibraryManager.Build" Version="2.1.175" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="config\idp-config.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Update="Resources\AccountsResource.Designer.cs">
|
||||
<DependentUpon>AccountsResource.resx</DependentUpon>
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
{
|
||||
"realm": "master",
|
||||
"api": {
|
||||
"name": "BI Portal API",
|
||||
"audience": "urn:bighand:api:bi:portal"
|
||||
},
|
||||
"scope": {
|
||||
"name": "bi.portal",
|
||||
"description": "BI Portal Scope",
|
||||
"claimMappers": [
|
||||
{
|
||||
"targetClaimPath": "roles",
|
||||
"sourceUserAttribute": "role",
|
||||
"tokenClaimJsonType": "STRING",
|
||||
"isMultiValued": true
|
||||
},
|
||||
{
|
||||
"targetClaimPath": "tid",
|
||||
"sourceUserAttribute": "tid",
|
||||
"tokenClaimJsonType": "STRING",
|
||||
"isMultiValued": false
|
||||
},
|
||||
{
|
||||
"targetClaimPath": "upn",
|
||||
"sourceUserAttribute": "email",
|
||||
"tokenClaimJsonType": "STRING",
|
||||
"isMultiValued": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"client": {
|
||||
"clientId": "212C9DB96C2A4B6DA0AFDB2222F6EEAA.bighand.com",
|
||||
"name": "BI Portal",
|
||||
"redirectUris": [
|
||||
"http://localhost:4200/loggedin"
|
||||
],
|
||||
"isPublic": true
|
||||
},
|
||||
"users": [
|
||||
{
|
||||
"login": "administrator",
|
||||
"password": "password",
|
||||
"name": "Administrator",
|
||||
"email": "adm@mail.com",
|
||||
"firstname": "Administrator",
|
||||
"emailVerified": true,
|
||||
"roles": [
|
||||
"BI.PORTAL_ADMIN",
|
||||
"BI.TENANT_ADMIN"
|
||||
],
|
||||
"claims": {
|
||||
"tid": "cbaa13c2-e95b-470a-bbcb-18911d5a6025",
|
||||
"aud": "urn:bighand:api:bi:portal"
|
||||
},
|
||||
"consents": [
|
||||
{
|
||||
"realm": "master",
|
||||
"clientId": "212C9DB96C2A4B6DA0AFDB2222F6EEAA.bighand.com",
|
||||
"scope": "bi.portal"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user