6 Commits

104 changed files with 460 additions and 17 deletions
+2 -2
View File
@@ -9,8 +9,8 @@ WORKDIR /src
COPY . . COPY . .
# Restore & publish (self-contained trimming can be added later if desired) # Restore & publish (self-contained trimming can be added later if desired)
RUN dotnet restore RUN dotnet restore SimpleIdp.Server/SimpleIdp.csproj
RUN dotnet publish -c $BUILD_CONFIGURATION -o /app/publish --no-restore RUN dotnet publish SimpleIdp.Server/SimpleIdp.csproj -c $BUILD_CONFIGURATION -o /app/publish --no-restore
# ========================= # =========================
# Runtime stage # Runtime stage
-12
View File
@@ -1,12 +0,0 @@
{
"profiles": {
"SimpleIdp": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost:65455;http://localhost:65456"
}
}
}
@@ -4,6 +4,11 @@
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<ErrorOnDuplicatePublishOutputFiles>false</ErrorOnDuplicatePublishOutputFiles> <ErrorOnDuplicatePublishOutputFiles>false</ErrorOnDuplicatePublishOutputFiles>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<Compile Remove="SimpleIdp.Tests/**" />
<EmbeddedResource Remove="SimpleIdp.Tests/**" />
<None Remove="SimpleIdp.Tests/**" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication" Version="2.3.0" /> <PackageReference Include="Microsoft.AspNetCore.Authentication" Version="2.3.0" />
<PackageReference Include="SimpleIdServer.IdServer.Pwd" Version="6.0.*-*" /> <PackageReference Include="SimpleIdServer.IdServer.Pwd" Version="6.0.*-*" />
@@ -244,4 +249,4 @@
<Generator>PublicResXFileCodeGenerator</Generator> <Generator>PublicResXFileCodeGenerator</Generator>
</EmbeddedResource> </EmbeddedResource>
</ItemGroup> </ItemGroup>
</Project> </Project>
+22
View File
@@ -0,0 +1,22 @@
{
"version": "1.0",
"defaultProvider": "cdnjs",
"libraries": [
{
"library": "jquery@3.6.3",
"destination": "wwwroot/lib/jquery/"
},
{
"library": "bootstrap@5.2.3",
"destination": "wwwroot/lib/bootstrap/"
},
{
"library": "popper.js@2.11.6",
"destination": "wwwroot/lib/popper.js/"
},
{
"library": "font-awesome@6.5.2",
"destination": "wwwroot/lib/fontawesome/"
}
]
}

Before

Width:  |  Height:  |  Size: 9.3 KiB

After

Width:  |  Height:  |  Size: 9.3 KiB

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

