Commit 48543ab4 authored by jan.koester's avatar jan.koester
Browse files

test

parent 2d6bc9b9
Loading
Loading
Loading
Loading
Loading
+1 −0
Changes for CMakeLists.txt: 1 added line, 0 removed lines.
Original line number Diff line number Diff line
@@ -64,6 +64,7 @@ add_subdirectory(src)
add_subdirectory(plugin/krb5)
add_subdirectory(plugin/ad)
add_subdirectory(plugin/dns)
add_subdirectory(plugin/oidc)
add_subdirectory(plugin/smb)
add_subdirectory(client)
add_subdirectory(cli)
+2 −0
Changes for data/admin.html: 2 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -71,6 +71,7 @@
                <li id="nav-gpos"><a href="#" onclick="loadGpos(); return false;">Policies</a></li>
                <li id="nav-wingpo" style="display:none"><a href="#" onclick="loadWinGpos(); return false;">Windows GPOs</a></li>
                <li id="nav-winscripts" style="display:none"><a href="#" onclick="loadWinScripts(); return false;">Windows-Skripte</a></li>
                <li id="nav-oauth" style="display:none"><a href="#" onclick="loadOAuthClients(); return false;">OAuth-Clients</a></li>
                <li id="nav-dns" style="display:none"><a href="#" onclick="loadDns(); return false;">DNS</a></li>
                <li id="nav-sessions"><a href="#" onclick="loadSessions(); return false;">Sessions</a></li>
                <li id="nav-server" style="display:none"><a href="#" onclick="loadServerConfig(); return false;">Server</a></li>
@@ -1528,6 +1529,7 @@ window.onload = function() {
            document.getElementById('nav-dns').style.display = '';
            document.getElementById('nav-wingpo').style.display = '';
            document.getElementById('nav-winscripts').style.display = '';
            document.getElementById('nav-oauth').style.display = '';
        }
        if (data.superadmin || gpo[GPO_EXPORT_IMPORT])
            document.getElementById('nav-export').style.display = '';
+93 −0
Changes for data/dirviews.js: 93 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -480,3 +480,96 @@ function saveWinScriptAssign(i) {
    if (add.length) dirPost("winscriptassign", "", {script: sc.name, users: JSON.stringify(add)}, doRemove);
    else doRemove();
}

/* ================= OAuth / OpenID Connect clients ================= */
var g_oauthClients = [], g_oauthIssuer = '';
var OAUTH_GRANTS = [['authorization_code', 'Authorization Code + PKCE (Web-Apps, SPAs)'],
                    ['refresh_token', 'Refresh-Tokens'],
                    ['client_credentials', 'Client Credentials (Dienst zu Dienst)'],
                    ['device_code', 'Device Code (CLI, Geräte ohne Browser)']];

function loadOAuthClients() {
    currentView = loadOAuthClients;
    setActiveNav('nav-oauth');
    loadDomainSelector(function() {
        if (needRealDomain('OAuth-Clients')) return;
        ajaxGet(dirApi("oauthlist"), function(err, data) {
            if (err) { showError(dirErr(err)); return; }
            g_oauthClients = data.clients || [];
            g_oauthIssuer = data.issuer || '';
            var h = '<h3>OAuth / OpenID Connect</h3>';
            h += '<p class="hint">Anwendungen melden Benutzer per OpenID Connect gegen das Verzeichnis an. Issuer: <code>' + escapeHtml(g_oauthIssuer) + '</code><br>Discovery: <code>' + escapeHtml(g_oauthIssuer) + '/.well-known/openid-configuration</code></p>';
            h += '<table class="admin-table userlist"><tr><th>Name</th><th>Client-ID</th><th>Typ</th><th>Grants</th><th>Gruppen</th><th>Actions</th></tr>';
            for (var i = 0; i < g_oauthClients.length; i++) {
                var c = g_oauthClients[i];
                h += '<tr><td>' + escapeHtml(c.name) + '</td><td><code>' + escapeHtml(c.id) + '</code></td>';
                h += '<td>' + (c.public ? 'öffentlich' : 'vertraulich') + '</td>';
                h += '<td>' + escapeHtml(c.grants.join(', ')) + '</td>';
                h += '<td>' + (c.groups.length ? escapeHtml(c.groups.join(', ')) : '<em>alle</em>') + '</td><td><ul>';
                h += '<li><a class="button" href="#" onclick="editOAuthClient(' + i + '); return false;">Bearbeiten</a></li>';
                if (!c.public) h += '<li><a class="button" href="#" onclick="newOAuthSecret(' + i + '); return false;">Neues Secret</a></li>';
                h += '<li><a class="button danger" href="#" onclick="deleteOAuthClient(' + i + '); return false;">Delete</a></li>';
                h += '</ul></td></tr>';
            }
            if (!g_oauthClients.length) h += '<tr><td colspan="6"><em>keine Clients</em></td></tr>';
            h += '</table><p><a class="button" href="#" onclick="editOAuthClient(-1); return false;">Neuer Client</a></p>';
            setContent(h);
        });
    }, true);
}

