working on client creation
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
/* © SRSoftware 2024 */
|
||||||
package de.srsoftware.oidc.api;
|
package de.srsoftware.oidc.api;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* © SRSoftware 2024 */
|
||||||
package de.srsoftware.oidc.api;
|
package de.srsoftware.oidc.api;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -5,8 +6,8 @@ import java.util.Optional;
|
|||||||
|
|
||||||
public interface ClientService {
|
public interface ClientService {
|
||||||
Optional<Client> getClient(String clientId);
|
Optional<Client> getClient(String clientId);
|
||||||
ClientService add(Client client);
|
ClientService add(Client client);
|
||||||
List<Client> listClients();
|
List<Client> listClients();
|
||||||
ClientService remove(Client client);
|
ClientService remove(Client client);
|
||||||
ClientService update(Client client);
|
ClientService update(Client client);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package de.srsoftware.oidc.api;
|
||||||
|
|
||||||
|
public class Constants {
|
||||||
|
public static final String CLIENT_ID = "client_id";
|
||||||
|
public static final String NAME = "name";
|
||||||
|
public static final String REDIRECT_URI = "redirect_uri";
|
||||||
|
public static final String SECRET = "secret";
|
||||||
|
}
|
||||||
@@ -9,7 +9,6 @@ import com.sun.net.httpserver.HttpExchange;
|
|||||||
import com.sun.net.httpserver.HttpHandler;
|
import com.sun.net.httpserver.HttpHandler;
|
||||||
import com.sun.net.httpserver.HttpServer;
|
import com.sun.net.httpserver.HttpServer;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@@ -115,7 +114,7 @@ public abstract class PathHandler implements HttpHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
||||||
@@ -124,10 +123,10 @@ public abstract class PathHandler implements HttpHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static boolean sendError(HttpExchange ex, byte[] bytes) throws IOException {
|
public static boolean sendError(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 sendError(HttpExchange ex, Object o) throws IOException {
|
||||||
return sendContent(ex,HTTP_BAD_REQUEST,o.toString().getBytes(UTF_8));
|
return sendContent(ex, HTTP_BAD_REQUEST, o.toString().getBytes(UTF_8));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
|
/* © SRSoftware 2024 */
|
||||||
package de.srsoftware.oidc.api;
|
package de.srsoftware.oidc.api;
|
||||||
|
|
||||||
public enum Permission {
|
public enum Permission { MANAGE_CLIENTS }
|
||||||
MANAGE_CLIENTS
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ package de.srsoftware.oidc.api;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
public final class User {
|
public final class User {
|
||||||
public static final String EMAIL = "email";
|
public static final String EMAIL = "email";
|
||||||
public static final String PASSWORD = "password";
|
public static final String PASSWORD = "password";
|
||||||
public static final String PERMISSIONS = "permissions";
|
public static final String PERMISSIONS = "permissions";
|
||||||
public static final String REALNAME = "realname";
|
public static final String REALNAME = "realname";
|
||||||
public static final String USERNAME = "username";
|
public static final String USERNAME = "username";
|
||||||
public static final String UUID = "uuid";
|
public static final String UUID = "uuid";
|
||||||
|
|
||||||
private final Set<Permission> permissions = new HashSet<>();
|
private final Set<Permission> permissions = new HashSet<>();
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ public final class User {
|
|||||||
return Objects.equals(this.uuid, that.uuid);
|
return Objects.equals(this.uuid, that.uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasPermission(Permission permission){
|
public boolean hasPermission(Permission permission) {
|
||||||
return permissions.contains(permission);
|
return permissions.contains(permission);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,9 +65,7 @@ public final class User {
|
|||||||
|
|
||||||
|
|
||||||
public Map<String, Object> map(boolean includePassword) {
|
public Map<String, Object> map(boolean includePassword) {
|
||||||
return includePassword
|
return includePassword ? Map.of(USERNAME, username, REALNAME, realName, EMAIL, email, PERMISSIONS, permissions, UUID, uuid, PASSWORD, hashedPassword) : Map.of(USERNAME, username, REALNAME, realName, EMAIL, email, PERMISSIONS, permissions, UUID, uuid);
|
||||||
? Map.of(USERNAME, username, REALNAME, realName, EMAIL, email, PERMISSIONS, permissions, UUID, uuid, PASSWORD, hashedPassword)
|
|
||||||
: Map.of(USERNAME, username, REALNAME, realName, EMAIL, email, PERMISSIONS, permissions, UUID, uuid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String realName() {
|
public String realName() {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import java.util.Optional;
|
|||||||
|
|
||||||
public interface UserService {
|
public interface UserService {
|
||||||
public UserService delete(User user);
|
public UserService delete(User user);
|
||||||
public boolean passwordMatches(String password, String hashedPassword);
|
public boolean passwordMatches(String password, String hashedPassword);
|
||||||
public UserService init(User defaultUser);
|
public UserService init(User defaultUser);
|
||||||
public List<User> list();
|
public List<User> list();
|
||||||
public Optional<User> load(String id);
|
public Optional<User> load(String id);
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
package de.srsoftware.oidc.app;
|
package de.srsoftware.oidc.app;
|
||||||
|
|
||||||
|
|
||||||
|
import static de.srsoftware.oidc.api.Permission.MANAGE_CLIENTS;
|
||||||
|
|
||||||
import com.sun.net.httpserver.HttpServer;
|
import com.sun.net.httpserver.HttpServer;
|
||||||
import de.srsoftware.oidc.api.ClientService;
|
import de.srsoftware.oidc.api.ClientService;
|
||||||
import de.srsoftware.oidc.api.SessionService;
|
import de.srsoftware.oidc.api.SessionService;
|
||||||
@@ -18,8 +20,6 @@ import java.nio.file.Path;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
import static de.srsoftware.oidc.api.Permission.MANAGE_CLIENTS;
|
|
||||||
|
|
||||||
public class Application {
|
public class Application {
|
||||||
public static final String BACKEND = "/api";
|
public static final String BACKEND = "/api";
|
||||||
private static final String FAVICON = "/favicon.ico";
|
private static final String FAVICON = "/favicon.ico";
|
||||||
@@ -40,7 +40,7 @@ public class Application {
|
|||||||
var firstHash = passwordHasher.hash(FIRST_USER_PASS, FIRST_UUID);
|
var firstHash = passwordHasher.hash(FIRST_USER_PASS, FIRST_UUID);
|
||||||
var firstUser = new User(FIRST_USER, firstHash, FIRST_USER, "%s@internal".formatted(FIRST_USER), FIRST_UUID).add(MANAGE_CLIENTS);
|
var firstUser = new User(FIRST_USER, firstHash, FIRST_USER, "%s@internal".formatted(FIRST_USER), FIRST_UUID).add(MANAGE_CLIENTS);
|
||||||
FileStore fileStore = new FileStore(storageFile, passwordHasher).init(firstUser);
|
FileStore fileStore = new FileStore(storageFile, passwordHasher).init(firstUser);
|
||||||
ClientService clientService = fileStore;
|
ClientService clientService = fileStore;
|
||||||
SessionService sessionService = fileStore;
|
SessionService sessionService = fileStore;
|
||||||
UserService userService = fileStore;
|
UserService userService = fileStore;
|
||||||
HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);
|
HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);
|
||||||
|
|||||||
@@ -3,47 +3,54 @@ package de.srsoftware.oidc.backend;
|
|||||||
|
|
||||||
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 de.srsoftware.oidc.api.Constants.*;
|
||||||
import static java.net.HttpURLConnection.*;
|
import static java.net.HttpURLConnection.*;
|
||||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
|
||||||
|
|
||||||
import com.sun.net.httpserver.HttpExchange;
|
import com.sun.net.httpserver.HttpExchange;
|
||||||
import de.srsoftware.cookies.SessionToken;
|
import de.srsoftware.cookies.SessionToken;
|
||||||
import de.srsoftware.oidc.api.*;
|
import de.srsoftware.oidc.api.*;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import org.json.JSONArray;
|
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
public class Backend extends PathHandler {
|
public class Backend extends PathHandler {
|
||||||
private static final String CLIENT_ID = "client_id";
|
|
||||||
private static final String REDIRECT_URI = "redirect_uri";
|
|
||||||
private final SessionService sessions;
|
private final SessionService sessions;
|
||||||
private final UserService users;
|
private final UserService users;
|
||||||
private final ClientService clients;
|
private final ClientService clients;
|
||||||
|
|
||||||
public Backend(ClientService clientService, SessionService sessionService, UserService userService) {
|
public Backend(ClientService clientService, SessionService sessionService, UserService userService) {
|
||||||
clients = clientService;
|
clients = clientService;
|
||||||
sessions = sessionService;
|
sessions = sessionService;
|
||||||
users = userService;
|
users = userService;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean authorize(HttpExchange ex, Session session) throws IOException {
|
private boolean addClient(HttpExchange ex, Session session) throws IOException {
|
||||||
var json = json(ex);
|
var json = json(ex);
|
||||||
|
var redirects = new HashSet<String>();
|
||||||
|
for (Object o : json.getJSONArray(REDIRECT_URI)){
|
||||||
|
if (o instanceof String s) redirects.add(s);
|
||||||
|
}
|
||||||
|
var client = new Client(json.getString(CLIENT_ID),json.getString(NAME),json.getString(SECRET),redirects);
|
||||||
|
clients.add(client);
|
||||||
|
return sendContent(ex,client);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean authorize(HttpExchange ex, Session session) throws IOException {
|
||||||
|
var json = json(ex);
|
||||||
var clientId = json.getString(CLIENT_ID);
|
var clientId = json.getString(CLIENT_ID);
|
||||||
var redirect = json.getString(REDIRECT_URI);
|
var redirect = json.getString(REDIRECT_URI);
|
||||||
System.out.println(json);
|
System.out.println(json);
|
||||||
return sendEmptyResponse(HTTP_NOT_FOUND,ex);
|
return sendEmptyResponse(HTTP_NOT_FOUND, ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean clients(HttpExchange ex, Session session) throws IOException {
|
private boolean clients(HttpExchange ex, Session session) throws IOException {
|
||||||
var user = session.user();
|
var user = session.user();
|
||||||
if (!user.hasPermission(MANAGE_CLIENTS)) return sendEmptyResponse(HTTP_FORBIDDEN,ex);
|
if (!user.hasPermission(MANAGE_CLIENTS)) return sendEmptyResponse(HTTP_FORBIDDEN, ex);
|
||||||
var json = new JSONObject();
|
var json = new JSONObject();
|
||||||
clients.listClients().forEach(client -> json.put(client.id(), Map.of("name",client.name(),"redirect_uris",client.redirectUris())));
|
clients.listClients().forEach(client -> json.put(client.id(), Map.of("name", client.name(), "redirect_uris", client.redirectUris())));
|
||||||
return sendContent(ex,json);
|
return sendContent(ex, json);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean doLogin(HttpExchange ex) throws IOException {
|
private boolean doLogin(HttpExchange ex) throws IOException {
|
||||||
@@ -72,7 +79,7 @@ public class Backend extends PathHandler {
|
|||||||
var session = optSession.get();
|
var session = optSession.get();
|
||||||
switch (path) {
|
switch (path) {
|
||||||
case "/logout":
|
case "/logout":
|
||||||
return logout(ex,session);
|
return logout(ex, session);
|
||||||
}
|
}
|
||||||
|
|
||||||
System.err.println("not implemented");
|
System.err.println("not implemented");
|
||||||
@@ -92,14 +99,16 @@ public class Backend extends PathHandler {
|
|||||||
// post-login paths
|
// post-login paths
|
||||||
var session = optSession.get();
|
var session = optSession.get();
|
||||||
switch (path) {
|
switch (path) {
|
||||||
|
case "/add/client":
|
||||||
|
return addClient(ex,session);
|
||||||
case "/authorize":
|
case "/authorize":
|
||||||
return authorize(ex,session);
|
return authorize(ex, session);
|
||||||
case "/clients":
|
case "/clients":
|
||||||
return clients(ex,session);
|
return clients(ex, session);
|
||||||
case "/update/password":
|
case "/update/password":
|
||||||
return updatePassword(ex,session);
|
return updatePassword(ex, session);
|
||||||
case "/update/user":
|
case "/update/user":
|
||||||
return updateUser(ex,session);
|
return updateUser(ex, session);
|
||||||
case "/user":
|
case "/user":
|
||||||
return sendUserAndCookie(ex, session);
|
return sendUserAndCookie(ex, session);
|
||||||
}
|
}
|
||||||
@@ -114,7 +123,7 @@ public class Backend extends PathHandler {
|
|||||||
private boolean logout(HttpExchange ex, Session session) throws IOException {
|
private boolean logout(HttpExchange ex, Session session) throws IOException {
|
||||||
sessions.dropSession(session.id());
|
sessions.dropSession(session.id());
|
||||||
new SessionToken("").addTo(ex);
|
new SessionToken("").addTo(ex);
|
||||||
return sendEmptyResponse(HTTP_OK,ex);
|
return sendEmptyResponse(HTTP_OK, ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean openidConfig(HttpExchange ex) throws IOException {
|
private boolean openidConfig(HttpExchange ex) throws IOException {
|
||||||
@@ -128,39 +137,39 @@ public class Backend extends PathHandler {
|
|||||||
|
|
||||||
private boolean sendUserAndCookie(HttpExchange ex, Session session) throws IOException {
|
private boolean sendUserAndCookie(HttpExchange ex, Session session) throws IOException {
|
||||||
new SessionToken(session.id()).addTo(ex);
|
new SessionToken(session.id()).addTo(ex);
|
||||||
return sendContent(ex,new JSONObject(session.user().map(false)));
|
return sendContent(ex, new JSONObject(session.user().map(false)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean updatePassword(HttpExchange ex, Session session) throws IOException {
|
private boolean updatePassword(HttpExchange ex, Session session) throws IOException {
|
||||||
var user =session.user();
|
var user = session.user();
|
||||||
var json = json(ex);
|
var json = json(ex);
|
||||||
var uuid = json.getString(UUID);
|
var uuid = json.getString(UUID);
|
||||||
if (!uuid.equals(user.uuid())) {
|
if (!uuid.equals(user.uuid())) {
|
||||||
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 sendError(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 sendError(ex, "password mismatch");
|
||||||
}
|
}
|
||||||
users.updatePassword(user,newPass1);
|
users.updatePassword(user, newPass1);
|
||||||
return sendContent(ex,new JSONObject(user.map(false)));
|
return sendContent(ex, new JSONObject(user.map(false)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean updateUser(HttpExchange ex, Session session) throws IOException {
|
private boolean updateUser(HttpExchange ex, Session session) throws IOException {
|
||||||
var user =session.user();
|
var user = session.user();
|
||||||
var json = json(ex);
|
var json = json(ex);
|
||||||
var uuid = json.getString(UUID);
|
var uuid = json.getString(UUID);
|
||||||
if (!uuid.equals(user.uuid())){
|
if (!uuid.equals(user.uuid())) {
|
||||||
return sendEmptyResponse(HTTP_FORBIDDEN,ex);
|
return sendEmptyResponse(HTTP_FORBIDDEN, ex);
|
||||||
}
|
}
|
||||||
user.username(json.getString(USERNAME));
|
user.username(json.getString(USERNAME));
|
||||||
user.email(json.getString(EMAIL));
|
user.email(json.getString(EMAIL));
|
||||||
users.save(user);
|
users.save(user);
|
||||||
JSONObject response = new JSONObject(user.map(false));
|
JSONObject response = new JSONObject(user.map(false));
|
||||||
return sendContent(ex,response);
|
return sendContent(ex, response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/* © SRSoftware 2024 */
|
/* © SRSoftware 2024 */
|
||||||
package de.srsoftware.oidc.datastore.file; /* © SRSoftware 2024 */
|
package de.srsoftware.oidc.datastore.file; /* © SRSoftware 2024 */
|
||||||
|
import static de.srsoftware.oidc.api.Constants.CLIENT_ID;
|
||||||
import static de.srsoftware.oidc.api.User.*;
|
import static de.srsoftware.oidc.api.User.*;
|
||||||
|
|
||||||
import de.srsoftware.oidc.api.*;
|
import de.srsoftware.oidc.api.*;
|
||||||
@@ -15,7 +16,11 @@ import java.util.*;
|
|||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
public class FileStore implements ClientService, SessionService, UserService {
|
public class FileStore implements ClientService, SessionService, UserService {
|
||||||
|
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 REDIRECT_URIS = "redirect_uris";
|
||||||
|
private static final String SECRET = "secret";
|
||||||
private static final String SESSIONS = "sessions";
|
private static final String SESSIONS = "sessions";
|
||||||
private static final String USERS = "users";
|
private static final String USERS = "users";
|
||||||
private static final String USER = "user";
|
private static final String USER = "user";
|
||||||
@@ -57,13 +62,13 @@ public class FileStore implements ClientService, SessionService, UserService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FileStore init(User defaultUser) {
|
public FileStore init(User defaultUser) {
|
||||||
|
if (!json.has(CLIENTS)) json.put(CLIENTS, new JSONObject());
|
||||||
if (!json.has(SESSIONS)) json.put(SESSIONS, new JSONObject());
|
if (!json.has(SESSIONS)) json.put(SESSIONS, new JSONObject());
|
||||||
if (!json.has(USERS)) save(defaultUser);
|
if (!json.has(USERS)) save(defaultUser);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<User> list() {
|
public List<User> list() {
|
||||||
return List.of();
|
return List.of();
|
||||||
@@ -73,9 +78,9 @@ public class FileStore implements ClientService, SessionService, UserService {
|
|||||||
@Override
|
@Override
|
||||||
public Optional<User> load(String userId) {
|
public Optional<User> load(String userId) {
|
||||||
try {
|
try {
|
||||||
var users = json.getJSONObject(USERS);
|
var users = json.getJSONObject(USERS);
|
||||||
var userData = users.getJSONObject(userId);
|
var userData = users.getJSONObject(userId);
|
||||||
return userOf(userData,userId);
|
return userOf(userData, userId);
|
||||||
} catch (Exception ignored) {
|
} catch (Exception ignored) {
|
||||||
}
|
}
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
@@ -91,7 +96,7 @@ public class FileStore implements ClientService, SessionService, UserService {
|
|||||||
if (!userData.getString(USERNAME).equals(username)) continue;
|
if (!userData.getString(USERNAME).equals(username)) continue;
|
||||||
var hashedPass = userData.getString(PASSWORD);
|
var hashedPass = userData.getString(PASSWORD);
|
||||||
if (passwordHasher.matches(password, hashedPass)) {
|
if (passwordHasher.matches(password, hashedPass)) {
|
||||||
return userOf(userData,userId);
|
return userOf(userData, userId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
@@ -102,7 +107,7 @@ public class FileStore implements ClientService, SessionService, UserService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean passwordMatches(String password, String hashedPassword) {
|
public boolean passwordMatches(String password, String hashedPassword) {
|
||||||
return passwordHasher.matches(password,hashedPassword);
|
return passwordHasher.matches(password, hashedPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -120,19 +125,19 @@ public class FileStore implements ClientService, SessionService, UserService {
|
|||||||
@Override
|
@Override
|
||||||
public FileStore updatePassword(User user, String plaintextPassword) {
|
public FileStore updatePassword(User user, String plaintextPassword) {
|
||||||
var oldHashedPassword = user.hashedPassword();
|
var oldHashedPassword = user.hashedPassword();
|
||||||
var salt = passwordHasher.salt(oldHashedPassword);
|
var salt = passwordHasher.salt(oldHashedPassword);
|
||||||
user.hashedPassword(passwordHasher.hash(plaintextPassword,salt));
|
user.hashedPassword(passwordHasher.hash(plaintextPassword, salt));
|
||||||
return save(user);
|
return save(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<User> userOf(JSONObject json, String userId){
|
private Optional<User> userOf(JSONObject json, String userId) {
|
||||||
var user = new User(json.getString(USERNAME), json.getString(PASSWORD), json.getString(REALNAME), json.getString(EMAIL), userId);
|
var user = new User(json.getString(USERNAME), json.getString(PASSWORD), json.getString(REALNAME), json.getString(EMAIL), userId);
|
||||||
var perms = json.getJSONArray(PERMISSIONS);
|
var perms = json.getJSONArray(PERMISSIONS);
|
||||||
for (Object perm : perms){
|
for (Object perm : perms) {
|
||||||
try {
|
try {
|
||||||
if (perm instanceof String s) perm = Permission.valueOf(s);
|
if (perm instanceof String s) perm = Permission.valueOf(s);
|
||||||
if (perm instanceof Permission p) user.add(p);
|
if (perm instanceof Permission p) user.add(p);
|
||||||
} catch (Exception e){
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -195,7 +200,9 @@ public class FileStore implements ClientService, SessionService, UserService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ClientService add(Client client) {
|
public ClientService add(Client client) {
|
||||||
return null;
|
json.getJSONObject(CLIENTS).put(client.id(), Map.of(NAME,client.name(),SECRET,client.secret(),REDIRECT_URIS,client.redirectUris()));
|
||||||
|
save();
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -205,7 +212,18 @@ public class FileStore implements ClientService, SessionService, UserService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Client> listClients() {
|
public List<Client> listClients() {
|
||||||
return List.of();
|
var clients = json.getJSONObject(CLIENTS);
|
||||||
|
var list = new ArrayList<Client>();
|
||||||
|
for (var clientId : clients.keySet()){
|
||||||
|
var clientData = clients.getJSONObject(clientId);
|
||||||
|
var redirectUris = new HashSet<String>();
|
||||||
|
for (var o : clientData.getJSONArray(REDIRECT_URIS)){
|
||||||
|
if (o instanceof String s) redirectUris.add(s);
|
||||||
|
}
|
||||||
|
var client = new Client(clientId,clientData.getString(NAME),clientData.getString(SECRET),redirectUris);
|
||||||
|
list.add(client);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ These are clients that are registered with LightOIDC:
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<button onclick="window.location.href='newclient.html';">Add new site…</button>
|
<button onclick="window.location.href='newclient.html';">Add new client…</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
|
|
||||||
async function handleClients(response){
|
async function handleClients(response){
|
||||||
if (response.status == UNAUTHORIZED) {
|
if (response.status == UNAUTHORIZED) {
|
||||||
window.location.href = 'login.html?return_to='+encodeURI(window.location.href);
|
redirect('login.html?return_to='+encodeURI(window.location.href))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var clients = await response.json();
|
var clients = await response.json();
|
||||||
console.log(clients);
|
get()
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch(api+"/clients",{method:'POST'}).then(handleClients);
|
fetch(api+"/clients",{method:'POST'}).then(handleClients);
|
||||||
@@ -19,6 +19,10 @@ function getValue(id){
|
|||||||
return get(id).value;
|
return get(id).value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function redirect(page){
|
||||||
|
window.location.href = page;
|
||||||
|
}
|
||||||
|
|
||||||
function setText(id, text){
|
function setText(id, text){
|
||||||
get(id).innerHTML = text;
|
get(id).innerHTML = text;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,7 @@ async function handleLogin(response){
|
|||||||
|
|
||||||
function doRedirect(){
|
function doRedirect(){
|
||||||
let params = new URL(document.location.toString()).searchParams;
|
let params = new URL(document.location.toString()).searchParams;
|
||||||
let redirect = params.get("return_to") || 'index.html';
|
redirect( params.get("return_to") || 'index.html');
|
||||||
window.location.href = redirect,true;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
function handleLogout(response){
|
function handleLogout(response){
|
||||||
if (response.ok){
|
if (response.ok) document.body.innerHTML += 'success';
|
||||||
document.body.innerHTML += 'success';
|
redirect('index.html')
|
||||||
document.location.href='index.html';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
fetch(api+"/logout").then(handleLogout)
|
fetch(api+"/logout").then(handleLogout)
|
||||||
@@ -4,13 +4,19 @@
|
|||||||
<title>Light OIDC</title>
|
<title>Light OIDC</title>
|
||||||
<script src="common.js"></script>
|
<script src="common.js"></script>
|
||||||
<script src="user.js"></script>
|
<script src="user.js"></script>
|
||||||
|
<script src="newclient.js"></script>
|
||||||
<link rel="stylesheet" href="style.css" />
|
<link rel="stylesheet" href="style.css" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<nav></nav>
|
||||||
<h1>Add new client</h1>
|
<h1>Add new client</h1>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>Settings</legend>
|
<legend>Settings</legend>
|
||||||
<table>
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>client id</th>
|
||||||
|
<td><input type="text" size="50" id="client-id"></td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>client name</th>
|
<th>client name</th>
|
||||||
<td><input type="text" size="50" id="client-name"></td>
|
<td><input type="text" size="50" id="client-name"></td>
|
||||||
@@ -24,6 +30,7 @@
|
|||||||
<td><textarea cols="50" rows="5" id="redirect-urls"></textarea></td>
|
<td><textarea cols="50" rows="5" id="redirect-urls"></textarea></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
<button id="button" type="button" onclick="addClient()">Add client</button>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
37
de.srsoftware.oidc.web/src/main/resources/en/newclient.js
Normal file
37
de.srsoftware.oidc.web/src/main/resources/en/newclient.js
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
function addClient(){
|
||||||
|
disable('button');
|
||||||
|
var newData = {
|
||||||
|
client_id : getValue('client-id'),
|
||||||
|
name : getValue('client-name'),
|
||||||
|
secret : getValue('client-secret'),
|
||||||
|
redirect_uri : getValue('redirect-urls').split("\n")
|
||||||
|
};
|
||||||
|
fetch(api+'/add/client',{
|
||||||
|
method : 'POST',
|
||||||
|
headers : {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body : JSON.stringify(newData)
|
||||||
|
}).then(handleClientdResponse);
|
||||||
|
|
||||||
|
setText('button','sent…');
|
||||||
|
setTimeout(function(){
|
||||||
|
setText('button','Add client');
|
||||||
|
enable('button');
|
||||||
|
},10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleClientdResponse(response){
|
||||||
|
if (response.ok){
|
||||||
|
redirect("clients.html");
|
||||||
|
} else {
|
||||||
|
setText('button','Failed!');
|
||||||
|
enable('button');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkPermissions(){
|
||||||
|
if (user && !user.permissions.includes('MANAGE_CLIENTS')) redirect("index.html");
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(checkPermissions,100);
|
||||||
@@ -28,7 +28,6 @@ function handlePasswordResponse(response){
|
|||||||
|
|
||||||
function update(){
|
function update(){
|
||||||
disable('updateBtn');
|
disable('updateBtn');
|
||||||
setText('updateBtn','sent…');
|
|
||||||
var newData = {
|
var newData = {
|
||||||
username : getValue('username'),
|
username : getValue('username'),
|
||||||
email : getValue('email'),
|
email : getValue('email'),
|
||||||
@@ -41,11 +40,11 @@ function update(){
|
|||||||
},
|
},
|
||||||
body : JSON.stringify(newData)
|
body : JSON.stringify(newData)
|
||||||
}).then(handleResponse)
|
}).then(handleResponse)
|
||||||
|
setText('updateBtn','sent…');
|
||||||
}
|
}
|
||||||
|
|
||||||
function updatePass(){
|
function updatePass(){
|
||||||
disable('passBtn');
|
disable('passBtn');
|
||||||
setText('passBtn','sent…');
|
|
||||||
var newData = {
|
var newData = {
|
||||||
oldpass : getValue('oldpass'),
|
oldpass : getValue('oldpass'),
|
||||||
newpass : [getValue('newpass1'),getValue('newpass2')],
|
newpass : [getValue('newpass1'),getValue('newpass2')],
|
||||||
@@ -58,11 +57,11 @@ function updatePass(){
|
|||||||
},
|
},
|
||||||
body : JSON.stringify(newData)
|
body : JSON.stringify(newData)
|
||||||
}).then(handlePasswordResponse);
|
}).then(handlePasswordResponse);
|
||||||
|
setText('passBtn','sent…');
|
||||||
setTimeout(function(){
|
setTimeout(function(){
|
||||||
setText('passBtn','Update');
|
setText('passBtn','Update');
|
||||||
enable('passBtn');
|
enable('passBtn');
|
||||||
},10000);
|
},10000);
|
||||||
}
|
}
|
||||||
|
|
||||||
function passKeyDown(ev){
|
function passKeyDown(ev){
|
||||||
|
|||||||
@@ -9,4 +9,9 @@ body fieldset {
|
|||||||
|
|
||||||
a {
|
a {
|
||||||
color: yellow;
|
color: yellow;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset th,
|
||||||
|
form th{
|
||||||
|
text-align: right;
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
var user = null;
|
var user = null;
|
||||||
async function handleUser(response){
|
async function handleUser(response){
|
||||||
if (response.status == UNAUTHORIZED) {
|
if (response.status == UNAUTHORIZED) {
|
||||||
window.location.href = 'login.html?return_to='+encodeURI(window.location.href);
|
redirect('login.html?return_to='+encodeURI(window.location.href));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (response.ok){
|
if (response.ok){
|
||||||
|
|||||||
Reference in New Issue
Block a user