Skip to content

Commit 7049077

Browse files
Update the administration website to manage federation entities
1 parent 72fb837 commit 7049077

85 files changed

Lines changed: 1321 additions & 53 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/IdServer/SimpleIdServer.IdServer.Domains/TranslatableConverter.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions
6868
writer.WriteNumber(prop.Item2, (double)obj);
6969
else if (propertyType == typeof(DateTime))
7070
writer.WriteString(prop.Item2, (DateTime)obj);
71+
else if (propertyType == typeof(DateTime?) && obj != null)
72+
writer.WriteString(prop.Item2, (DateTime)obj);
7173
else if (TryGetEnumType(propertyType, out Type resultType))
7274
writer.WriteString(prop.Item2, Enum.GetName(resultType, obj));
7375
else if (propertyType.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>)))
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Copyright (c) SimpleIdServer. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
3+
using Newtonsoft.Json;
4+
5+
namespace SimpleIdServer.IdServer.Federation.Apis.FederationEntity;
6+
7+
public class AddTrustedAnchorRequest
8+
{
9+
[JsonProperty("url")]
10+
public string Url { get; set; } = null!;
11+
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
// Copyright (c) SimpleIdServer. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
3+
using Microsoft.AspNetCore.Mvc;
4+
using Microsoft.Extensions.Logging;
5+
using SimpleIdServer.IdServer.Api;
6+
using SimpleIdServer.IdServer.Exceptions;
7+
using SimpleIdServer.IdServer.Helpers;
8+
using SimpleIdServer.IdServer.Jwt;
9+
using SimpleIdServer.IdServer.Stores;
10+
using SimpleIdServer.OpenidFederation.Clients;
11+
using SimpleIdServer.OpenidFederation.Stores;
12+
using System.Net;
13+
using System.Text.Json;
14+
15+
namespace SimpleIdServer.IdServer.Federation.Apis.FederationEntity;
16+
17+
public class FederationEntitiesController : BaseController
18+
{
19+
private readonly IFederationEntityStore _federationEntityStore;
20+
private readonly ITransactionBuilder _transactionBuilder;
21+
private readonly ILogger<FederationEntitiesController> _logger;
22+
private readonly IdServer.Helpers.IHttpClientFactory _httpClientFactory;
23+
24+
public FederationEntitiesController(
25+
IFederationEntityStore federationEntityStore,
26+
ITransactionBuilder transactionBuilder,
27+
ILogger<FederationEntitiesController> logger,
28+
IdServer.Helpers.IHttpClientFactory httpClientFactory,
29+
ITokenRepository tokenRepository,
30+
IJwtBuilder jwtBuilder) : base(tokenRepository, jwtBuilder)
31+
{
32+
_federationEntityStore = federationEntityStore;
33+
_transactionBuilder = transactionBuilder;
34+
_logger = logger;
35+
_httpClientFactory = httpClientFactory;
36+
}
37+
38+
[HttpPost]
39+
public async Task<IActionResult> AddTrustAnchor([FromRoute] string prefix, [FromBody] AddTrustedAnchorRequest request, CancellationToken cancellationToken)
40+
{
41+
prefix = prefix ?? Constants.DefaultRealm;
42+
try
43+
{
44+
await CheckAccessToken(prefix, IdServerFederationConstants.StandardScopes.FederationEntities.Name);
45+
if (request == null) throw new OAuthException(ErrorCodes.INVALID_REQUEST, IdServer.Resources.Global.InvalidIncomingRequest);
46+
if (string.IsNullOrWhiteSpace(request.Url)) throw new OAuthException(ErrorCodes.INVALID_REQUEST, string.Format(IdServer.Resources.Global.MissingParameter, "url"));
47+
var federationEntity = await _federationEntityStore.GetByUrl(prefix, request.Url, cancellationToken);
48+
if (federationEntity != null) throw new OAuthException(ErrorCodes.INVALID_REQUEST, Resources.Global.FederationEntityAlreadyExists);
49+
using(var resolver = TrustChainResolver.New(_httpClientFactory.GetHttpClient()))
50+
{
51+
using (var transaction = _transactionBuilder.Build())
52+
{
53+
var openidFederation = await resolver.ResolveOpenidFederation(request.Url, cancellationToken);
54+
if (openidFederation == null) throw new OAuthException(ErrorCodes.INVALID_REQUEST, Resources.Global.OpenidFederationCannotBeResolved);
55+
federationEntity = new SimpleIdServer.OpenidFederation.Domains.FederationEntity
56+
{
57+
Id = Guid.NewGuid().ToString(),
58+
IsSubordinate = false,
59+
Realm = prefix,
60+
Sub = request.Url,
61+
CreateDateTime = DateTime.UtcNow
62+
};
63+
_federationEntityStore.Add(federationEntity);
64+
await transaction.Commit(cancellationToken);
65+
return new ContentResult
66+
{
67+
Content = JsonSerializer.Serialize(federationEntity),
68+
ContentType = "application/json",
69+
StatusCode = (int)HttpStatusCode.Created
70+
};
71+
}
72+
}
73+
}
74+
catch(InvalidOperationException ex)
75+
{
76+
_logger.LogError(ex.ToString());
77+
return BuildError(ex);
78+
}
79+
catch (OAuthException ex)
80+
{
81+
_logger.LogError(ex.ToString());
82+
return BuildError(ex);
83+
}
84+
}
85+
86+
[HttpDelete]
87+
public async Task<IActionResult> Delete([FromRoute] string prefix, string id, CancellationToken cancellationToken)
88+
{
89+
prefix = prefix ?? Constants.DefaultRealm;
90+
try
91+
{
92+
using (var transaction = _transactionBuilder.Build())
93+
{
94+
await CheckAccessToken(prefix, IdServerFederationConstants.StandardScopes.FederationEntities.Name);
95+
var federationEntity = await _federationEntityStore.Get(id, cancellationToken);
96+
if (federationEntity == null) throw new OAuthException(HttpStatusCode.NotFound, ErrorCodes.INVALID_REQUEST, Resources.Global.FederationEntityUnknown);
97+
_federationEntityStore.Remove(federationEntity);
98+
await transaction.Commit(cancellationToken);
99+
return NoContent();
100+
}
101+
}
102+
catch (OAuthException ex)
103+
{
104+
_logger.LogError(ex.ToString());
105+
return BuildError(ex);
106+
}
107+
}
108+
109+
[HttpPost]
110+
public async Task<IActionResult> SearchFederationEntities([FromRoute] string prefix, [FromBody] SearchRequest request, CancellationToken cancellationToken)
111+
{
112+
prefix = prefix ?? Constants.DefaultRealm;
113+
try
114+
{
115+
await CheckAccessToken(prefix, IdServerFederationConstants.StandardScopes.FederationEntities.Name);
116+
var result = await _federationEntityStore.Search(prefix, request, cancellationToken);
117+
return new OkObjectResult(result);
118+
}
119+
catch (OAuthException ex)
120+
{
121+
_logger.LogError(ex.ToString());
122+
return BuildError(ex);
123+
}
124+
}
125+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright (c) SimpleIdServer. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
3+
4+
using SimpleIdServer.IdServer.Domains;
5+
using static SimpleIdServer.IdServer.Constants;
6+
7+
namespace SimpleIdServer.IdServer.Federation;
8+
9+
public static class IdServerFederationConstants
10+
{
11+
public static class StandardScopes
12+
{
13+
public static Scope FederationEntities = new Scope
14+
{
15+
Id = Guid.NewGuid().ToString(),
16+
Type = ScopeTypes.APIRESOURCE,
17+
Name = "federation_entities",
18+
Realms = new List<Domains.Realm>
19+
{
20+
StandardRealms.Master
21+
},
22+
Protocol = ScopeProtocols.OAUTH,
23+
IsExposedInConfigurationEdp = true,
24+
CreateDateTime = DateTime.UtcNow,
25+
UpdateDateTime = DateTime.UtcNow
26+
};
27+
}
28+
}

src/IdServer/SimpleIdServer.IdServer.Federation/Resources/Global.Designer.cs

Lines changed: 27 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/IdServer/SimpleIdServer.IdServer.Federation/Resources/Global.resx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,12 @@
123123
<data name="ClientRegistrationTypesMustBeSpecified" xml:space="preserve">
124124
<value>the client_registration_types must be specified by the RP entity statement</value>
125125
</data>
126+
<data name="FederationEntityAlreadyExists" xml:space="preserve">
127+
<value>federation entity already exists</value>
128+
</data>
129+
<data name="FederationEntityUnknown" xml:space="preserve">
130+
<value>the federation entity doesn't exist</value>
131+
</data>
126132
<data name="InvalidIssuer" xml:space="preserve">
127133
<value>the issuer is not valid</value>
128134
</data>
@@ -138,4 +144,7 @@
138144
<data name="OnlyFollowingAuthMethodsAreSupported" xml:space="preserve">
139145
<value>only the following authentication methods {0} are supported</value>
140146
</data>
147+
<data name="OpenidFederationCannotBeResolved" xml:space="preserve">
148+
<value>the openid-federation cannot be resolved</value>
149+
</data>
141150
</root>

src/IdServer/SimpleIdServer.IdServer.Federation/WebApplicationExtensions.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,15 @@ public static WebApplication UseOpenidFederation(this WebApplication webApplicat
2323
webApplication.SidMapControllerRoute("federationRegistration",
2424
pattern: (usePrefix ? "{prefix}/" : string.Empty) + OpenidFederationConstants.EndPoints.FederationRegistration,
2525
defaults: new { controller = "FederationRegistration", action = "Post" });
26+
webApplication.SidMapControllerRoute("addFederationEntity",
27+
pattern: (usePrefix ? "{prefix}/" : string.Empty) + OpenidFederationConstants.EndPoints.FederationEntities + "/trustanchors",
28+
defaults: new { controller = "FederationEntities", action = "AddTrustAnchor" });
29+
webApplication.SidMapControllerRoute("searchFederationEntities",
30+
pattern: (usePrefix ? "{prefix}/" : string.Empty) + OpenidFederationConstants.EndPoints.FederationEntities + "/.search",
31+
defaults: new { controller = "FederationEntities", action = "SearchFederationEntities" });
32+
webApplication.SidMapControllerRoute("removeFederationEntity",
33+
pattern: (usePrefix ? "{prefix}/" : string.Empty) + OpenidFederationConstants.EndPoints.FederationEntities + "/{id}",
34+
defaults: new { controller = "FederationEntities", action = "Delete" });
2635
if (federationOpts.IsFederationEnabled)
2736
{
2837
webApplication.SidMapControllerRoute("federationFetch",

src/IdServer/SimpleIdServer.IdServer/Stores/SearchParameter.cs renamed to src/IdServer/SimpleIdServer.IdServer.Helpers/SearchRequest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
using System.Text.Json.Serialization;
55

6-
namespace SimpleIdServer.IdServer.Stores;
6+
namespace SimpleIdServer.IdServer.Helpers;
77

88
public class SearchRequest
99
{

src/IdServer/SimpleIdServer.IdServer/Stores/SearchResult.cs renamed to src/IdServer/SimpleIdServer.IdServer.Helpers/SearchResult.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
// Copyright (c) SimpleIdServer. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
3-
using System.Collections.Generic;
43
using System.Text.Json.Serialization;
54

6-
namespace SimpleIdServer.IdServer.Stores;
5+
namespace SimpleIdServer.IdServer.Helpers;
76

87
public class SearchResult<TContent>
98
{

src/IdServer/SimpleIdServer.IdServer.Startup/IdServerConfiguration.cs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,8 @@ public class IdServerConfiguration
103103
SimpleIdServer.IdServer.Constants.StandardScopes.CredentialInstances,
104104
SimpleIdServer.IdServer.Constants.StandardScopes.DeferredCreds,
105105
UniversityDegreeScope,
106-
CtWalletScope
106+
CtWalletScope,
107+
SimpleIdServer.IdServer.Federation.IdServerFederationConstants.StandardScopes.FederationEntities
107108
};
108109

109110
public static ICollection<User> Users => new List<User>
@@ -158,7 +159,8 @@ public class IdServerConfiguration
158159
SimpleIdServer.IdServer.Constants.StandardScopes.CertificateAuthorities,
159160
SimpleIdServer.IdServer.Constants.StandardScopes.Clients,
160161
SimpleIdServer.IdServer.Constants.StandardScopes.Realms,
161-
SimpleIdServer.IdServer.Constants.StandardScopes.Groups).Build(),
162+
SimpleIdServer.IdServer.Constants.StandardScopes.Groups,
163+
SimpleIdServer.IdServer.Federation.IdServerFederationConstants.StandardScopes.FederationEntities).Build(),
162164
ClientBuilder.BuildTraditionalWebsiteClient("swaggerClient", "password", null, "https://localhost:5001/swagger/oauth2-redirect.html", "https://localhost:5001/(.*)/swagger/oauth2-redirect.html", "http://localhost").AddScope(
163165
SimpleIdServer.IdServer.Constants.StandardScopes.Provisioning,
164166
SimpleIdServer.IdServer.Constants.StandardScopes.Users,
@@ -173,7 +175,8 @@ public class IdServerConfiguration
173175
SimpleIdServer.IdServer.Constants.StandardScopes.CertificateAuthorities,
174176
SimpleIdServer.IdServer.Constants.StandardScopes.Clients,
175177
SimpleIdServer.IdServer.Constants.StandardScopes.Realms,
176-
SimpleIdServer.IdServer.Constants.StandardScopes.Groups).Build(),
178+
SimpleIdServer.IdServer.Constants.StandardScopes.Groups,
179+
SimpleIdServer.IdServer.Federation.IdServerFederationConstants.StandardScopes.FederationEntities).Build(),
177180
ClientBuilder.BuildTraditionalWebsiteClient("postman", "password", null, "http://localhost").EnableClientGrantType().AddScope(
178181
SimpleIdServer.IdServer.Constants.StandardScopes.Provisioning,
179182
SimpleIdServer.IdServer.Constants.StandardScopes.Users,
@@ -188,7 +191,8 @@ public class IdServerConfiguration
188191
SimpleIdServer.IdServer.Constants.StandardScopes.CertificateAuthorities,
189192
SimpleIdServer.IdServer.Constants.StandardScopes.Clients,
190193
SimpleIdServer.IdServer.Constants.StandardScopes.Realms,
191-
SimpleIdServer.IdServer.Constants.StandardScopes.Groups).Build(),
194+
SimpleIdServer.IdServer.Constants.StandardScopes.Groups,
195+
SimpleIdServer.IdServer.Federation.IdServerFederationConstants.StandardScopes.FederationEntities).Build(),
192196
WsClientBuilder.BuildWsFederationClient("urn:website").SetClientName("NAME").Build(),
193197
ClientBuilder.BuildUserAgentClient("oauth", "password", null, "https://oauth.tools/callback/code")
194198
.AddScope(SimpleIdServer.IdServer.Constants.StandardScopes.OpenIdScope, SimpleIdServer.IdServer.Constants.StandardScopes.Profile)
@@ -272,7 +276,8 @@ public class IdServerConfiguration
272276
Id = Guid.NewGuid().ToString(),
273277
IsSubordinate = false,
274278
Realm = IdServer.Constants.DefaultRealm,
275-
Sub = "http://localhost:7000"
279+
Sub = "http://localhost:7000",
280+
CreateDateTime = DateTime.UtcNow
276281
}
277282
};
278283
}

0 commit comments

Comments
 (0)