@@ -0,0 +1,380 @@
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace SimpleIdp.Tests.Configuration;
public class ConfigurationTests
{
private const string TestConfigPath = "test-config.json";
[TearDown]
public void Cleanup()
{
if (File.Exists(TestConfigPath))
File.Delete(TestConfigPath);
}
[Test]
public void Deserialize_ValidConfig_ReturnsConfig()
{
var config = new IdpConfig(
Api: new ApiConfig("api", "api-audience"),
Scope: new ScopeConfig("scope", "description", new List<ClaimMapperConfig>()),
Client: new ClientConfig("client-id", "client-name", new List<string> { "http://redirect" }),
Users: new List<UserConfig>()
);
var json = SerializeConfig(config);
File.WriteAllText(TestConfigPath, json);
var result = LoadConfigFromFile(TestConfigPath);
Assert.IsNotNull(result);
Assert.That(result.Api.Name, Is.EqualTo(config.Api.Name));
Assert.That(result.Scope.Name, Is.EqualTo(config.Scope.Name));
Assert.That(result.Client.ClientId, Is.EqualTo(config.Client.ClientId));
}
[Test]
public void Deserialize_WithClaimMappers_ParsesClaimMappersCorrectly()
{
var claimMappers = new List<ClaimMapperConfig>
{
new ClaimMapperConfig("claim1", "source1", "string"),
new ClaimMapperConfig("claim2", "source2", "int", true)
};
var config = new IdpConfig(
Api: new ApiConfig("api", "audience"),
Scope: new ScopeConfig("scope", "desc", claimMappers),
Client: new ClientConfig("client", "name", new List<string>()),
Users: new List<UserConfig>()
);
var json = SerializeConfig(config);
File.WriteAllText(TestConfigPath, json);
var result = LoadConfigFromFile(TestConfigPath);
Assert.IsNotNull(result.Scope.ClaimMappers);
Assert.That(result.Scope.ClaimMappers.Count, Is.EqualTo(2));
Assert.That(result.Scope.ClaimMappers[0].TargetClaimPath, Is.EqualTo("claim1"));
Assert.That(result.Scope.ClaimMappers[0].SourceUserAttribute, Is.EqualTo("source1"));
Assert.That(result.Scope.ClaimMappers[0].TokenClaimJsonType, Is.EqualTo("string"));
Assert.That(result.Scope.ClaimMappers[1].TargetClaimPath, Is.EqualTo("claim2"));
Assert.IsTrue(result.Scope.ClaimMappers[1].IsMultiValued);
}
[Test]
public void Deserialize_WithUsers_ParsesUsersCorrectly()
{
var users = new List<UserConfig>
{
new UserConfig(
"user1",
"pass1",
"User One",
"user1@test.com",
"User",
true,
new List<string> { "admin" },
new Dictionary<string, string> { { "role", "admin" } },
new List<ConsentConfig>()
)
};
var config = new IdpConfig(
Api: new ApiConfig("api", "audience"),
Scope: new ScopeConfig("scope", "desc", new List<ClaimMapperConfig>()),
Client: new ClientConfig("client", "name", new List<string>()),
Users: users
);
var json = SerializeConfig(config);
File.WriteAllText(TestConfigPath, json);
var result = LoadConfigFromFile(TestConfigPath);
Assert.IsNotNull(result.Users);
Assert.That(result.Users.Count, Is.EqualTo(1));
Assert.That(result.Users[0].Login, Is.EqualTo("user1"));
Assert.That(result.Users[0].Password, Is.EqualTo("pass1"));
Assert.That(result.Users[0].Name, Is.EqualTo("User One"));
Assert.That(result.Users[0].Email, Is.EqualTo("user1@test.com"));
Assert.IsTrue(result.Users[0].EmailVerified);
Assert.That(result.Users[0].Roles.Count, Is.EqualTo(1));
Assert.That(result.Users[0].Roles[0], Is.EqualTo("admin"));
}
[Test]
public void Deserialize_WithConsents_ParsesConsentsCorrectly()
{
var consents = new List<ConsentConfig>
{
new ConsentConfig("realm1", "client1", "scope1")
};
var users = new List<UserConfig>
{
new UserConfig(
"user1",
"pass1",
"User One",
"user1@test.com",
"User",
true,
new List<string>(),
new Dictionary<string, string>(),
consents
)
};
var config = new IdpConfig(
Api: new ApiConfig("api", "audience"),
Scope: new ScopeConfig("scope", "desc", new List<ClaimMapperConfig>()),
Client: new ClientConfig("client", "name", new List<string>()),
Users: users
);
var json = SerializeConfig(config);
File.WriteAllText(TestConfigPath, json);
var result = LoadConfigFromFile(TestConfigPath);
Assert.IsNotNull(result.Users[0].Consents);
Assert.That(result.Users[0].Consents.Count, Is.EqualTo(1));
Assert.That(result.Users[0].Consents[0].Realm, Is.EqualTo("realm1"));
Assert.That(result.Users[0].Consents[0].ClientId, Is.EqualTo("client1"));
Assert.That(result.Users[0].Consents[0].Scope, Is.EqualTo("scope1"));
}
[Test]
public void Deserialize_CaseInsensitive_ParsesCorrectly()
{
var json = """
{
"api": {
"name": "test-api",
"audience": "test-audience"
},
"scope": {
"name": "test-scope",
"description": "test-desc",
"claimmappers": []
},
"client": {
"clientid": "test-client",
"name": "test client",
"redirecturis": ["http://test.com"],
"ispublic": false
},
"users": []
}
""";
File.WriteAllText(TestConfigPath, json);
var result = LoadConfigFromFile(TestConfigPath);
Assert.IsNotNull(result);
Assert.That(result.Api.Name, Is.EqualTo("test-api"));
Assert.That(result.Client.ClientId, Is.EqualTo("test-client"));
Assert.IsFalse(result.Client.IsPublic);
}
[Test]
public void Deserialize_MissingApiConfig_ReturnsNullApi()
{
var json = """
{
"scope": { "name": "scope", "description": "desc", "claimmappers": [] },
"client": { "clientid": "client", "name": "name", "redirecturis": [] },
"users": []
}
""";
File.WriteAllText(TestConfigPath, json);
var result = LoadConfigFromFile(TestConfigPath);
Assert.IsNull(result.Api);
}
[Test]
public void Deserialize_MissingScopeConfig_ReturnsNullScope()
{
var json = """
{
"api": { "name": "api", "audience": "audience" },
"client": { "clientid": "client", "name": "name", "redirecturis": [] },
"users": []
}
""";
File.WriteAllText(TestConfigPath, json);
var result = LoadConfigFromFile(TestConfigPath);
Assert.IsNull(result.Scope);
}
[Test]
public void Deserialize_MissingClientConfig_ReturnsNullClient()
{
var json = """
{
"api": { "name": "api", "audience": "audience" },
"scope": { "name": "scope", "description": "desc", "claimmappers": [] },
"users": []
}
""";
File.WriteAllText(TestConfigPath, json);
var result = LoadConfigFromFile(TestConfigPath);
Assert.IsNull(result.Client);
}
[Test]
public void Deserialize_EmptyUsersList_ParsesSuccessfully()
{
var config = new IdpConfig(
Api: new ApiConfig("api", "audience"),
Scope: new ScopeConfig("scope", "desc", new List<ClaimMapperConfig>()),
Client: new ClientConfig("client", "name", new List<string>()),
Users: new List<UserConfig>()
);
var json = SerializeConfig(config);
File.WriteAllText(TestConfigPath, json);
var result = LoadConfigFromFile(TestConfigPath);
Assert.IsNotNull(result.Users);
Assert.That(result.Users, Is.Empty);
}
[Test]
public void Deserialize_MultipleUsers_ParsesAllUsers()
{
var users = new List<UserConfig>
{
new UserConfig("user1", "pass1", "User 1", "user1@test.com", "User", true, new List<string>(), new Dictionary<string, string>(), new List<ConsentConfig>()),
new UserConfig("user2", "pass2", "User 2", "user2@test.com", "User", false, new List<string>(), new Dictionary<string, string>(), new List<ConsentConfig>()),
new UserConfig("user3", "pass3", "User 3", "user3@test.com", "User", true, new List<string>(), new Dictionary<string, string>(), new List<ConsentConfig>())
};
var config = new IdpConfig(
Api: new ApiConfig("api", "audience"),
Scope: new ScopeConfig("scope", "desc", new List<ClaimMapperConfig>()),
Client: new ClientConfig("client", "name", new List<string>()),
Users: users
);
var json = SerializeConfig(config);
File.WriteAllText(TestConfigPath, json);
var result = LoadConfigFromFile(TestConfigPath);
Assert.That(result.Users.Count, Is.EqualTo(3));
}
[Test]
public void Deserialize_ClientWithMultipleRedirectUris_ParsesAllUris()
{
var redirectUris = new List<string>
{
"http://redirect1.com",
"http://redirect2.com",
"https://redirect3.com"
};
var config = new IdpConfig(
Api: new ApiConfig("api", "audience"),
Scope: new ScopeConfig("scope", "desc", new List<ClaimMapperConfig>()),
Client: new ClientConfig("client", "name", redirectUris),
Users: new List<UserConfig>()
);
var json = SerializeConfig(config);
File.WriteAllText(TestConfigPath, json);
var result = LoadConfigFromFile(TestConfigPath);
Assert.That(result.Client.RedirectUris.Count, Is.EqualTo(3));
Assert.That(result.Client.RedirectUris, Contains.Item("http://redirect1.com"));
Assert.That(result.Client.RedirectUris, Contains.Item("http://redirect2.com"));
Assert.That(result.Client.RedirectUris, Contains.Item("https://redirect3.com"));
}
private string SerializeConfig(IdpConfig config)
{
var options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = true
};
return JsonSerializer.Serialize(config, options);
}
private IdpConfig LoadConfigFromFile(string path)
{
var json = File.ReadAllText(path);
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
};
return JsonSerializer.Deserialize<IdpConfig>(json, options)
?? throw new InvalidOperationException("Failed to deserialize config");
}
}
public record IdpConfig(
ApiConfig Api,
ScopeConfig Scope,
ClientConfig Client,
List<UserConfig> Users);
public record ApiConfig(
string Name,
string Audience);
public record ScopeConfig(
string Name,
string Description,
List<ClaimMapperConfig> ClaimMappers);
public record ClaimMapperConfig(
string TargetClaimPath,
string SourceUserAttribute,
string TokenClaimJsonType,
bool IsMultiValued = false);
public record ClientConfig(
string ClientId,
string Name,
List<string> RedirectUris,
bool IsPublic = true);
public record UserConfig(
string Login,
string Password,
string Name,
string Email,
string Firstname,
bool EmailVerified,
List<string> Roles,
Dictionary<string, string> Claims,
List<ConsentConfig> Consents);
public record ConsentConfig(
string Realm,
string ClientId,
string Scope);
+3
View File
@@ -0,0 +1,3 @@
# SimpleIdp.Tests
Unit tests for SimpleIdP

Some files were not shown because too many files have changed in this diff Show More