Browse Source

working on client creation

Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
sqlite
Stephan Richter 4 months ago
parent
commit
1c0ef0e257
  1. 1
      de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/Client.java
  2. 1
      de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/ClientService.java
  3. 8
      de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/Constants.java
  4. 7
      de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/PathHandler.java
  5. 5
      de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/Permission.java
  6. 6
      de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/User.java
  7. 4
      de.srsoftware.oidc.app/src/main/java/de/srsoftware/oidc/app/Application.java
  8. 63
      de.srsoftware.oidc.backend/src/main/java/de/srsoftware/oidc/backend/Backend.java
  9. 38
      de.srsoftware.oidc.datastore.file/src/main/java/de/srsoftware/oidc/datastore/file/FileStore.java
  10. 2
      de.srsoftware.oidc.web/src/main/resources/en/clients.html
  11. 4
      de.srsoftware.oidc.web/src/main/resources/en/clients.js
  12. 4
      de.srsoftware.oidc.web/src/main/resources/en/common.js
  13. 3
      de.srsoftware.oidc.web/src/main/resources/en/login.js
  14. 6
      de.srsoftware.oidc.web/src/main/resources/en/logout.js
  15. 7
      de.srsoftware.oidc.web/src/main/resources/en/newclient.html
  16. 37
      de.srsoftware.oidc.web/src/main/resources/en/newclient.js
  17. 5
      de.srsoftware.oidc.web/src/main/resources/en/settings.js
  18. 5
      de.srsoftware.oidc.web/src/main/resources/en/style.css
  19. 2
      de.srsoftware.oidc.web/src/main/resources/en/user.js

1
de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/Client.java

@ -1,3 +1,4 @@ @@ -1,3 +1,4 @@
/* © SRSoftware 2024 */
package de.srsoftware.oidc.api;
import java.util.Set;

1
de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/ClientService.java

@ -1,3 +1,4 @@ @@ -1,3 +1,4 @@
/* © SRSoftware 2024 */
package de.srsoftware.oidc.api;
import java.util.List;

8
de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/Constants.java

@ -0,0 +1,8 @@ @@ -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";
}

7
de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/PathHandler.java

