Skip to content

Commit e61b79c

Browse files
authored
Add SQLite database (#21)
1 parent 0442d7b commit e61b79c

7 files changed

Lines changed: 119 additions & 6 deletions

File tree

ARCHITECTURE.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@ The documentation uses [MkDocs](https://www.mkdocs.org/) and [Material for
1212
MkDocs](https://squidfunk.github.io/mkdocs-material/). This documentation is both provided on readthedocs and built
1313
in to the application itself.
1414

15-
The application reads the configuration on start up and then populates an in-memory database with users and OpenID
16-
connect clients. Additionally, users and clients can be created at runtime through the web interface at `/users` and
17-
`/clients` respectively. Note that there is no persistence between application restarts, as the in-memory database is
18-
wiped and a new one is used - any users or clients created at runtime will be lost upon restart.
15+
The application reads the configuration on start up and then populates the database with users and OpenID connect
16+
clients. By default, an in-memory database is used and all data is lost on restart — any users or clients created at
17+
runtime through the web interface will not survive a restart. Optionally, a SQLite database file can be configured to
18+
persist data between restarts; see the [Database configuration](DevOidcToolkit.Documentation/docs/configuration.md)
19+
for details.
1920

2021
The frontend is styled using basic styling, using the [Sakura CSS library](https://github.com/oxalorg/sakura).
2122

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
Versioning](https://semver.org/spec/v2.0.0.html).
66

77
## [Unreleased]
8+
- Add optional SQLite persistence via `Database.SqliteFile` config option; defaults to in-memory when not set (see [#20](https://github.com/BusinessSimulations/dev-oidc-toolkit/issues/20))
89
## [0.5.0]
910
- Add configurable user roles through `DevOidcToolkit__Users__INDEX__Roles__INDEX` (see [#17](https://github.com/BusinessSimulations/dev-oidc-toolkit/pull/17))
1011
- Add runtime user registration at `/users` page (see [#15](https://github.com/BusinessSimulations/dev-oidc-toolkit/issues/15))

DevOidcToolkit.Documentation/docs/configuration.md

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ This is a list of all of the environment variables that can be used to configure
5959
<td>https://fake-issuer.example.com</td>
6060
<td>None (derived from request URL)</td>
6161
</tr>
62+
<tr>
63+
<td>DevOidcToolkit__Database__SqliteFile</td>
64+
<td>The path to the SQLite database file. When set, data is persisted to this file and survives restarts. When not set, an in-memory database is used and all data is lost on restart.</td>
65+
<td>/data/dev-oidc-toolkit.db</td>
66+
<td>None (in-memory)</td>
67+
</tr>
6268
<tr>
6369
<td>DevOidcToolkit__Logging__MinimumLevel</td>
6470
<td>The minimum log level, possible values are Trace, Debug, Information, Warning, Error, Critical.</td>
@@ -204,6 +210,13 @@ details](#example-json-configuration)).
204210
<td>https://fake-issuer.example.com</td>
205211
<td>None</td>
206212
</tr>
213+
<tr>
214+
<td>Database</td>
215+
<td>object</td>
216+
<td>The database configuration, see <a href="#database">Database</a> for more information.</td>
217+
<td>See <a href="#database">Database</a> for more information.</td>
218+
<td>None (in-memory)</td>
219+
</tr>
207220
<tr>
208221
<td>Https</td>
209222
<td>object</td>
@@ -352,6 +365,38 @@ details](#example-json-configuration)).
352365
</tbody>
353366
</table>
354367

368+
#### Database
369+
370+
The database configuration controls how data is stored. By default, an in-memory database is used and all data
371+
(including users and clients created at runtime) is lost when the application stops. Set `SqliteFile` to a file path to
372+
use a SQLite database instead, which persists data between restarts.
373+
374+
!!! note "Limitations"
375+
The SQLite database schema is created automatically on first run using `EnsureCreated`. There are no migrations
376+
supported — if the schema changes in a future version of dev-oidc-toolkit you may need to delete and recreate the
377+
database file.
378+
379+
<table>
380+
<thead>
381+
<tr>
382+
<th>Property</th>
383+
<th>Type</th>
384+
<th>Description</th>
385+
<th>Example</th>
386+
<th>Default Value</th>
387+
</tr>
388+
</thead>
389+
<tbody>
390+
<tr>
391+
<td>SqliteFile</td>
392+
<td>string</td>
393+
<td>Path to the SQLite database file. When set, all data is persisted to this file. When omitted, an in-memory database is used and data is lost on restart.</td>
394+
<td>/data/dev-oidc-toolkit.db</td>
395+
<td>None (in-memory)</td>
396+
</tr>
397+
</tbody>
398+
</table>
399+
355400
#### Users
356401

357402
<table>
@@ -435,6 +480,8 @@ details](#example-json-configuration)).
435480

436481
### Example JSON configuration
437482

483+
In-memory database (default, no persistence):
484+
438485
```json
439486
{
440487
"DevOidcToolkit": {
@@ -459,3 +506,32 @@ details](#example-json-configuration)).
459506
}
460507
}
461508
```
509+
510+
SQLite database (data persists across restarts):
511+
512+
```json
513+
{
514+
"DevOidcToolkit": {
515+
"Port": 8080,
516+
"Database": {
517+
"SqliteFile": "/data/dev-oidc-toolkit.db"
518+
},
519+
"Users": [
520+
{
521+
"Email": "sudo@localhost",
522+
"FirstName": "Test",
523+
"LastName": "User"
524+
}
525+
],
526+
"Clients": [
527+
{
528+
"Id": "test",
529+
"Secret": "ThisIsNotARealSecret",
530+
"RedirectUris": [
531+
"http://localhost:3000/callback"
532+
]
533+
}
534+
]
535+
}
536+
}
537+
```

DevOidcToolkit.Documentation/docs/runtime-management.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,6 @@ URIs are validated on submission to ensure they are properly formatted.
5656

5757
## Important Notes
5858

59-
- **No Persistence**: Users and clients created at runtime exist only in the in-memory database. They will be lost when the application restarts.
59+
- **Persistence**: By default, users and clients created at runtime exist only in the in-memory database and will be lost when the application restarts. To persist runtime changes, configure a [SQLite database](configuration.md#database).
6060
- **Configuration + Runtime**: You can use both configuration-based users/clients and runtime-created ones simultaneously.
6161
- **Role Management**: Roles created at runtime persist for the lifetime of the application and can be assigned to multiple users.

DevOidcToolkit/DevOidcToolkit.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="10.0.2" />
2020
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="10.0.2" />
2121
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="10.0.2" />
22+
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.2" />
2223
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.2">
2324
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
2425
<PrivateAssets>all</PrivateAssets>

DevOidcToolkit/Infrastructure/Configuration/Configuration.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public class DevOidcToolkitConfiguration
1818

1919
[ValidateObjectMembers] public HttpsConfiguration? Https { get; set; }
2020
[ValidateObjectMembers] public LoggingConfiguration Logging { get; set; } = new LoggingConfiguration();
21+
[ValidateObjectMembers] public DatabaseConfiguration Database { get; set; } = new DatabaseConfiguration();
2122
}
2223

2324
public class UserConfiguration
@@ -89,4 +90,9 @@ public class LoggingConfiguration
8990
{
9091
public LogEventLevel MinimumLevel { get; set; } = LogEventLevel.Information;
9192
public bool UseXForwardedForHeader { get; set; } = false;
93+
}
94+
95+
public class DatabaseConfiguration
96+
{
97+
public string? SqliteFile { get; set; }
9298
}

DevOidcToolkit/Program.cs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using DevOidcToolkit.Infrastructure.Database;
66

77
using Microsoft.AspNetCore.Identity;
8+
using Microsoft.Data.Sqlite;
89
using Microsoft.EntityFrameworkCore;
910
using Microsoft.Extensions.FileProviders;
1011

@@ -30,7 +31,18 @@
3031

3132
builder.Services.AddDbContext<DevOidcToolkitContext>(options =>
3233
{
33-
options.UseInMemoryDatabase("dev-auth");
34+
if (config.Database.SqliteFile is not null)
35+
{
36+
var connectionString = new SqliteConnectionStringBuilder
37+
{
38+
DataSource = config.Database.SqliteFile
39+
}.ToString();
40+
options.UseSqlite(connectionString);
41+
}
42+
else
43+
{
44+
options.UseInMemoryDatabase("dev-auth");
45+
}
3446
options.UseOpenIddict();
3547
});
3648

@@ -179,12 +191,23 @@
179191
var services = scope.ServiceProvider;
180192
var db = services.GetRequiredService<DevOidcToolkitContext>();
181193

194+
if (config.Database.SqliteFile is not null)
195+
{
196+
db.Database.EnsureCreated();
197+
}
198+
182199
// Set up users and clients in the DB
183200
var userManager = services.GetRequiredService<UserManager<DevOidcToolkitUser>>();
184201
var roleManager = services.GetRequiredService<RoleManager<IdentityRole>>();
185202
for (var i = 0; i < config.Users.Count; i++)
186203
{
187204
var user = config.Users[i];
205+
206+
if (await userManager.FindByEmailAsync(user.Email) is not null)
207+
{
208+
continue;
209+
}
210+
188211
var userEntity = new DevOidcToolkitUser()
189212
{
190213
Id = i.ToString(),
@@ -215,6 +238,11 @@
215238
var openIddictManager = scope.ServiceProvider.GetRequiredService<IOpenIddictApplicationManager>();
216239
foreach (var client in config.Clients)
217240
{
241+
if (await openIddictManager.FindByClientIdAsync(client.Id) is not null)
242+
{
243+
continue;
244+
}
245+
218246
var clientApp = new OpenIddictApplicationDescriptor()
219247
{
220248
ClientId = client.Id,

0 commit comments

Comments
 (0)