diff --git a/de.srsoftware.logging/build.gradle b/de.srsoftware.logging/build.gradle new file mode 100644 index 0000000..a55b584 --- /dev/null +++ b/de.srsoftware.logging/build.gradle @@ -0,0 +1,19 @@ +plugins { + id 'java' +} + +group = 'de.srsoftware' +version = '1.0-SNAPSHOT' + +repositories { + mavenCentral() +} + +dependencies { + testImplementation platform('org.junit:junit-bom:5.10.0') + testImplementation 'org.junit.jupiter:junit-jupiter' +} + +test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/de.srsoftware.logging/src/main/java/de/srsoftware/logging/ColorLogger.java b/de.srsoftware.logging/src/main/java/de/srsoftware/logging/ColorLogger.java new file mode 100644 index 0000000..16357d2 --- /dev/null +++ b/de.srsoftware.logging/src/main/java/de/srsoftware/logging/ColorLogger.java @@ -0,0 +1,57 @@ +/* © SRSoftware 2024 */ +package de.srsoftware.logging; + +import java.text.MessageFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Map; +import java.util.ResourceBundle; + +import static de.srsoftware.logging.ConsoleColors.*; +import static java.lang.System.Logger.Level.*; + +public class ColorLogger implements System.Logger { + private final String name; + private static int rootLevel = INFO.getSeverity(); + + public ColorLogger(String name) { + this.name = name; + } + + @Override + public String getName() { + return name; + } + + @Override + public boolean isLoggable(Level level) { + return level.getSeverity() >= rootLevel; + } + + @Override + public void log(Level level, ResourceBundle bundle, String msg, Throwable thrown) { + if (isLoggable(level)) { + System.out.println(colorize(msg,level.getSeverity())); + thrown.printStackTrace(); + } + } + + @Override + public void log(Level level, ResourceBundle bundle, String format, Object... params) { + if (isLoggable(level)) { + System.out.println(colorize(MessageFormat.format(format, params),level.getSeverity())); + } + } + + public ColorLogger setLogLevel(Level level){ + rootLevel = level.getSeverity(); + return this; + } + + private static String colorize(String message,int severity){ + var color = severity >= ERROR.getSeverity() ? RED : severity >= WARNING.getSeverity() ? YELLOW : severity >= INFO.getSeverity() ? WHITE_BRIGHT : WHITE; + var date = new Date(); + var FORMAT = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS"); + return WHITE+FORMAT.format(date)+" "+color+message+RESET; + } +} diff --git a/de.srsoftware.logging/src/main/java/de/srsoftware/logging/ColorLoggerFinder.java b/de.srsoftware.logging/src/main/java/de/srsoftware/logging/ColorLoggerFinder.java new file mode 100644 index 0000000..0c7930d --- /dev/null +++ b/de.srsoftware.logging/src/main/java/de/srsoftware/logging/ColorLoggerFinder.java @@ -0,0 +1,14 @@ +/* © SRSoftware 2024 */ +package de.srsoftware.logging; + +import java.util.HashMap; +import java.util.Map; + +public class ColorLoggerFinder extends System.LoggerFinder { + private static final Map LOGGERS = new HashMap<>(); + + @Override + public System.Logger getLogger(String name, Module module) { + return LOGGERS.computeIfAbsent(name, ColorLogger::new); + } +} diff --git a/de.srsoftware.logging/src/main/java/de/srsoftware/logging/ConsoleColors.java b/de.srsoftware.logging/src/main/java/de/srsoftware/logging/ConsoleColors.java new file mode 100644 index 0000000..f76648f --- /dev/null +++ b/de.srsoftware.logging/src/main/java/de/srsoftware/logging/ConsoleColors.java @@ -0,0 +1,76 @@ +package de.srsoftware.logging; + +public class ConsoleColors { + // Reset + public static final String RESET = "\033[0m"; // Text Reset + + // Regular Colors + public static final String BLACK = "\033[0;30m"; // BLACK + public static final String RED = "\033[0;31m"; // RED + public static final String GREEN = "\033[0;32m"; // GREEN + public static final String YELLOW = "\033[0;33m"; // YELLOW + public static final String BLUE = "\033[0;34m"; // BLUE + public static final String PURPLE = "\033[0;35m"; // PURPLE + public static final String CYAN = "\033[0;36m"; // CYAN + public static final String WHITE = "\033[0;37m"; // WHITE + + // Bold + public static final String BLACK_BOLD = "\033[1;30m"; // BLACK + public static final String RED_BOLD = "\033[1;31m"; // RED + public static final String GREEN_BOLD = "\033[1;32m"; // GREEN + public static final String YELLOW_BOLD = "\033[1;33m"; // YELLOW + public static final String BLUE_BOLD = "\033[1;34m"; // BLUE + public static final String PURPLE_BOLD = "\033[1;35m"; // PURPLE + public static final String CYAN_BOLD = "\033[1;36m"; // CYAN + public static final String WHITE_BOLD = "\033[1;37m"; // WHITE + + // Underline + public static final String BLACK_UNDERLINED = "\033[4;30m"; // BLACK + public static final String RED_UNDERLINED = "\033[4;31m"; // RED + public static final String GREEN_UNDERLINED = "\033[4;32m"; // GREEN + public static final String YELLOW_UNDERLINED = "\033[4;33m"; // YELLOW + public static final String BLUE_UNDERLINED = "\033[4;34m"; // BLUE + public static final String PURPLE_UNDERLINED = "\033[4;35m"; // PURPLE + public static final String CYAN_UNDERLINED = "\033[4;36m"; // CYAN + public static final String WHITE_UNDERLINED = "\033[4;37m"; // WHITE + + // Background + public static final String BLACK_BACKGROUND = "\033[40m"; // BLACK + public static final String RED_BACKGROUND = "\033[41m"; // RED + public static final String GREEN_BACKGROUND = "\033[42m"; // GREEN + public static final String YELLOW_BACKGROUND = "\033[43m"; // YELLOW + public static final String BLUE_BACKGROUND = "\033[44m"; // BLUE + public static final String PURPLE_BACKGROUND = "\033[45m"; // PURPLE + public static final String CYAN_BACKGROUND = "\033[46m"; // CYAN + public static final String WHITE_BACKGROUND = "\033[47m"; // WHITE + + // High Intensity + public static final String BLACK_BRIGHT = "\033[0;90m"; // BLACK + public static final String RED_BRIGHT = "\033[0;91m"; // RED + public static final String GREEN_BRIGHT = "\033[0;92m"; // GREEN + public static final String YELLOW_BRIGHT = "\033[0;93m"; // YELLOW + public static final String BLUE_BRIGHT = "\033[0;94m"; // BLUE + public static final String PURPLE_BRIGHT = "\033[0;95m"; // PURPLE + public static final String CYAN_BRIGHT = "\033[0;96m"; // CYAN + public static final String WHITE_BRIGHT = "\033[0;97m"; // WHITE + + // Bold High Intensity + public static final String BLACK_BOLD_BRIGHT = "\033[1;90m"; // BLACK + public static final String RED_BOLD_BRIGHT = "\033[1;91m"; // RED + public static final String GREEN_BOLD_BRIGHT = "\033[1;92m"; // GREEN + public static final String YELLOW_BOLD_BRIGHT = "\033[1;93m";// YELLOW + public static final String BLUE_BOLD_BRIGHT = "\033[1;94m"; // BLUE + public static final String PURPLE_BOLD_BRIGHT = "\033[1;95m";// PURPLE + public static final String CYAN_BOLD_BRIGHT = "\033[1;96m"; // CYAN + public static final String WHITE_BOLD_BRIGHT = "\033[1;97m"; // WHITE + + // High Intensity backgrounds + public static final String BLACK_BACKGROUND_BRIGHT = "\033[0;100m";// BLACK + public static final String RED_BACKGROUND_BRIGHT = "\033[0;101m";// RED + public static final String GREEN_BACKGROUND_BRIGHT = "\033[0;102m";// GREEN + public static final String YELLOW_BACKGROUND_BRIGHT = "\033[0;103m";// YELLOW + public static final String BLUE_BACKGROUND_BRIGHT = "\033[0;104m";// BLUE + public static final String PURPLE_BACKGROUND_BRIGHT = "\033[0;105m"; // PURPLE + public static final String CYAN_BACKGROUND_BRIGHT = "\033[0;106m"; // CYAN + public static final String WHITE_BACKGROUND_BRIGHT = "\033[0;107m"; // WHITE +} \ No newline at end of file diff --git a/de.srsoftware.logging/src/main/resources/META-INF/services/java.lang.System$LoggerFinder b/de.srsoftware.logging/src/main/resources/META-INF/services/java.lang.System$LoggerFinder new file mode 100644 index 0000000..29b80f4 --- /dev/null +++ b/de.srsoftware.logging/src/main/resources/META-INF/services/java.lang.System$LoggerFinder @@ -0,0 +1 @@ +de.srsoftware.logging.ColorLoggerFinder \ No newline at end of file diff --git a/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/AuthorizationService.java b/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/AuthorizationService.java new file mode 100644 index 0000000..88cdfe4 --- /dev/null +++ b/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/AuthorizationService.java @@ -0,0 +1,13 @@ +/* © SRSoftware 2024 */ +package de.srsoftware.oidc.api; + +import java.util.Date; +import java.util.List; + +public interface AuthorizationService { + AuthorizationService authorize(Client client, User user, Date expiration); + boolean isAuthorized(Client client, User user); + List authorizedUsers(Client client); + List authorizedClients(User user); + AuthorizationService revoke(Client client, User user); +} diff --git a/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/Client.java b/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/Client.java index d2cc369..3081fa7 100644 --- a/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/Client.java +++ b/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/Client.java @@ -2,18 +2,20 @@ package de.srsoftware.oidc.api; import static de.srsoftware.oidc.api.Constants.*; +import static java.lang.System.Logger.Level.WARNING; import java.util.Map; import java.util.Set; import java.util.UUID; public record Client(String id, String name, String secret, Set redirectUris) { + private static System.Logger LOG = System.getLogger(Client.class.getSimpleName()); public Map map() { return Map.of(CLIENT_ID, id, NAME, name, SECRET, secret, REDIRECT_URIS, redirectUris); } public String generateCode() { - System.err.printf("%s.generateCode() not implemented!", getClass().getSimpleName()); + LOG.log(WARNING,"{0}.generateCode() not implemented!", getClass().getSimpleName()); return UUID.randomUUID().toString(); } } diff --git a/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/Constants.java b/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/Constants.java index 63ba891..bafb86d 100644 --- a/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/Constants.java +++ b/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/Constants.java @@ -2,9 +2,10 @@ package de.srsoftware.oidc.api; public class Constants { - public - static final String CODE = "code"; + public static final String CAUSE = "cause"; public static final String CLIENT_ID = "client_id"; + public static final String CODE = "code"; + public static final String CONFIRMED = "confirmed"; public static final String NAME = "name"; public static final String REDIRECT_URI = "redirect_uri"; public static final String REDIRECT_URIS = "redirect_uris"; diff --git a/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/PathHandler.java b/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/PathHandler.java index c3e57a1..207b9bf 100644 --- a/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/PathHandler.java +++ b/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/PathHandler.java @@ -1,6 +1,8 @@ /* © SRSoftware 2024 */ package de.srsoftware.oidc.api; +import static java.lang.System.Logger.Level.DEBUG; +import static java.lang.System.Logger.Level.INFO; import static java.net.HttpURLConnection.*; import static java.nio.charset.StandardCharsets.UTF_8; @@ -16,6 +18,7 @@ import java.util.stream.Stream; import org.json.JSONObject; public abstract class PathHandler implements HttpHandler { + public System.Logger LOG = System.getLogger(getClass().getSimpleName()); public static final String CONTENT_TYPE = "Content-Type"; public static final String DELETE = "DELETE"; public static final String GET = "GET"; @@ -54,7 +57,7 @@ public abstract class PathHandler implements HttpHandler { public void handle(HttpExchange ex) throws IOException { String path = relativePath(ex); String method = ex.getRequestMethod(); - System.out.printf("%s %s\n", method, path); + LOG.log(INFO, "{0} {1}", method, path); boolean ignored = switch (method) { case DELETE -> doDelete(path,ex); case GET -> doGet(path,ex); @@ -109,32 +112,38 @@ public abstract class PathHandler implements HttpHandler { return false; } + public static boolean sendRedirect(HttpExchange ex, String url) throws IOException { + ex.getResponseHeaders().add("Location", url); + return sendEmptyResponse(HTTP_MOVED_TEMP, ex); + } + public static boolean sendContent(HttpExchange ex, int status, byte[] bytes) throws IOException { ex.sendResponseHeaders(status, bytes.length); ex.getResponseBody().write(bytes); return true; } + public static boolean sendContent(HttpExchange ex, int status, Object o) throws IOException { + 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)); + } + + public static boolean sendContent(HttpExchange ex, byte[] bytes) throws IOException { return sendContent(ex, HTTP_OK, bytes); } public static boolean sendContent(HttpExchange ex, Object o) throws IOException { - if (o instanceof Map map) o = new JSONObject(map); - if (o instanceof JSONObject) ex.getResponseHeaders().add(CONTENT_TYPE, JSON); - return sendContent(ex, HTTP_OK, o.toString().getBytes(UTF_8)); + return sendContent(ex, HTTP_OK, o); } - public static boolean sendError(HttpExchange ex, byte[] bytes) throws IOException { - return sendContent(ex, HTTP_BAD_REQUEST, bytes); - } - public static boolean sendError(HttpExchange ex, Object o) throws IOException { - return sendContent(ex, HTTP_BAD_REQUEST, o.toString().getBytes(UTF_8)); + public static boolean badRequest(HttpExchange ex, byte[] bytes) throws IOException { + return sendContent(ex, HTTP_BAD_REQUEST, bytes); } - public static boolean sendRedirect(HttpExchange ex, String url) throws IOException { - ex.getResponseHeaders().add("Location", url); - return sendEmptyResponse(HTTP_MOVED_TEMP, ex); + public static boolean badRequest(HttpExchange ex, Object o) throws IOException { + return sendContent(ex, HTTP_BAD_REQUEST, o); } } diff --git a/de.srsoftware.oidc.app/build.gradle b/de.srsoftware.oidc.app/build.gradle index 8418a89..4eeb19c 100644 --- a/de.srsoftware.oidc.app/build.gradle +++ b/de.srsoftware.oidc.app/build.gradle @@ -12,6 +12,7 @@ repositories { dependencies { testImplementation platform('org.junit:junit-bom:5.10.0') testImplementation 'org.junit.jupiter:junit-jupiter' + implementation project(':de.srsoftware.logging') implementation project(':de.srsoftware.oidc.api') implementation project(':de.srsoftware.oidc.backend') implementation project(':de.srsoftware.oidc.web') diff --git a/de.srsoftware.oidc.app/src/main/java/de/srsoftware/oidc/app/Application.java b/de.srsoftware.oidc.app/src/main/java/de/srsoftware/oidc/app/Application.java index cd28cf7..54a7f41 100644 --- a/de.srsoftware.oidc.app/src/main/java/de/srsoftware/oidc/app/Application.java +++ b/de.srsoftware.oidc.app/src/main/java/de/srsoftware/oidc/app/Application.java @@ -3,8 +3,11 @@ package de.srsoftware.oidc.app; import static de.srsoftware.oidc.api.Permission.MANAGE_CLIENTS; +import static java.lang.System.Logger.Level.DEBUG; +import static java.lang.System.Logger.Level.ERROR; import com.sun.net.httpserver.HttpServer; +import de.srsoftware.logging.ColorLogger; import de.srsoftware.oidc.api.User; import de.srsoftware.oidc.backend.Backend; import de.srsoftware.oidc.datastore.file.FileStore; @@ -28,6 +31,7 @@ public class Application { public static final String FIRST_UUID = UUID.randomUUID().toString(); public static final String INDEX = STATIC_PATH + "/index.html"; private static final String BASE_PATH = "basePath"; + private static System.Logger LOG = new ColorLogger("Application").setLogLevel(DEBUG); public static void main(String[] args) throws Exception { var argMap = map(args); @@ -40,7 +44,7 @@ public class Application { HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0); new StaticPages(basePath).bindPath(STATIC_PATH, FAVICON).on(server); new Forward(INDEX).bindPath(ROOT).on(server); - new Backend(fileStore, fileStore, fileStore).bindPath(BACKEND, WELL_KNOWN).on(server); + new Backend(fileStore, fileStore, fileStore, fileStore).bindPath(BACKEND, WELL_KNOWN).on(server); server.setExecutor(Executors.newCachedThreadPool()); server.start(); } @@ -56,7 +60,7 @@ public class Application { map.put(BASE_PATH, Path.of(tokens.remove(0))); break; default: - System.err.printf("Unknown option: %s\n", token); + LOG.log(ERROR,"Unknown option: {0}", token); } } diff --git a/de.srsoftware.oidc.backend/build.gradle b/de.srsoftware.oidc.backend/build.gradle index 4d47a02..adeaecc 100644 --- a/de.srsoftware.oidc.backend/build.gradle +++ b/de.srsoftware.oidc.backend/build.gradle @@ -14,6 +14,7 @@ dependencies { testImplementation 'org.junit.jupiter:junit-jupiter' implementation project(':de.srsoftware.cookies') implementation project(':de.srsoftware.oidc.api') + implementation project(':de.srsoftware.logging') implementation 'org.json:json:20240303' } diff --git a/de.srsoftware.oidc.backend/src/main/java/de/srsoftware/oidc/backend/Backend.java b/de.srsoftware.oidc.backend/src/main/java/de/srsoftware/oidc/backend/Backend.java index cd67e44..baf26c8 100644 --- a/de.srsoftware.oidc.backend/src/main/java/de/srsoftware/oidc/backend/Backend.java +++ b/de.srsoftware.oidc.backend/src/main/java/de/srsoftware/oidc/backend/Backend.java @@ -4,6 +4,8 @@ package de.srsoftware.oidc.backend; import static de.srsoftware.oidc.api.Constants.*; import static de.srsoftware.oidc.api.Permission.MANAGE_CLIENTS; import static de.srsoftware.oidc.api.User.*; +import static java.lang.System.Logger.Level.ERROR; +import static java.lang.System.Logger.Level.WARNING; import static java.net.HttpURLConnection.*; import com.sun.net.httpserver.HttpExchange; @@ -16,18 +18,21 @@ import java.util.Optional; import org.json.JSONObject; public class Backend extends PathHandler { - private final SessionService sessions; - private final UserService users; - private final ClientService clients; + private static final System.Logger LOG = System.getLogger(Backend.class.getSimpleName()); + private final AuthorizationService authorizations; + private final SessionService sessions; + private final UserService users; + private final ClientService clients; - public Backend(ClientService clientService, SessionService sessionService, UserService userService) { - clients = clientService; - sessions = sessionService; - users = userService; + public Backend(AuthorizationService authorizationService, ClientService clientService, SessionService sessionService, UserService userService) { + authorizations = authorizationService; + clients = clientService; + sessions = sessionService; + users = userService; } private boolean addClient(HttpExchange ex, Session session) throws IOException { - if (!session.user().hasPermission(MANAGE_CLIENTS)) return sendError(ex, "NOT ALLOWED"); + if (!session.user().hasPermission(MANAGE_CLIENTS)) return badRequest(ex, "NOT ALLOWED"); var json = json(ex); var redirects = new HashSet(); for (Object o : json.getJSONArray(REDIRECT_URIS)) { @@ -39,16 +44,26 @@ public class Backend extends PathHandler { } private boolean authorize(HttpExchange ex, Session session) throws IOException { + var user = session.user(); var json = json(ex); var clientId = json.getString(CLIENT_ID); + var redirect = json.getString(REDIRECT_URI); var optClient = clients.getClient(clientId); - if (optClient.isEmpty()) return sendEmptyResponse(HTTP_NOT_FOUND, ex); - var client = optClient.get(); - var redirect = json.getString(REDIRECT_URI); - if (!client.redirectUris().contains(redirect)) return sendEmptyResponse(HTTP_BAD_REQUEST, ex); + if (optClient.isEmpty()) return badRequest(ex, Map.of(CAUSE, "unknown client", CLIENT_ID, clientId)); + var client = optClient.get(); + + if (!client.redirectUris().contains(redirect)) return badRequest(ex, Map.of(CAUSE, "unknown redirect uri", REDIRECT_URI, redirect)); + + if (!authorizations.isAuthorized(client, session.user())) { + if (json.has(CONFIRMED) && json.getBoolean(CONFIRMED)) { + authorizations.authorize(client, user, null); + } else { + return sendContent(ex, Map.of(CONFIRMED, false, NAME, client.name())); + } + } var state = json.getString(STATE); var code = client.generateCode(); - return sendContent(ex, Map.of(CODE,code,REDIRECT_URI,redirect,STATE,state)); + return sendContent(ex, Map.of(CONFIRMED, true, CODE, code, REDIRECT_URI, redirect, STATE, state)); } private boolean clients(HttpExchange ex, Session session) throws IOException { @@ -60,7 +75,7 @@ public class Backend extends PathHandler { } private boolean deleteClient(HttpExchange ex, Session session) throws IOException { - if (!session.user().hasPermission(MANAGE_CLIENTS)) return sendError(ex, "NOT ALLOWED"); + if (!session.user().hasPermission(MANAGE_CLIENTS)) return badRequest(ex, "NOT ALLOWED"); var json = json(ex); var id = json.getString(CLIENT_ID); clients.getClient(id).ifPresent(clients::remove); @@ -89,8 +104,7 @@ public class Backend extends PathHandler { case "/client": return deleteClient(ex, session); } - - System.err.println("not implemented"); + LOG.log(ERROR, "not implemented"); return sendEmptyResponse(HTTP_NOT_FOUND, ex); } @@ -112,7 +126,7 @@ public class Backend extends PathHandler { return logout(ex, session); } - System.err.println("not implemented"); + LOG.log(WARNING,"not implemented"); return sendEmptyResponse(HTTP_NOT_FOUND, ex); } @@ -146,7 +160,7 @@ public class Backend extends PathHandler { case "/user": return sendUserAndCookie(ex, session); } - System.err.println("not implemented"); + LOG.log(WARNING,"not implemented"); return sendEmptyResponse(HTTP_NOT_FOUND, ex); } @@ -172,17 +186,14 @@ public class Backend extends PathHandler { } private boolean provideToken(HttpExchange ex) throws IOException { - System.err.printf("%s.provideToken(ex) not implemented!\n",getClass().getSimpleName()); - var json = json(ex); - System.err.println(json); - return sendEmptyResponse(HTTP_NOT_FOUND,ex); + LOG.log(ERROR,"{0}.provideToken(ex) not implemented!\n", getClass().getSimpleName()); + LOG.log(WARNING,json(ex)); + return sendEmptyResponse(HTTP_NOT_FOUND, ex); } private boolean openidConfig(HttpExchange ex) throws IOException { - return sendContent(ex, Map.of( - "token_endpoint",hostname(ex)+"/api/token", - "authorization_endpoint", hostname(ex) + "/web/authorization.html") - ); + var host = hostname(ex); + return sendContent(ex, Map.of("token_endpoint", host + "/api/token", "authorization_endpoint", host + "/web/authorization.html", "userinfo_endpoint", host + "/api/userinfo", "jwks_uri", host + "/api/jwks")); } private boolean sendUserAndCookie(HttpExchange ex, Session session) throws IOException { @@ -198,12 +209,12 @@ public class Backend extends PathHandler { return sendEmptyResponse(HTTP_FORBIDDEN, ex); } var oldPass = json.getString("oldpass"); - if (!users.passwordMatches(oldPass, user.hashedPassword())) return sendError(ex, "wrong password"); + if (!users.passwordMatches(oldPass, user.hashedPassword())) return badRequest(ex, "wrong password"); var newpass = json.getJSONArray("newpass"); var newPass1 = newpass.getString(0); if (!newPass1.equals(newpass.getString(1))) { - return sendError(ex, "password mismatch"); + return badRequest(ex, "password mismatch"); } users.updatePassword(user, newPass1); return sendContent(ex, user.map(false)); diff --git a/de.srsoftware.oidc.datastore.file/src/main/java/de/srsoftware/oidc/datastore/file/FileStore.java b/de.srsoftware.oidc.datastore.file/src/main/java/de/srsoftware/oidc/datastore/file/FileStore.java index 7016639..28bc07d 100644 --- a/de.srsoftware.oidc.datastore.file/src/main/java/de/srsoftware/oidc/datastore/file/FileStore.java +++ b/de.srsoftware.oidc.datastore.file/src/main/java/de/srsoftware/oidc/datastore/file/FileStore.java @@ -14,7 +14,7 @@ import java.time.temporal.ChronoUnit; import java.util.*; import org.json.JSONObject; -public class FileStore implements ClientService, SessionService, UserService { +public class FileStore implements AuthorizationService, ClientService, SessionService, UserService { private static final String CLIENTS = "clients"; private static final String EXPIRATION = "expiration"; private static final String NAME = "name"; @@ -238,4 +238,31 @@ public class FileStore implements ClientService, SessionService, UserService { public ClientService update(Client client) { return null; } + + /*** Authorization service methods ***/ + + @Override + public AuthorizationService authorize(Client client, User user, Date expiration) { + return null; + } + + @Override + public boolean isAuthorized(Client client, User user) { + return false; + } + + @Override + public List authorizedUsers(Client client) { + return List.of(); + } + + @Override + public List authorizedClients(User user) { + return List.of(); + } + + @Override + public AuthorizationService revoke(Client client, User user) { + return null; + } } diff --git a/de.srsoftware.oidc.web/src/main/java/de/srsoftware/oidc/web/Forward.java b/de.srsoftware.oidc.web/src/main/java/de/srsoftware/oidc/web/Forward.java index b2ec48c..03cf63f 100644 --- a/de.srsoftware.oidc.web/src/main/java/de/srsoftware/oidc/web/Forward.java +++ b/de.srsoftware.oidc.web/src/main/java/de/srsoftware/oidc/web/Forward.java @@ -5,6 +5,8 @@ import com.sun.net.httpserver.HttpExchange; import de.srsoftware.oidc.api.PathHandler; import java.io.IOException; +import static java.lang.System.Logger.Level.INFO; + public class Forward extends PathHandler { private final int CODE = 302; private final String toPath; @@ -15,7 +17,7 @@ public class Forward extends PathHandler { @Override public boolean doGet(String path, HttpExchange ex) throws IOException { - System.out.printf("Forwarding (%d) %s to %s…\n", CODE, path, toPath); - return sendRedirect(ex,toPath); + LOG.log(INFO,"Forwarding ({0}}) {1} to {2}…", CODE, path, toPath); + return sendRedirect(ex, toPath); } } diff --git a/de.srsoftware.oidc.web/src/main/java/de/srsoftware/oidc/web/StaticPages.java b/de.srsoftware.oidc.web/src/main/java/de/srsoftware/oidc/web/StaticPages.java index ea0c9c0..3859635 100644 --- a/de.srsoftware.oidc.web/src/main/java/de/srsoftware/oidc/web/StaticPages.java +++ b/de.srsoftware.oidc.web/src/main/java/de/srsoftware/oidc/web/StaticPages.java @@ -1,6 +1,7 @@ /* © SRSoftware 2024 */ package de.srsoftware.oidc.web; +import static java.lang.System.Logger.Level.*; import static java.net.HttpURLConnection.HTTP_NOT_FOUND; import com.sun.net.httpserver.HttpExchange; @@ -36,14 +37,12 @@ public class StaticPages extends PathHandler { relativePath = ex.getRequestURI().toString().endsWith(FAVICON) ? FAVICON : INDEX; } try { - System.out.printf("Loading %s for language %s…", relativePath, lang); Response response = loadFile(lang, relativePath).orElseThrow(() -> new FileNotFoundException()); - ex.getResponseHeaders().add(CONTENT_TYPE, response.contentType); - System.out.println("success."); + LOG.log(DEBUG,"Loaded {0} for language {1}…success.", relativePath, lang); return sendContent(ex, response.content); } catch (FileNotFoundException fnf) { - System.err.println("failed!"); + LOG.log(WARNING,"Loaded {0} for language {1}…failed.", relativePath, lang); return sendEmptyResponse(HTTP_NOT_FOUND, ex); } } diff --git a/de.srsoftware.oidc.web/src/main/resources/en/authorization.html b/de.srsoftware.oidc.web/src/main/resources/en/authorization.html index 3e883b8..f3d0077 100644 --- a/de.srsoftware.oidc.web/src/main/resources/en/authorization.html +++ b/de.srsoftware.oidc.web/src/main/resources/en/authorization.html @@ -8,8 +8,13 @@ - -

Authorization!

-Not implemented, yet! + + + \ No newline at end of file diff --git a/de.srsoftware.oidc.web/src/main/resources/en/authorization.js b/de.srsoftware.oidc.web/src/main/resources/en/authorization.js index 7435f94..252025c 100644 --- a/de.srsoftware.oidc.web/src/main/resources/en/authorization.js +++ b/de.srsoftware.oidc.web/src/main/resources/en/authorization.js @@ -1,19 +1,46 @@ var params = new URLSearchParams(window.location.search) var json = Object.fromEntries(params); +function showConfirmationDialog(name){ + get('name').innerHTML = name; + show('content'); +} + async function handleResponse(response){ if (response.ok){ var json = await response.json(); - console.log(json); - redirect(json.redirect_uri+'?code='+json.code+'&state='+json.state); + console.log("handleResponse(ok) ←",json); + if (!json.confirmed){ + showConfirmationDialog(json.name); + } else { + redirect(json.redirect_uri+'?code='+json.code+'&state='+json.state+'&scope=openid'); + } return; + } else { + var json = await response.json(); + console.log("handleResponse(error) ←",json); + get('error').innerHTML = "Error:
"+JSON.stringify(json); + show('error'); } } -fetch(api+"/authorize",{ - method: 'POST', - body: JSON.stringify(json), - headers: { - 'Content-Type': 'application/json' - } -}).then(handleResponse); \ No newline at end of file +function grantAutorization(){ + json.confirmed = true; + backendAutorization(); +} + +function denyAutorization(){ + redirect(params.get('redirect_uri')+"?error=access denied"); +} + +function backendAutorization(){ + fetch(api+"/authorize",{ + method: 'POST', + body: JSON.stringify(json), + headers: { + 'Content-Type': 'application/json' + } + }).then(handleResponse); +} + +backendAutorization(); \ No newline at end of file diff --git a/de.srsoftware.oidc.web/src/main/resources/en/common.js b/de.srsoftware.oidc.web/src/main/resources/en/common.js index 62ee924..3bc151e 100644 --- a/de.srsoftware.oidc.web/src/main/resources/en/common.js +++ b/de.srsoftware.oidc.web/src/main/resources/en/common.js @@ -20,7 +20,6 @@ function getValue(id){ } function hide(id){ - console.log('hide('+id+')'); get(id).style.display = 'none'; } @@ -38,6 +37,5 @@ function setValue(id,newVal){ } function show(id){ - console.log('show('+id+')'); get(id).style.display = ''; } \ No newline at end of file diff --git a/de.srsoftware.oidc.web/src/main/resources/en/login.html b/de.srsoftware.oidc.web/src/main/resources/en/login.html index 29245ba..2526fe5 100644 --- a/de.srsoftware.oidc.web/src/main/resources/en/login.html +++ b/de.srsoftware.oidc.web/src/main/resources/en/login.html @@ -29,5 +29,6 @@ + Umbrella \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 1fafedf..2ceaf4b 100644 --- a/settings.gradle +++ b/settings.gradle @@ -5,4 +5,5 @@ include 'de.srsoftware.oidc.web' include 'de.srsoftware.oidc.backend' include 'de.srsoftware.oidc.datastore.file' include 'de.srsoftware.cookies' +include 'de.srsoftware.logging'