function editOAuthClient(i) {
    var c = i >= 0 ? g_oauthClients[i] : {name: '', public: false, grants: ['authorization_code', 'refresh_token'],
                                           redirect_uris: [], post_logout_redirect_uris: [], groups: []};
    var h = '<h3>' + (i >= 0 ? escapeHtml(c.name) : 'Neuer OAuth-Client') + '</h3>';
    h += '<p><a href="#" onclick="loadOAuthClients(); return false;">&larr; alle Clients</a></p>';
    h += '<form id="oauthform" class="admin-form" onsubmit="saveOAuthClient(' + i + '); return false;"><table>';
    h += '<tr><td><label>Name</label></td><td><input name="name" size="40" value="' + escapeHtml(c.name) + '" required></td></tr>';
    h += '<tr><td><label>Typ</label></td><td><label><input type="radio" name="public" value="false"' + (!c.public ? ' checked' : '') + '> vertraulich (Server-Anwendung, mit Secret)</label><br>';
    h += '<label><input type="radio" name="public" value="true"' + (c.public ? ' checked' : '') + '> öffentlich (SPA, App, CLI &ndash; ohne Secret, PKCE Pflicht)</label></td></tr>';
    h += '<tr><td><label>Grants</label></td><td>';
    OAUTH_GRANTS.forEach(function(g) {
        h += '<label><input type="checkbox" name="grant" value="' + g[0] + '"' + (c.grants.indexOf(g[0]) >= 0 ? ' checked' : '') + '> ' + escapeHtml(g[1]) + '</label><br>';
    });
    h += '</td></tr>';
    h += '<tr><td><label>Redirect-URIs</label></td><td><textarea name="redirect_uris" rows="3" cols="50" placeholder="https://app.example.com/oauth/callback">' + escapeHtml(c.redirect_uris.join('\n')) + '</textarea><br><span class="hint">eine pro Zeile, exakt so wie die Anwendung sie sendet</span></td></tr>';
    h += '<tr><td><label>Logout-Rücksprung</label></td><td><textarea name="post_logout_redirect_uris" rows="2" cols="50">' + escapeHtml(c.post_logout_redirect_uris.join('\n')) + '</textarea></td></tr>';
    h += '<tr><td><label>Erlaubte Gruppen</label></td><td><input name="groups" size="40" value="' + escapeHtml(c.groups.join(', ')) + '"><br><span class="hint">kommagetrennt; leer = alle Benutzer der Domäne</span></td></tr>';
    h += '<tr><td></td><td><input type="submit" class="button" value="Speichern"></td></tr></table></form>';
    setContent(h);
}

function saveOAuthClient(i) {
    var f = document.getElementById('oauthform');
    var grants = [];
    f.querySelectorAll('input[name=grant]').forEach(function(b) { if (b.checked) grants.push(b.value); });
    var fields = {name: f.name.value, public: f.querySelector('input[name=public]:checked').value,
                  grants: grants.join('\n'), redirect_uris: f.redirect_uris.value,
                  post_logout_redirect_uris: f.post_logout_redirect_uris.value, groups: f.groups.value};
    var action = i >= 0 ? "oauthupdate" : "oauthcreate";
    dirPost(action, i >= 0 ? g_oauthClients[i].id : "", fields, function(data) {
        if (data.secret) showOAuthSecret(data.id || g_oauthClients[i].id, data.secret);
        else loadOAuthClients();
    });
}

