Merge branch 'devel' into sqlite

This commit is contained in:
2025-02-27 22:33:01 +01:00
9 changed files with 103 additions and 11 deletions

View File

@@ -22,15 +22,44 @@ Im Gegensatz zum [Main-Branch][main], der keine DB-Abhängigkeiten enthält,
Leider werden durch die SQLite-Bibliothek weitere Transitive Abhängigkeiten eingebunden, Leider werden durch die SQLite-Bibliothek weitere Transitive Abhängigkeiten eingebunden,
die das JAR ein wenig aufblähen. die das JAR ein wenig aufblähen.
## Motivation
Natürlich gibt es freie Implementierungen des OIDC-Protokolls.
Der Platzhirsch auf dem Open-Source-Markt dürfte wohl [Keycloak] sein.
Allerdings ist Keykloak alles andere als einfach zu konfigurieren und dürfte wohl für viele Nutzer deutlich mehr Features als Ballast mitbringen, als eigentlich benötigt werden.
Deshalb war das Ziel hier:
* kein [Feature-Creep](https://datei.wiki/definition/die-grundlagen-des-feature-creep/)
* kein NodeJS und damit keine Dependency-Hölle
* Platformunabhängig dank Java
* erweiterbar
* verschlüsselte Datenbank
## bauen ## bauen
Dies ist ein Gradle-Project. Um es zu compilieren brauchen Sie ein aktuelles Java-Development-Kit. Dies ist ein Gradle-Project. Um es zu compilieren brauchen Sie ein aktuelles Java-Development-Kit.
Das Projekt kann durch Aufruf von `./gradlew build` in einem Terminal innerhalb des Wurzelverzeichnisses gebaut werden. Das Projekt kann durch Aufruf von `./gradlew build` in einem Terminal innerhalb des Wurzelverzeichnisses gebaut werden.
## Datenbank-Unterstützung ## Backends
Im Main-Branch ist kein Datenbank-Backend enthalten.
Alle Einstellungen werden in einer JSON-Datei gespeichert, wobei eine Verschlüsselung einfach konfiguriert werden kann.
Dieses Setup sollte für kleine und mittlere Instanzen reichen.
### Datenbank-Unterstützung
Um das Projekt klein zu halten ist im _main_-Branch kein Datenbank-Support eingebaut. Um das Projekt klein zu halten ist im _main_-Branch kein Datenbank-Support eingebaut.
Es gibt aber einen separaten Branch, der die Benutzung von SQLite-Datenbanken untersützt: [sqlite] Es gibt aber einen separaten Branch, der die Benutzung von SQLite-Datenbanken untersützt: [sqlite]
Die Anbindung an andere Datenbanksysteme ist möglich, im Moment aber noch nicht implementiert.
### andere Backends
Aufgrund der Architektur des OIDC-Providers sollte es jederzeit möglich sein weitere Backends, wie z.B. LDAP anzubinden.
Dies ist aber im Moment noch nicht implementiert und wird wohl erst auf Nachfrage implementiert werden.
</td><td> </td><td>
This aims to be a [specification] compliant OpenID connect provider with minimal footprint. This aims to be a [specification] compliant OpenID connect provider with minimal footprint.
@@ -47,19 +76,47 @@ While the [main] branch does not contain any dependencies for database support,
Unfortunately the SQLite library also draws in some additional dependencies, Unfortunately the SQLite library also draws in some additional dependencies,
which to a certain extend increases the size of the compiled JAR archive. which to a certain extend increases the size of the compiled JAR archive.
## Motivation
Of course, there are plenty other implementations of the OIDC protocol.
The most well-known open source OIDC provider might be [Keycloak].
However, that piece of software called Keycloak is really heavy duty, hard to configure and bloated with features the most users won`t need.
Thus, the goal for LightOIDC was:
* don`t be a feature creep
* don`t use NodeJS avoid the dependency hell
* be platform neutral by using Java
* be extensible
* allow data to be encrypted
## build ## build
This is a gradle project. To compile it, you should have a recent version of a Java Development Kit installed. This is a gradle project. To compile it, you should have a recent version of a Java Development Kit installed.
Build the roject by launching `./gradlew build` in a terminal while being in the root folder of the project. Build the roject by launching `./gradlew build` in a terminal while being in the root folder of the project.
## database support ## backends
In the main branch, there ist no database backend.
Alle preferences and data are stored in a JSON file, allowing for easy encryption of data.
This setup should be fine for small and medium instances.
### database support
In order to achieve a minimal footprint, no database support is incorporated in the main branch. In order to achieve a minimal footprint, no database support is incorporated in the main branch.
However, there is SQLite support in a separate branch: [sqlite] However, there is SQLite support in a separate branch: [sqlite]
Utilizing other databases should be possible, but has not been implemented, yet.
### other backends
LightOIDCs architecture shout allow integration other backends, like LDAP, without a hazzle.
However, this is not implemented yet work will have to be done on demand!
</td> </td>
</tr> </tr>
</table> </table>
[main]: ../main [main]: ../main
[Keycloak]: https://www.keycloak.org/
[specification]: https://openid.net/specs/openid-connect-core-1_0.html [specification]: https://openid.net/specs/openid-connect-core-1_0.html

View File

@@ -1,5 +1,9 @@
description = "SRSoftware OIDC: app" description = "SRSoftware OIDC: app"
plugins {
java
}
dependencies{ dependencies{
implementation("org.json:json:20240303") implementation("org.json:json:20240303")
implementation("org.xerial:sqlite-jdbc:3.46.0.0") implementation("org.xerial:sqlite-jdbc:3.46.0.0")
@@ -14,3 +18,13 @@ dependencies{
implementation(project(":de.srsoftware.oidc.datastore.sqlite")) implementation(project(":de.srsoftware.oidc.datastore.sqlite"))
implementation(project(":de.srsoftware.oidc.web")) implementation(project(":de.srsoftware.oidc.web"))
} }
tasks.jar {
manifest.attributes["Main-Class"] = "de.srsoftware.oidc.app.Application"
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
val dependencies = configurations
.runtimeClasspath
.get()
.map(::zipTree) // OR .map { zipTree(it) }
from(dependencies)
}

View File

@@ -169,7 +169,7 @@ public class ClientController extends Controller {
.stream() .stream()
.map(clients::getClient) .map(clients::getClient)
.flatMap(Optional::stream) .flatMap(Optional::stream)
.sorted(Comparator.comparing(Client::name)) .sorted(Comparator.comparing(Client::name, String.CASE_INSENSITIVE_ORDER))
.map(Client::safeMap) .map(Client::safeMap)
.toList(); .toList();
return sendContent(ex, Map.of(AUTHORZED, authorizedClients, NAME, user.realName())); return sendContent(ex, Map.of(AUTHORZED, authorizedClients, NAME, user.realName()));

View File

@@ -10,6 +10,7 @@
</head> </head>
<body> <body>
<nav></nav> <nav></nav>
<h2>Prüfe Berechtigung…</h2>
<div id="content" style="display: none"> <div id="content" style="display: none">
<p>Eine vertrauende Seite, <span id="rp">unbekannt</span>, hat Zugriff auf die folgenden Informationen erfragt:</p> <p>Eine vertrauende Seite, <span id="rp">unbekannt</span>, hat Zugriff auf die folgenden Informationen erfragt:</p>
<ul id="scopes"> <ul id="scopes">

View File

@@ -42,6 +42,10 @@
<th>Error</th> <th>Error</th>
<td class="warning">Zugriffs-Token gefunden, ist aber ungültig!</td> <td class="warning">Zugriffs-Token gefunden, ist aber ungültig!</td>
</tr> </tr>
<tr id="other_error" style="display: none">
<th>Error</th>
<td class="warning" id="other_error_text">Unbekannter Fehler!</td>
</tr>
<tr> <tr>
<td></td> <td></td>
<td><button id="passBtn" type="button" onClick="updatePass()">Aktualisieren</button></td> <td><button id="passBtn" type="button" onClick="updatePass()">Aktualisieren</button></td>

View File

@@ -10,6 +10,7 @@
</head> </head>
<body> <body>
<nav></nav> <nav></nav>
<h2>Checking permission…</h2>
<div id="content" style="display: none"> <div id="content" style="display: none">
A relying party, <span id="rp">unknown</span>, requested access to the following information: A relying party, <span id="rp">unknown</span>, requested access to the following information:
<ul id="scopes"> <ul id="scopes">

View File

@@ -42,6 +42,11 @@
<th>Error</th> <th>Error</th>
<td class="warning">I received an access token, but it is invalid!</td> <td class="warning">I received an access token, but it is invalid!</td>
</tr> </tr>
<tr id="other_error" style="display: none">
<th>Error</th>
<td class="warning" id="other_error_text">Unknown error!</td>
</tr>
<tr> <tr>
<td></td> <td></td>
<td><button id="passBtn" type="button" onClick="updatePass()">Update</button></td> <td><button id="passBtn" type="button" onClick="updatePass()">Update</button></td>

View File

@@ -11,12 +11,18 @@ function handleDash(response){
var clients = data.authorized; var clients = data.authorized;
var content = document.getElementById('content'); var content = document.getElementById('content');
var any = false; var any = false;
var lastLetter = null;
for (let id in clients){ for (let id in clients){
var client = clients[id]; var client = clients[id];
if (client.landing_page){ if (client.landing_page){
var div = document.createElement("div"); var initialLetter = client.name.charAt(0).toUpperCase();
div.innerHTML = `<button onclick="window.open('${client.landing_page}','_blank').focus();">${client.name}</button>`; if (initialLetter != lastLetter) {
content.append(div); if (lastLetter) content.append(document.createElement("br"));
lastLetter = initialLetter;
}
var span = document.createElement("span");
span.innerHTML = `<button onclick="window.location.href='${client.landing_page}';">${client.name}</button>`;
content.append(span);
any = true; any = true;
} }
} }

View File

@@ -10,10 +10,14 @@ function handlePasswordResponse(response){
} else { } else {
setText('passBtn', 'Update failed!'); setText('passBtn', 'Update failed!');
response.text().then(text => { response.text().then(text => {
if (text == 'invalid token') show('invalid_token'); if (text == 'invalid token') show('invalid_token'); else
if (text == 'token missing') show('missing_token'); if (text == 'token missing') show('missing_token'); else
if (text == 'password mismatch') show('password_mismatch'); if (text == 'password mismatch') show('password_mismatch'); else
if (text == 'weak password') show('weak_password'); if (text == 'weak password') show('weak_password'); else {
setText('other_error_text',text);
show('other_error');
}
}); });
} }
enable('passBtn'); enable('passBtn');