Browse Source

added dashboard

Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
sqlite
Stephan Richter 2 months ago
parent
commit
30f2e115ea
  1. 4
      de.srsoftware.http/src/main/java/de/srsoftware/http/PathHandler.java
  2. 5
      de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/AuthorizationService.java
  3. 15
      de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/data/Client.java
  4. 1
      de.srsoftware.oidc.api/src/test/java/de/srsoftware/oidc/api/AuthServiceTest.java
  5. 38
      de.srsoftware.oidc.backend/src/main/java/de/srsoftware/oidc/backend/ClientController.java
  6. 9
      de.srsoftware.oidc.datastore.file/src/main/java/de/srsoftware/oidc/datastore/file/FileStore.java
  7. 18
      de.srsoftware.oidc.datastore.sqlite/src/main/java/de/srsoftware/oidc/datastore/sqlite/SqliteAuthService.java
  8. 4
      de.srsoftware.oidc.web/src/main/resources/en/index.html
  9. 2
      de.srsoftware.oidc.web/src/main/resources/en/scripts/clients.js
  10. 27
      de.srsoftware.oidc.web/src/main/resources/en/scripts/index.js

4
de.srsoftware.http/src/main/java/de/srsoftware/http/PathHandler.java

@ -15,6 +15,7 @@ import java.io.IOException; @@ -15,6 +15,7 @@ import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.json.JSONArray;
import org.json.JSONObject;
public abstract class PathHandler implements HttpHandler {
@ -173,7 +174,8 @@ public abstract class PathHandler implements HttpHandler { @@ -173,7 +174,8 @@ public abstract class PathHandler implements HttpHandler {
}
public static boolean sendContent(HttpExchange ex, int status, Object o) throws IOException {
if (o instanceof Map map) o = new JSONObject(map);
if (o instanceof List<?> list) o = new JSONArray(list);
if (o instanceof Map<?, ?> map) o = new JSONObject(map);
if (o instanceof JSONObject) ex.getResponseHeaders().add(CONTENT_TYPE, JSON);
return sendContent(ex, status, o.toString().getBytes(UTF_8));
}

5
de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/AuthorizationService.java

@ -5,14 +5,15 @@ import de.srsoftware.oidc.api.data.AuthResult; @@ -5,14 +5,15 @@ import de.srsoftware.oidc.api.data.AuthResult;
import de.srsoftware.oidc.api.data.Authorization;
import java.time.Instant;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
public interface AuthorizationService {
AuthorizationService authorize(String userId, String clientId, Collection<String> scopes, Instant expiration);
Optional<Authorization> consumeAuthorization(String authCode);
AuthResult getAuthorization(String userId, String clientId, Collection<String> scopes);
Optional<String> consumeNonce(String uuid, String id);
List<String> authorizedClients(String userId);
Optional<String> consumeNonce(String uuid, String id);
void nonce(String uuid, String id, String string);
}

15
de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/data/Client.java

@ -37,12 +37,8 @@ public final class Client { @@ -37,12 +37,8 @@ public final class Client {
}
public Map<String, Object> map() {
var map = new HashMap<String, Object>();
map.put(CLIENT_ID, id);
map.put(NAME, name);
var map = safeMap();
map.put(SECRET, secret);
nullable(redirectUris).ifPresent(uris -> map.put(REDIRECT_URIS, uris));
nullable(landingPage).ifPresent(lp -> map.put(LANDING_PAGE, lp));
return map;
}
@ -51,6 +47,15 @@ public final class Client { @@ -51,6 +47,15 @@ public final class Client {
return name;
}
public Map<String, Object> safeMap() {
var map = new HashMap<String, Object>();
map.put(CLIENT_ID, id);
map.put(NAME, name);
nullable(redirectUris).ifPresent(uris -> map.put(REDIRECT_URIS, uris));
nullable(landingPage).ifPresent(lp -> map.put(LANDING_PAGE, lp));
return map;
}
public String secret() {
return secret;
}

1
de.srsoftware.oidc.api/src/test/java/de/srsoftware/oidc/api/AuthServiceTest.java

@ -74,4 +74,5 @@ public abstract class AuthServiceTest { @@ -74,4 +74,5 @@ public abstract class AuthServiceTest {
}
// TODO: test nonce methods
// TODO: test authorizedClients method
}

38
de.srsoftware.oidc.backend/src/main/java/de/srsoftware/oidc/backend/ClientController.java

@ -11,6 +11,7 @@ import de.srsoftware.oidc.api.*; @@ -11,6 +11,7 @@ import de.srsoftware.oidc.api.*;
import de.srsoftware.oidc.api.data.AuthorizedScopes;
import de.srsoftware.oidc.api.data.Client;
import de.srsoftware.oidc.api.data.Session;
import de.srsoftware.oidc.api.data.User;
import de.srsoftware.utils.Optionals;
import java.io.IOException;
import java.time.Instant;
@ -130,6 +131,39 @@ public class ClientController extends Controller { @@ -130,6 +131,39 @@ public class ClientController extends Controller {
return notFound(ex);
}
@Override
public boolean doGet(String path, HttpExchange ex) throws IOException {
var optSession = getSession(ex);
if (optSession.isEmpty()) return sendContent(ex, HTTP_UNAUTHORIZED, "No authorized!");
// post-login paths
var session = optSession.get();
var optUser = users.load(session.userId());
if (optUser.isEmpty()) return invalidSessionUser(ex);
var user = optUser.get();
sessions.extend(session, user);
switch (path) {
case "/dash":
return dashboard(ex, user);
case "/list":
return list(ex, session);
}
return notFound(ex);
}
private boolean dashboard(HttpExchange ex, User user) throws IOException {
var authorizedClients = authorizations //
.authorizedClients(user.uuid())
.stream()
.map(clients::getClient)
.flatMap(Optional::stream)
.sorted(Comparator.comparing(Client::name))
.map(Client::safeMap)
.toList();
return sendContent(ex, Map.of(AUTHORZED, authorizedClients, NAME, user.realName()));
}
@Override
public boolean doPost(String path, HttpExchange ex) throws IOException {
@ -150,8 +184,6 @@ public class ClientController extends Controller { @@ -150,8 +184,6 @@ public class ClientController extends Controller {
return save(ex, session);
case "/authorize":
return authorize(ex, session);
case "/list":
return list(ex, session);
}
return notFound(ex);
}
@ -189,7 +221,7 @@ public class ClientController extends Controller { @@ -189,7 +221,7 @@ public class ClientController extends Controller {
if (o instanceof String s) redirects.add(s);
}
var landingPage = json.has(LANDING_PAGE) ? json.getString(LANDING_PAGE) : null;
var client = new Client(json.getString(CLIENT_ID), json.getString(NAME), json.getString(SECRET), redirects).landingPage(landingPage);
var client = new Client(json.getString(CLIENT_ID), json.getString(NAME), json.getString(SECRET), redirects).landingPage(landingPage);
clients.save(client);
return sendContent(ex, client);
}

9
de.srsoftware.oidc.datastore.file/src/main/java/de/srsoftware/oidc/datastore/file/FileStore.java

@ -334,6 +334,15 @@ public class FileStore implements AuthorizationService, ClientService, SessionSe @@ -334,6 +334,15 @@ public class FileStore implements AuthorizationService, ClientService, SessionSe
return this;
}
@Override
public List<String> authorizedClients(String userId) {
if (!json.has(AUTHORIZATIONS)) return List.of();
var authorizations = json.getJSONObject(AUTHORIZATIONS);
if (!authorizations.has(userId)) return List.of();
var clients = authorizations.getJSONObject(userId);
return new ArrayList<>(clients.keySet());
}
@Override
public Optional<Authorization> consumeAuthorization(String authCode) {

18
de.srsoftware.oidc.datastore.sqlite/src/main/java/de/srsoftware/oidc/datastore/sqlite/SqliteAuthService.java

@ -23,7 +23,8 @@ public class SqliteAuthService extends SqliteStore implements AuthorizationServi @@ -23,7 +23,8 @@ public class SqliteAuthService extends SqliteStore implements AuthorizationServi
private static final String CREATE_AUTHSTORE_TABLE = "CREATE TABLE IF NOT EXISTS authorizations(userId VARCHAR(255), clientId VARCHAR(255), scope VARCHAR(255), expiration LONG, PRIMARY KEY(userId, clientId, scope));";
private static final String SAVE_AUTHORIZATION = "INSERT INTO authorizations(userId, clientId, scope, expiration) VALUES (?,?,?,?) ON CONFLICT DO UPDATE SET expiration = ?";
private static final String SELECT_AUTH = "SELECT * FROM authorizations WHERE userid = ? AND clientId = ? AND scope IN";
private static final String SELECT_AUTH = "SELECT * FROM authorizations WHERE userId = ? AND clientId = ? AND scope IN";
private static final String SELECT_USER_CLIENTS = "SELECT DISTINCT clientId FROM authorizations WHERE userId = ?";
private Map<String, Authorization> authCodes = new HashMap<>();
private Map<String, String> nonceMap = new HashMap<>();
@ -97,6 +98,21 @@ public class SqliteAuthService extends SqliteStore implements AuthorizationServi @@ -97,6 +98,21 @@ public class SqliteAuthService extends SqliteStore implements AuthorizationServi
}
}
@Override
public List<String> authorizedClients(String userId) {
try {
var stmt = conn.prepareStatement(SELECT_USER_CLIENTS);
stmt.setString(1, userId);
var rs = stmt.executeQuery();
var result = new ArrayList<String>();
while (rs.next()) result.add(rs.getString(1));
rs.close();
return result;
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
@Override
public Optional<Authorization> consumeAuthorization(String authCode) {
return nullable(authCodes.remove(authCode));

4
de.srsoftware.oidc.web/src/main/resources/en/index.html

@ -4,13 +4,15 @@ @@ -4,13 +4,15 @@
<title>Light OIDC</title>
<script src="scripts/common.js"></script>
<script src="scripts/user.js"></script>
<script src="scripts/index.js"></script>
<link rel="stylesheet" href="style.css" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<nav></nav>
<div id="content">
<h1>Welcome!</h1>
<h1 id="welcome">Welcome, {}!</h1>
<h3 id="client_hint" style="display: none">These are your authorized clients:</h3>
</div>
</body>
</html>

2
de.srsoftware.oidc.web/src/main/resources/en/scripts/clients.js

@ -44,4 +44,4 @@ function remove(clientId){ @@ -44,4 +44,4 @@ function remove(clientId){
}
}
fetch(client_controller+"/list",{method:'POST',credentials:'include'}).then(handleClients);
fetch(client_controller+"/list").then(handleClients);

27
de.srsoftware.oidc.web/src/main/resources/en/scripts/index.js

@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
function handleDash(response){
if (response.status == UNAUTHORIZED) {
redirect('login.html?return_to='+encodeURI(window.location.href))
return;
}
var clients = response.json().then(data => {
var name = data.name;
var welcome = get('welcome');
welcome.innerHTML = welcome.innerHTML.replace('{}',name);
var clients = data.authorized;
var content = document.getElementById('content');
var any = false;
for (let id in clients){
var client = clients[id];
if (client.landing_page){
var div = document.createElement("div");
div.innerHTML = `<button onclick="window.location.href='${client.landing_page}';">${client.name}</button>`;
content.append(div);
any = true;
}
}
if (any) show('client_hint');
});
}
fetch(client_controller+"/dash").then(handleDash)
Loading…
Cancel
Save