@ -9,7 +9,6 @@ import com.sun.net.httpserver.HttpExchange; @@ -9,7 +9,6 @@ import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
@ -115,7 +114,7 @@ public abstract class PathHandler implements HttpHandler { @@ -115,7 +114,7 @@ public abstract class PathHandler implements HttpHandler {
}
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 {
@ -124,10 +123,10 @@ public abstract class PathHandler implements HttpHandler { @@ -124,10 +123,10 @@ public abstract class PathHandler implements HttpHandler {
}
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 {
return sendContent(ex,HTTP_BAD_REQUEST,o.toString().getBytes(UTF_8));
return sendContent(ex, HTTP_BAD_REQUEST, o.toString().getBytes(UTF_8));
}
}

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

@ -1,5 +1,4 @@ @@ -1,5 +1,4 @@
/* © SRSoftware 2024 */
package de.srsoftware.oidc.api;
public enum Permission {
MANAGE_CLIENTS
}
public enum Permission { MANAGE_CLIENTS }

6
de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/User.java

@ -45,7 +45,7 @@ public final class User { @@ -45,7 +45,7 @@ public final class User {
return Objects.equals(this.uuid, that.uuid);
}
public boolean hasPermission(Permission permission){
public boolean hasPermission(Permission permission) {
return permissions.contains(permission);
}
@ -65,9 +65,7 @@ public final class User { @@ -65,9 +65,7 @@ public final class User {
public Map<String, Object> map(boolean 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);
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);
}
public String realName() {

4
de.srsoftware.oidc.app/src/main/java/de/srsoftware/oidc/app/Application.java

@ -2,6 +2,8 @@ @@ -2,6 +2,8 @@
package de.srsoftware.oidc.app;
import static de.srsoftware.oidc.api.Permission.MANAGE_CLIENTS;
import com.sun.net.httpserver.HttpServer;
import de.srsoftware.oidc.api.ClientService;
import de.srsoftware.oidc.api.SessionService;
@ -18,8 +20,6 @@ import java.nio.file.Path; @@ -18,8 +20,6 @@ import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.Executors;
import static de.srsoftware.oidc.api.Permission.MANAGE_CLIENTS;
public class Application {
public static final String BACKEND = "/api";
private static final String FAVICON = "/favicon.ico";

63
de.srsoftware.oidc.backend/src/main/java/de/srsoftware/oidc/backend/Backend.java

@ -3,23 +3,19 @@ package de.srsoftware.oidc.backend; @@ -3,23 +3,19 @@ package de.srsoftware.oidc.backend;
import static de.srsoftware.oidc.api.Permission.MANAGE_CLIENTS;
import static de.srsoftware.oidc.api.User.*;
import static de.srsoftware.oidc.api.Constants.*;
import static java.net.HttpURLConnection.*;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.sun.net.httpserver.HttpExchange;
import de.srsoftware.cookies.SessionToken;
import de.srsoftware.oidc.api.*;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import org.json.JSONArray;
import org.json.JSONObject;
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 UserService users;
private final ClientService clients;
@ -30,20 +26,31 @@ public class Backend extends PathHandler { @@ -30,20 +26,31 @@ public class Backend extends PathHandler {
users = userService;
}
private boolean addClient(HttpExchange ex, Session session) throws IOException {
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 redirect = json.getString(REDIRECT_URI);
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 {
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();
clients.listClients().forEach(client -> json.put(client.id(), Map.of("name",client.name(),"redirect_uris",client.redirectUris())));
return sendContent(ex,json);
clients.listClients().forEach(client -> json.put(client.id(), Map.of("name", client.name(), "redirect_uris", client.redirectUris())));
return sendContent(ex, json);
}
private boolean doLogin(HttpExchange ex) throws IOException {
@ -72,7 +79,7 @@ public class Backend extends PathHandler { @@ -72,7 +79,7 @@ public class Backend extends PathHandler {
var session = optSession.get();
switch (path) {
case "/logout":
return logout(ex,session);
return logout(ex, session);
}
System.err.println("not implemented");
@ -92,14 +99,16 @@ public class Backend extends PathHandler { @@ -92,14 +99,16 @@ public class Backend extends PathHandler {
// post-login paths
var session = optSession.get();
switch (path) {
case "/add/client":
return addClient(ex,session);
case "/authorize":
return authorize(ex,session);
return authorize(ex, session);
case "/clients":
return clients(ex,session);
return clients(ex, session);
case "/update/password":
return updatePassword(ex,session);
return updatePassword(ex, session);
case "/update/user":
return updateUser(ex,session);
return updateUser(ex, session);
case "/user":
return sendUserAndCookie(ex, session);
}
@ -114,7 +123,7 @@ public class Backend extends PathHandler { @@ -114,7 +123,7 @@ public class Backend extends PathHandler {
private boolean logout(HttpExchange ex, Session session) throws IOException {
sessions.dropSession(session.id());
new SessionToken("").addTo(ex);
return sendEmptyResponse(HTTP_OK,ex);
return sendEmptyResponse(HTTP_OK, ex);
}
private boolean openidConfig(HttpExchange ex) throws IOException {
@ -128,39 +137,39 @@ public class Backend extends PathHandler { @@ -128,39 +137,39 @@ public class Backend extends PathHandler {
private boolean sendUserAndCookie(HttpExchange ex, Session session) throws IOException {
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 {
var user =session.user();
var user = session.user();
var json = json(ex);
var uuid = json.getString(UUID);
if (!uuid.equals(user.uuid())) {
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 sendError(ex, "wrong password");
var newpass = json.getJSONArray("newpass");
var newPass1 = newpass.getString(0);
if (!newPass1.equals(newpass.getString(1))){
return sendError(ex,"password mismatch");
if (!newPass1.equals(newpass.getString(1))) {
return sendError(ex, "password mismatch");
}
users.updatePassword(user,newPass1);
return sendContent(ex,new JSONObject(user.map(false)));
users.updatePassword(user, newPass1);
return sendContent(ex, new JSONObject(user.map(false)));
}
private boolean updateUser(HttpExchange ex, Session session) throws IOException {
var user =session.user();
var user = session.user();
var json = json(ex);
var uuid = json.getString(UUID);
if (!uuid.equals(user.uuid())){
return sendEmptyResponse(HTTP_FORBIDDEN,ex);
if (!uuid.equals(user.uuid())) {
return sendEmptyResponse(HTTP_FORBIDDEN, ex);
}
user.username(json.getString(USERNAME));
user.email(json.getString(EMAIL));
users.save(user);
JSONObject response = new JSONObject(user.map(false));
return sendContent(ex,response);
return sendContent(ex, response);
}
}

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

@ -1,5 +1,6 @@ @@ -1,5 +1,6 @@
/* © 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 de.srsoftware.oidc.api.*;
@ -15,7 +16,11 @@ import java.util.*; @@ -15,7 +16,11 @@ import java.util.*;
import org.json.JSONObject;
public class FileStore implements ClientService, SessionService, UserService {
private static final String CLIENTS = "clients";
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 USERS = "users";
private static final String USER = "user";
@ -57,13 +62,13 @@ public class FileStore implements ClientService, SessionService, UserService { @@ -57,13 +62,13 @@ public class FileStore implements ClientService, SessionService, UserService {
@Override
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(USERS)) save(defaultUser);
return this;
}
@Override
public List<User> list() {
return List.of();
@ -75,7 +80,7 @@ public class FileStore implements ClientService, SessionService, UserService { @@ -75,7 +80,7 @@ public class FileStore implements ClientService, SessionService, UserService {
try {
var users = json.getJSONObject(USERS);
var userData = users.getJSONObject(userId);
return userOf(userData,userId);
return userOf(userData, userId);
} catch (Exception ignored) {
}
return Optional.empty();
@ -91,7 +96,7 @@ public class FileStore implements ClientService, SessionService, UserService { @@ -91,7 +96,7 @@ public class FileStore implements ClientService, SessionService, UserService {
if (!userData.getString(USERNAME).equals(username)) continue;
var hashedPass = userData.getString(PASSWORD);
if (passwordHasher.matches(password, hashedPass)) {
return userOf(userData,userId);
return userOf(userData, userId);
}
}
return Optional.empty();
@ -102,7 +107,7 @@ public class FileStore implements ClientService, SessionService, UserService { @@ -102,7 +107,7 @@ public class FileStore implements ClientService, SessionService, UserService {
@Override
public boolean passwordMatches(String password, String hashedPassword) {
return passwordHasher.matches(password,hashedPassword);
return passwordHasher.matches(password, hashedPassword);
}
@Override
@ -121,18 +126,18 @@ public class FileStore implements ClientService, SessionService, UserService { @@ -121,18 +126,18 @@ public class FileStore implements ClientService, SessionService, UserService {
public FileStore updatePassword(User user, String plaintextPassword) {
var oldHashedPassword = user.hashedPassword();
var salt = passwordHasher.salt(oldHashedPassword);
user.hashedPassword(passwordHasher.hash(plaintextPassword,salt));
user.hashedPassword(passwordHasher.hash(plaintextPassword, salt));
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 perms = json.getJSONArray(PERMISSIONS);
for (Object perm : perms){
for (Object perm : perms) {
try {
if (perm instanceof String s) perm = Permission.valueOf(s);
if (perm instanceof Permission p) user.add(p);
} catch (Exception e){
} catch (Exception e) {
e.printStackTrace();
}
}
@ -195,7 +200,9 @@ public class FileStore implements ClientService, SessionService, UserService { @@ -195,7 +200,9 @@ public class FileStore implements ClientService, SessionService, UserService {
@Override
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
@ -205,7 +212,18 @@ public class FileStore implements ClientService, SessionService, UserService { @@ -205,7 +212,18 @@ public class FileStore implements ClientService, SessionService, UserService {
@Override
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

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

@ -21,7 +21,7 @@ These are clients that are registered with LightOIDC: @@ -21,7 +21,7 @@ These are clients that are registered with LightOIDC:
</tr>
<tr>
<td>
<button onclick="window.location.href='newclient.html';">Add new site</button>
<button onclick="window.location.href='newclient.html';">Add new client</button>
</td>
</tr>
</table>

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

@ -1,11 +1,11 @@ @@ -1,11 +1,11 @@
async function handleClients(response){
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;
}
var clients = await response.json();
console.log(clients);
get()
}
fetch(api+"/clients",{method:'POST'}).then(handleClients);

4
de.srsoftware.oidc.web/src/main/resources/en/common.js

@ -19,6 +19,10 @@ function getValue(id){ @@ -19,6 +19,10 @@ function getValue(id){
return get(id).value;
}
function redirect(page){
window.location.href = page;
}
function setText(id, text){
get(id).innerHTML = text;
}

3
de.srsoftware.oidc.web/src/main/resources/en/login.js

@ -9,8 +9,7 @@ async function handleLogin(response){ @@ -9,8 +9,7 @@ async function handleLogin(response){
function doRedirect(){
let params = new URL(document.location.toString()).searchParams;
let redirect = params.get("return_to") || 'index.html';
window.location.href = redirect,true;
redirect( params.get("return_to") || 'index.html');
return false;
}

6
de.srsoftware.oidc.web/src/main/resources/en/logout.js

@ -1,7 +1,5 @@ @@ -1,7 +1,5 @@
function handleLogout(response){
if (response.ok){
document.body.innerHTML += 'success';
document.location.href='index.html';
}
if (response.ok) document.body.innerHTML += 'success';
redirect('index.html')
}
fetch(api+"/logout").then(handleLogout)

7
de.srsoftware.oidc.web/src/main/resources/en/newclient.html

@ -4,13 +4,19 @@ @@ -4,13 +4,19 @@
<title>Light OIDC</title>
<script src="common.js"></script>
<script src="user.js"></script>
<script src="newclient.js"></script>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<nav></nav>
<h1>Add new client</h1>
<fieldset>
<legend>Settings</legend>
<table>
<tr>
<th>client id</th>
<td><input type="text" size="50" id="client-id"></td>
</tr>
<tr>
<th>client name</th>
<td><input type="text" size="50" id="client-name"></td>
@ -24,6 +30,7 @@ @@ -24,6 +30,7 @@
<td><textarea cols="50" rows="5" id="redirect-urls"></textarea></td>
</tr>
</table>
<button id="button" type="button" onclick="addClient()">Add client</button>
</fieldset>
</body>
</html>

37
de.srsoftware.oidc.web/src/main/resources/en/newclient.js

@ -0,0 +1,37 @@ @@ -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);

5
de.srsoftware.oidc.web/src/main/resources/en/settings.js

@ -28,7 +28,6 @@ function handlePasswordResponse(response){ @@ -28,7 +28,6 @@ function handlePasswordResponse(response){
function update(){
disable('updateBtn');
setText('updateBtn','sent…');
var newData = {
username : getValue('username'),
email : getValue('email'),
@ -41,11 +40,11 @@ function update(){ @@ -41,11 +40,11 @@ function update(){
},
body : JSON.stringify(newData)
}).then(handleResponse)
setText('updateBtn','sent…');
}
function updatePass(){
disable('passBtn');
setText('passBtn','sent…');
var newData = {
oldpass : getValue('oldpass'),
newpass : [getValue('newpass1'),getValue('newpass2')],
@ -58,7 +57,7 @@ function updatePass(){ @@ -58,7 +57,7 @@ function updatePass(){
},
body : JSON.stringify(newData)
}).then(handlePasswordResponse);
setText('passBtn','sent…');
setTimeout(function(){
setText('passBtn','Update');
enable('passBtn');

5
de.srsoftware.oidc.web/src/main/resources/en/style.css

@ -10,3 +10,8 @@ body fieldset { @@ -10,3 +10,8 @@ body fieldset {
a {
color: yellow;
}
fieldset th,
form th{
text-align: right;
}

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

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
var user = null;
async function handleUser(response){
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;
}
if (response.ok){

Loading…
Cancel
Save