function showOAuthSecret(id, secret) {
    var h = '<h3>Client angelegt / Secret erzeugt</h3>';
    h += '<p>Client-ID: <code>' + escapeHtml(id) + '</code></p>';
    h += '<p>Client-Secret: <code style="user-select:all">' + escapeHtml(secret) + '</code></p>';
    h += '<p class="hint">Das Secret wird nur jetzt angezeigt &ndash; gespeichert ist nur ein Hash. Bei Verlust ein neues erzeugen.</p>';
    h += '<p><a class="button" href="#" onclick="loadOAuthClients(); return false;">Fertig</a></p>';
    setContent(h);
}

function newOAuthSecret(i) {
    var c = g_oauthClients[i];
    if (!confirm('Neues Secret für "' + c.name + '"? Das alte wird sofort ungültig.')) return;
    dirPost("oauthsecret", c.id, {}, function(data) { showOAuthSecret(c.id, data.secret); });
}

function deleteOAuthClient(i) {
    var c = g_oauthClients[i];
    if (!confirm('Client "' + c.name + '" löschen? Ausgestellte Refresh-Tokens werden damit unbrauchbar.')) return;
    dirPost("oauthdelete", c.id, {}, function() { loadOAuthClients(); });
}
+1 −0
Changes for docs/ROADMAP.md: 1 added line, 0 removed lines.
Original line number Diff line number Diff line
@@ -67,6 +67,7 @@ The directory layer maps them onto AD objects and adds generic objects:
| 7 | RPC services: lsarpc, samr, netlogon, drsuapi (bind/crack names), srvsvc/wkssvc | netlogon: secure channel, GetCapabilities, GetDomainInfo, ServerPasswordSet2, DsrGetDcName*, NTLM pass-through (LogonSamLogon/Ex/WithFlags, interactive + network, validation 2/3/6) for Windows clients and member servers (proven with adconnect incl. EPM lookup); drsuapi DsBind/DsCrackNames; samr ChangePasswordUser2/3; srvsvc (share enum/info, server info, time) and wkssvc (workstation info), tested with rpcclient/smbclient/Samba python |
| 8 | SMB2/3 server: IPC$ pipes, NETLOGON/SYSVOL shares, signing | |
| 9 | Packaging for FenrirOS (spec, syslite unit, container), admin UI for AD objects/DNS/LDIF | |
| 10 | OAuth 2.0 / OpenID Connect provider (plugin oidc) | Issuer per domain <base>/<domain>; discovery, JWKS (RS256), authorize (code + PKCE S256, login page, SSO cookie, prompt=none/login), token (authorization_code, refresh_token with rotation, client_credentials, device_code), device verification page, userinfo, introspection, revocation, end-session; clients with group restriction managed in the admin UI; state in OAuthObject records (replicated, not visible over LDAP/SMB); tested end to end with Authlib |

## Test bench

+6 −0
Changes for plugin/oidc/CMakeLists.txt: 6 added lines, 0 removed lines.
Original line number Diff line number Diff line
add_library(fenrirds-oidc SHARED oidc.cpp)
target_include_directories(fenrirds-oidc PUBLIC ${JSONC_INCLUDE_DIR})
target_link_libraries(fenrirds-oidc PUBLIC fenrirds-core uuidp::uuidp json-c::json-c netplus::netplus httppp::httppp confplus::confplus)
set_target_properties(fenrirds-oidc PROPERTIES OUTPUT_NAME "fenrirds-oidc" VERSION ${version} SOVERSION 0)
install(TARGETS fenrirds-oidc DESTINATION ${CMAKE_INSTALL_LIBDIR}/fenrirds)
install(FILES oidc.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/fenrirds)
Loading