implemented custom loggin
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
This commit is contained in:
19
de.srsoftware.logging/build.gradle
Normal file
19
de.srsoftware.logging/build.gradle
Normal file
@@ -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()
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<String, ColorLogger> LOGGERS = new HashMap<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public System.Logger getLogger(String name, Module module) {
|
||||||
|
return LOGGERS.computeIfAbsent(name, ColorLogger::new);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
de.srsoftware.logging.ColorLoggerFinder
|
||||||
@@ -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<User> authorizedUsers(Client client);
|
||||||
|
List<Client> authorizedClients(User user);
|
||||||
|
AuthorizationService revoke(Client client, User user);
|
||||||
|
}
|
||||||
@@ -2,18 +2,20 @@
|
|||||||
package de.srsoftware.oidc.api;
|
package de.srsoftware.oidc.api;
|
||||||
|
|
||||||
import static de.srsoftware.oidc.api.Constants.*;
|
import static de.srsoftware.oidc.api.Constants.*;
|
||||||
|
import static java.lang.System.Logger.Level.WARNING;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public record Client(String id, String name, String secret, Set<String> redirectUris) {
|
public record Client(String id, String name, String secret, Set<String> redirectUris) {
|
||||||
|
private static System.Logger LOG = System.getLogger(Client.class.getSimpleName());
|
||||||
public Map<String, Object> map() {
|
public Map<String, Object> map() {
|
||||||
return Map.of(CLIENT_ID, id, NAME, name, SECRET, secret, REDIRECT_URIS, redirectUris);
|
return Map.of(CLIENT_ID, id, NAME, name, SECRET, secret, REDIRECT_URIS, redirectUris);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String generateCode() {
|
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();
|
return UUID.randomUUID().toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,10 @@
|
|||||||
package de.srsoftware.oidc.api;
|
package de.srsoftware.oidc.api;
|
||||||
|
|
||||||
public class Constants {
|
public class Constants {
|
||||||
public
|
public static final String CAUSE = "cause";
|
||||||
static final String CODE = "code";
|
|
||||||
public static final String CLIENT_ID = "client_id";
|
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 NAME = "name";
|
||||||
public static final String REDIRECT_URI = "redirect_uri";
|
public static final String REDIRECT_URI = "redirect_uri";
|
||||||
public static final String REDIRECT_URIS = "redirect_uris";
|
public static final String REDIRECT_URIS = "redirect_uris";
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
/* © SRSoftware 2024 */
|
/* © SRSoftware 2024 */
|
||||||
package de.srsoftware.oidc.api;
|
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.net.HttpURLConnection.*;
|
||||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
|
|
||||||
@@ -16,6 +18,7 @@ import java.util.stream.Stream;
|
|||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
public abstract class PathHandler implements HttpHandler {
|
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 CONTENT_TYPE = "Content-Type";
|
||||||
public static final String DELETE = "DELETE";
|
public static final String DELETE = "DELETE";
|
||||||
public static final String GET = "GET";
|
public static final String GET = "GET";
|
||||||
@@ -54,7 +57,7 @@ public abstract class PathHandler implements HttpHandler {
|
|||||||
public void handle(HttpExchange ex) throws IOException {
|
public void handle(HttpExchange ex) throws IOException {
|
||||||
String path = relativePath(ex);
|
String path = relativePath(ex);
|
||||||
String method = ex.getRequestMethod();
|
String method = ex.getRequestMethod();
|
||||||
System.out.printf("%s %s\n", method, path);
|
LOG.log(INFO, "{0} {1}", method, path);
|
||||||
boolean ignored = switch (method) {
|
boolean ignored = switch (method) {
|
||||||
case DELETE -> doDelete(path,ex);
|
case DELETE -> doDelete(path,ex);
|
||||||
case GET -> doGet(path,ex);
|
case GET -> doGet(path,ex);
|
||||||
@@ -109,32 +112,38 @@ public abstract class PathHandler implements HttpHandler {
|
|||||||
return false;
|
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 {
|
public static boolean sendContent(HttpExchange ex, int status, byte[] bytes) throws IOException {
|
||||||
ex.sendResponseHeaders(status, bytes.length);
|
ex.sendResponseHeaders(status, bytes.length);
|
||||||
ex.getResponseBody().write(bytes);
|
ex.getResponseBody().write(bytes);
|
||||||
return true;
|
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 {
|
public static boolean sendContent(HttpExchange ex, byte[] bytes) throws IOException {
|
||||||
return sendContent(ex, HTTP_OK, bytes);
|
return sendContent(ex, HTTP_OK, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean sendContent(HttpExchange ex, Object o) throws IOException {
|
public static boolean sendContent(HttpExchange ex, Object o) throws IOException {
|
||||||
if (o instanceof Map map) o = new JSONObject(map);
|
return sendContent(ex, HTTP_OK, o);
|
||||||
if (o instanceof JSONObject) ex.getResponseHeaders().add(CONTENT_TYPE, JSON);
|
|
||||||
return sendContent(ex, HTTP_OK, o.toString().getBytes(UTF_8));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean sendError(HttpExchange ex, byte[] bytes) throws IOException {
|
|
||||||
|
public static boolean badRequest(HttpExchange ex, byte[] bytes) throws IOException {
|
||||||
return sendContent(ex, HTTP_BAD_REQUEST, bytes);
|
return sendContent(ex, HTTP_BAD_REQUEST, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean sendError(HttpExchange ex, Object o) throws IOException {
|
public static boolean badRequest(HttpExchange ex, Object o) throws IOException {
|
||||||
return sendContent(ex, HTTP_BAD_REQUEST, o.toString().getBytes(UTF_8));
|
return sendContent(ex, HTTP_BAD_REQUEST, o);
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean sendRedirect(HttpExchange ex, String url) throws IOException {
|
|
||||||
ex.getResponseHeaders().add("Location", url);
|
|
||||||
return sendEmptyResponse(HTTP_MOVED_TEMP, ex);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ repositories {
|
|||||||
dependencies {
|
dependencies {
|
||||||
testImplementation platform('org.junit:junit-bom:5.10.0')
|
testImplementation platform('org.junit:junit-bom:5.10.0')
|
||||||
testImplementation 'org.junit.jupiter:junit-jupiter'
|
testImplementation 'org.junit.jupiter:junit-jupiter'
|
||||||
|
implementation project(':de.srsoftware.logging')
|
||||||
implementation project(':de.srsoftware.oidc.api')
|
implementation project(':de.srsoftware.oidc.api')
|
||||||
implementation project(':de.srsoftware.oidc.backend')
|
implementation project(':de.srsoftware.oidc.backend')
|
||||||
implementation project(':de.srsoftware.oidc.web')
|
implementation project(':de.srsoftware.oidc.web')
|
||||||
|
|||||||
@@ -3,8 +3,11 @@ package de.srsoftware.oidc.app;
|
|||||||
|
|
||||||
|
|
||||||
import static de.srsoftware.oidc.api.Permission.MANAGE_CLIENTS;
|
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 com.sun.net.httpserver.HttpServer;
|
||||||
|
import de.srsoftware.logging.ColorLogger;
|
||||||
import de.srsoftware.oidc.api.User;
|
import de.srsoftware.oidc.api.User;
|
||||||
import de.srsoftware.oidc.backend.Backend;
|
import de.srsoftware.oidc.backend.Backend;
|
||||||
import de.srsoftware.oidc.datastore.file.FileStore;
|
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 FIRST_UUID = UUID.randomUUID().toString();
|
||||||
public static final String INDEX = STATIC_PATH + "/index.html";
|
public static final String INDEX = STATIC_PATH + "/index.html";
|
||||||
private static final String BASE_PATH = "basePath";
|
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 {
|
public static void main(String[] args) throws Exception {
|
||||||
var argMap = map(args);
|
var argMap = map(args);
|
||||||
@@ -40,7 +44,7 @@ public class Application {
|
|||||||
HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);
|
HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);
|
||||||
new StaticPages(basePath).bindPath(STATIC_PATH, FAVICON).on(server);
|
new StaticPages(basePath).bindPath(STATIC_PATH, FAVICON).on(server);
|
||||||
new Forward(INDEX).bindPath(ROOT).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.setExecutor(Executors.newCachedThreadPool());
|
||||||
server.start();
|
server.start();
|
||||||
}
|
}
|
||||||
@@ -56,7 +60,7 @@ public class Application {
|
|||||||
map.put(BASE_PATH, Path.of(tokens.remove(0)));
|
map.put(BASE_PATH, Path.of(tokens.remove(0)));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
System.err.printf("Unknown option: %s\n", token);
|
LOG.log(ERROR,"Unknown option: {0}", token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ dependencies {
|
|||||||
testImplementation 'org.junit.jupiter:junit-jupiter'
|
testImplementation 'org.junit.jupiter:junit-jupiter'
|
||||||
implementation project(':de.srsoftware.cookies')
|
implementation project(':de.srsoftware.cookies')
|
||||||
implementation project(':de.srsoftware.oidc.api')
|
implementation project(':de.srsoftware.oidc.api')
|
||||||
|
implementation project(':de.srsoftware.logging')
|
||||||
implementation 'org.json:json:20240303'
|
implementation 'org.json:json:20240303'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ package de.srsoftware.oidc.backend;
|
|||||||
import static de.srsoftware.oidc.api.Constants.*;
|
import static de.srsoftware.oidc.api.Constants.*;
|
||||||
import static de.srsoftware.oidc.api.Permission.MANAGE_CLIENTS;
|
import static de.srsoftware.oidc.api.Permission.MANAGE_CLIENTS;
|
||||||
import static de.srsoftware.oidc.api.User.*;
|
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 static java.net.HttpURLConnection.*;
|
||||||
|
|
||||||
import com.sun.net.httpserver.HttpExchange;
|
import com.sun.net.httpserver.HttpExchange;
|
||||||
@@ -16,18 +18,21 @@ import java.util.Optional;
|
|||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
public class Backend extends PathHandler {
|
public class Backend extends PathHandler {
|
||||||
private final SessionService sessions;
|
private static final System.Logger LOG = System.getLogger(Backend.class.getSimpleName());
|
||||||
private final UserService users;
|
private final AuthorizationService authorizations;
|
||||||
private final ClientService clients;
|
private final SessionService sessions;
|
||||||
|
private final UserService users;
|
||||||
|
private final ClientService clients;
|
||||||
|
|
||||||
public Backend(ClientService clientService, SessionService sessionService, UserService userService) {
|
public Backend(AuthorizationService authorizationService, ClientService clientService, SessionService sessionService, UserService userService) {
|
||||||
clients = clientService;
|
authorizations = authorizationService;
|
||||||
sessions = sessionService;
|
clients = clientService;
|
||||||
users = userService;
|
sessions = sessionService;
|
||||||
|
users = userService;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean addClient(HttpExchange ex, Session session) throws IOException {
|
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 json = json(ex);
|
||||||
var redirects = new HashSet<String>();
|
var redirects = new HashSet<String>();
|
||||||
for (Object o : json.getJSONArray(REDIRECT_URIS)) {
|
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 {
|
private boolean authorize(HttpExchange ex, Session session) throws IOException {
|
||||||
|
var user = session.user();
|
||||||
var json = json(ex);
|
var json = json(ex);
|
||||||
var clientId = json.getString(CLIENT_ID);
|
var clientId = json.getString(CLIENT_ID);
|
||||||
|
var redirect = json.getString(REDIRECT_URI);
|
||||||
var optClient = clients.getClient(clientId);
|
var optClient = clients.getClient(clientId);
|
||||||
if (optClient.isEmpty()) return sendEmptyResponse(HTTP_NOT_FOUND, ex);
|
if (optClient.isEmpty()) return badRequest(ex, Map.of(CAUSE, "unknown client", CLIENT_ID, clientId));
|
||||||
var client = optClient.get();
|
var client = optClient.get();
|
||||||
var redirect = json.getString(REDIRECT_URI);
|
|
||||||
if (!client.redirectUris().contains(redirect)) return sendEmptyResponse(HTTP_BAD_REQUEST, ex);
|
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 state = json.getString(STATE);
|
||||||
var code = client.generateCode();
|
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 {
|
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 {
|
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 json = json(ex);
|
||||||
var id = json.getString(CLIENT_ID);
|
var id = json.getString(CLIENT_ID);
|
||||||
clients.getClient(id).ifPresent(clients::remove);
|
clients.getClient(id).ifPresent(clients::remove);
|
||||||
@@ -89,8 +104,7 @@ public class Backend extends PathHandler {
|
|||||||
case "/client":
|
case "/client":
|
||||||
return deleteClient(ex, session);
|
return deleteClient(ex, session);
|
||||||
}
|
}
|
||||||
|
LOG.log(ERROR, "not implemented");
|
||||||
System.err.println("not implemented");
|
|
||||||
return sendEmptyResponse(HTTP_NOT_FOUND, ex);
|
return sendEmptyResponse(HTTP_NOT_FOUND, ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,7 +126,7 @@ public class Backend extends PathHandler {
|
|||||||
return logout(ex, session);
|
return logout(ex, session);
|
||||||
}
|
}
|
||||||
|
|
||||||
System.err.println("not implemented");
|
LOG.log(WARNING,"not implemented");
|
||||||
return sendEmptyResponse(HTTP_NOT_FOUND, ex);
|
return sendEmptyResponse(HTTP_NOT_FOUND, ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,7 +160,7 @@ public class Backend extends PathHandler {
|
|||||||
case "/user":
|
case "/user":
|
||||||
return sendUserAndCookie(ex, session);
|
return sendUserAndCookie(ex, session);
|
||||||
}
|
}
|
||||||
System.err.println("not implemented");
|
LOG.log(WARNING,"not implemented");
|
||||||
return sendEmptyResponse(HTTP_NOT_FOUND, ex);
|
return sendEmptyResponse(HTTP_NOT_FOUND, ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,17 +186,14 @@ public class Backend extends PathHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean provideToken(HttpExchange ex) throws IOException {
|
private boolean provideToken(HttpExchange ex) throws IOException {
|
||||||
System.err.printf("%s.provideToken(ex) not implemented!\n",getClass().getSimpleName());
|
LOG.log(ERROR,"{0}.provideToken(ex) not implemented!\n", getClass().getSimpleName());
|
||||||
var json = json(ex);
|
LOG.log(WARNING,json(ex));
|
||||||
System.err.println(json);
|
return sendEmptyResponse(HTTP_NOT_FOUND, ex);
|
||||||
return sendEmptyResponse(HTTP_NOT_FOUND,ex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean openidConfig(HttpExchange ex) throws IOException {
|
private boolean openidConfig(HttpExchange ex) throws IOException {
|
||||||
return sendContent(ex, Map.of(
|
var host = hostname(ex);
|
||||||
"token_endpoint",hostname(ex)+"/api/token",
|
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"));
|
||||||
"authorization_endpoint", hostname(ex) + "/web/authorization.html")
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean sendUserAndCookie(HttpExchange ex, Session session) throws IOException {
|
private boolean sendUserAndCookie(HttpExchange ex, Session session) throws IOException {
|
||||||
@@ -198,12 +209,12 @@ public class Backend extends PathHandler {
|
|||||||
return sendEmptyResponse(HTTP_FORBIDDEN, ex);
|
return sendEmptyResponse(HTTP_FORBIDDEN, ex);
|
||||||
}
|
}
|
||||||
var oldPass = json.getString("oldpass");
|
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 newpass = json.getJSONArray("newpass");
|
||||||
var newPass1 = newpass.getString(0);
|
var newPass1 = newpass.getString(0);
|
||||||
if (!newPass1.equals(newpass.getString(1))) {
|
if (!newPass1.equals(newpass.getString(1))) {
|
||||||
return sendError(ex, "password mismatch");
|
return badRequest(ex, "password mismatch");
|
||||||
}
|
}
|
||||||
users.updatePassword(user, newPass1);
|
users.updatePassword(user, newPass1);
|
||||||
return sendContent(ex, user.map(false));
|
return sendContent(ex, user.map(false));
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import java.time.temporal.ChronoUnit;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
import org.json.JSONObject;
|
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 CLIENTS = "clients";
|
||||||
private static final String EXPIRATION = "expiration";
|
private static final String EXPIRATION = "expiration";
|
||||||
private static final String NAME = "name";
|
private static final String NAME = "name";
|
||||||
@@ -238,4 +238,31 @@ public class FileStore implements ClientService, SessionService, UserService {
|
|||||||
public ClientService update(Client client) {
|
public ClientService update(Client client) {
|
||||||
return null;
|
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<User> authorizedUsers(Client client) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Client> authorizedClients(User user) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AuthorizationService revoke(Client client, User user) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import com.sun.net.httpserver.HttpExchange;
|
|||||||
import de.srsoftware.oidc.api.PathHandler;
|
import de.srsoftware.oidc.api.PathHandler;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import static java.lang.System.Logger.Level.INFO;
|
||||||
|
|
||||||
public class Forward extends PathHandler {
|
public class Forward extends PathHandler {
|
||||||
private final int CODE = 302;
|
private final int CODE = 302;
|
||||||
private final String toPath;
|
private final String toPath;
|
||||||
@@ -15,7 +17,7 @@ public class Forward extends PathHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean doGet(String path, HttpExchange ex) throws IOException {
|
public boolean doGet(String path, HttpExchange ex) throws IOException {
|
||||||
System.out.printf("Forwarding (%d) %s to %s…\n", CODE, path, toPath);
|
LOG.log(INFO,"Forwarding ({0}}) {1} to {2}…", CODE, path, toPath);
|
||||||
return sendRedirect(ex,toPath);
|
return sendRedirect(ex, toPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
/* © SRSoftware 2024 */
|
/* © SRSoftware 2024 */
|
||||||
package de.srsoftware.oidc.web;
|
package de.srsoftware.oidc.web;
|
||||||
|
|
||||||
|
import static java.lang.System.Logger.Level.*;
|
||||||
import static java.net.HttpURLConnection.HTTP_NOT_FOUND;
|
import static java.net.HttpURLConnection.HTTP_NOT_FOUND;
|
||||||
|
|
||||||
import com.sun.net.httpserver.HttpExchange;
|
import com.sun.net.httpserver.HttpExchange;
|
||||||
@@ -36,14 +37,12 @@ public class StaticPages extends PathHandler {
|
|||||||
relativePath = ex.getRequestURI().toString().endsWith(FAVICON) ? FAVICON : INDEX;
|
relativePath = ex.getRequestURI().toString().endsWith(FAVICON) ? FAVICON : INDEX;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
System.out.printf("Loading %s for language %s…", relativePath, lang);
|
|
||||||
Response response = loadFile(lang, relativePath).orElseThrow(() -> new FileNotFoundException());
|
Response response = loadFile(lang, relativePath).orElseThrow(() -> new FileNotFoundException());
|
||||||
|
|
||||||
ex.getResponseHeaders().add(CONTENT_TYPE, response.contentType);
|
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);
|
return sendContent(ex, response.content);
|
||||||
} catch (FileNotFoundException fnf) {
|
} catch (FileNotFoundException fnf) {
|
||||||
System.err.println("failed!");
|
LOG.log(WARNING,"Loaded {0} for language {1}…failed.", relativePath, lang);
|
||||||
return sendEmptyResponse(HTTP_NOT_FOUND, ex);
|
return sendEmptyResponse(HTTP_NOT_FOUND, ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,13 @@
|
|||||||
<link rel="stylesheet" href="style.css" />
|
<link rel="stylesheet" href="style.css" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<nav></nav>
|
<nav></nav>
|
||||||
<h1>Authorization!</h1>
|
<div id="content" style="display: none">
|
||||||
Not implemented, yet!
|
<h1>Authorization</h1>
|
||||||
|
Confirmation required: are you shure you want to grant access to <span id="name">some client</span>?
|
||||||
|
<button type="button" onclick="grantAutorization()">Yes</button>
|
||||||
|
<button type="button" onclick="denyAutorization()">No</button>
|
||||||
|
</div>
|
||||||
|
<div id="error" class="error" style="display: none"></div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -1,19 +1,46 @@
|
|||||||
var params = new URLSearchParams(window.location.search)
|
var params = new URLSearchParams(window.location.search)
|
||||||
var json = Object.fromEntries(params);
|
var json = Object.fromEntries(params);
|
||||||
|
|
||||||
|
function showConfirmationDialog(name){
|
||||||
|
get('name').innerHTML = name;
|
||||||
|
show('content');
|
||||||
|
}
|
||||||
|
|
||||||
async function handleResponse(response){
|
async function handleResponse(response){
|
||||||
if (response.ok){
|
if (response.ok){
|
||||||
var json = await response.json();
|
var json = await response.json();
|
||||||
console.log(json);
|
console.log("handleResponse(ok) ←",json);
|
||||||
redirect(json.redirect_uri+'?code='+json.code+'&state='+json.state);
|
if (!json.confirmed){
|
||||||
|
showConfirmationDialog(json.name);
|
||||||
|
} else {
|
||||||
|
redirect(json.redirect_uri+'?code='+json.code+'&state='+json.state+'&scope=openid');
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
|
} else {
|
||||||
|
var json = await response.json();
|
||||||
|
console.log("handleResponse(error) ←",json);
|
||||||
|
get('error').innerHTML = "Error: <br/>"+JSON.stringify(json);
|
||||||
|
show('error');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch(api+"/authorize",{
|
function grantAutorization(){
|
||||||
method: 'POST',
|
json.confirmed = true;
|
||||||
body: JSON.stringify(json),
|
backendAutorization();
|
||||||
headers: {
|
}
|
||||||
'Content-Type': 'application/json'
|
|
||||||
}
|
function denyAutorization(){
|
||||||
}).then(handleResponse);
|
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();
|
||||||
@@ -20,7 +20,6 @@ function getValue(id){
|
|||||||
}
|
}
|
||||||
|
|
||||||
function hide(id){
|
function hide(id){
|
||||||
console.log('hide('+id+')');
|
|
||||||
get(id).style.display = 'none';
|
get(id).style.display = 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,6 +37,5 @@ function setValue(id,newVal){
|
|||||||
}
|
}
|
||||||
|
|
||||||
function show(id){
|
function show(id){
|
||||||
console.log('show('+id+')');
|
|
||||||
get(id).style.display = '';
|
get(id).style.display = '';
|
||||||
}
|
}
|
||||||
@@ -29,5 +29,6 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
<a href="https://umbrella.srsoftware.de/user/login">Umbrella</a>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -5,4 +5,5 @@ include 'de.srsoftware.oidc.web'
|
|||||||
include 'de.srsoftware.oidc.backend'
|
include 'de.srsoftware.oidc.backend'
|
||||||
include 'de.srsoftware.oidc.datastore.file'
|
include 'de.srsoftware.oidc.datastore.file'
|
||||||
include 'de.srsoftware.cookies'
|
include 'de.srsoftware.cookies'
|
||||||
|
include 'de.srsoftware.logging'
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user