Browse Source

implemented permission editing

Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
sqlite
Stephan Richter 3 months ago
parent
commit
b275064aba
  1. 2
      de.srsoftware.http/src/main/java/de/srsoftware/http/SessionToken.java
  2. 1
      de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/Constants.java
  3. 2
      de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/data/Permission.java
  4. 5
      de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/data/User.java
  5. 2
      de.srsoftware.oidc.app/src/main/java/de/srsoftware/oidc/app/Application.java
  6. 58
      de.srsoftware.oidc.backend/src/main/java/de/srsoftware/oidc/backend/UserController.java
  7. 7
      de.srsoftware.oidc.web/src/main/resources/en/scripts/common.js
  8. 1
      de.srsoftware.oidc.web/src/main/resources/en/scripts/settings.js
  9. 32
      de.srsoftware.oidc.web/src/main/resources/en/scripts/users.js
  10. 1
      de.srsoftware.oidc.web/src/main/resources/en/users.html

2
de.srsoftware.http/src/main/java/de/srsoftware/http/SessionToken.java

@ -18,7 +18,7 @@ public class SessionToken extends Cookie {
@Override @Override
public <T extends Cookie> T addTo(Headers headers) { public <T extends Cookie> T addTo(Headers headers) {
headers.add("session", sessionId); headers.add("session", sessionId);
return (T)this;//super.addTo(headers); return (T)this; // super.addTo(headers);
} }
public static Optional<SessionToken> from(HttpExchange ex) { public static Optional<SessionToken> from(HttpExchange ex) {

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

@ -32,6 +32,7 @@ public class Constants {
public static final String MAILCONFIG = "mail_config"; public static final String MAILCONFIG = "mail_config";
public static final String NAME = "name"; public static final String NAME = "name";
public static final String NONCE = "nonce"; public static final String NONCE = "nonce";
public static final String PERMISSION = "permission";
public static final String OPENID = "openid"; public static final String OPENID = "openid";
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";

2
de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/data/Permission.java

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

5
de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/data/User.java

@ -33,6 +33,11 @@ public final class User {
return this; return this;
} }
public User drop(Permission... perms) {
for (var permission : perms) permissions.remove(permission);
return this;
}
public String email() { public String email() {
return email; return email;
} }

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

@ -55,7 +55,7 @@ public class Application {
var keyDir = storageFile.getParentFile().toPath().resolve("keys"); var keyDir = storageFile.getParentFile().toPath().resolve("keys");
var passwordHasher = new UuidHasher(); var passwordHasher = new UuidHasher();
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, MANAGE_SMTP, MANAGE_USERS); var firstUser = new User(FIRST_USER, firstHash, FIRST_USER, "%s@internal".formatted(FIRST_USER), FIRST_UUID).add(MANAGE_CLIENTS, MANAGE_PERMISSIONS, MANAGE_SMTP, MANAGE_USERS);
KeyStorage keyStore = new PlaintextKeyStore(keyDir); KeyStorage keyStore = new PlaintextKeyStore(keyDir);
{ // SQLite { // SQLite
SQLiteDataSource dataSource = new SQLiteDataSource(); SQLiteDataSource dataSource = new SQLiteDataSource();

58
de.srsoftware.oidc.backend/src/main/java/de/srsoftware/oidc/backend/UserController.java

@ -12,6 +12,7 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpExchange;
import de.srsoftware.http.SessionToken; import de.srsoftware.http.SessionToken;
import de.srsoftware.oidc.api.*; import de.srsoftware.oidc.api.*;
import de.srsoftware.oidc.api.data.Permission;
import de.srsoftware.oidc.api.data.Session; import de.srsoftware.oidc.api.data.Session;
import de.srsoftware.oidc.api.data.User; import de.srsoftware.oidc.api.data.User;
import jakarta.mail.*; import jakarta.mail.*;
@ -35,8 +36,7 @@ public class UserController extends Controller {
this.resourceLoader = resourceLoader; this.resourceLoader = resourceLoader;
} }
private boolean addUser(HttpExchange ex, Session session) throws IOException { private boolean addUser(HttpExchange ex, User user) throws IOException {
var user = session.user();
if (!user.hasPermission(MANAGE_USERS)) return sendEmptyResponse(HTTP_FORBIDDEN, ex); if (!user.hasPermission(MANAGE_USERS)) return sendEmptyResponse(HTTP_FORBIDDEN, ex);
var json = json(ex); var json = json(ex);
var newID = uuid(); var newID = uuid();
@ -50,22 +50,24 @@ public class UserController extends Controller {
if (optSession.isEmpty()) return sendEmptyResponse(HTTP_UNAUTHORIZED, ex); if (optSession.isEmpty()) return sendEmptyResponse(HTTP_UNAUTHORIZED, ex);
// post-login paths // post-login paths
var session = optSession.get(); var user = sessions.extend(optSession.get()).user();
sessions.extend(session);
switch (path) { switch (path) {
case "/delete": case "/delete":
return deleteUser(ex, session); return deleteUser(ex, user);
case "/permission":
return editPermission(ex, user, true);
} }
return badRequest(ex, "%s not found".formatted(path)); return badRequest(ex, "%s not found".formatted(path));
} }
private boolean deleteUser(HttpExchange ex, Session session) throws IOException { private boolean deleteUser(HttpExchange ex, User user) throws IOException {
if (!user.hasPermission(MANAGE_USERS)) return sendEmptyResponse(HTTP_FORBIDDEN, ex);
var json = json(ex); var json = json(ex);
if (!json.has(USER_ID)) return badRequest(ex, "missing_user_id"); if (!json.has(USER_ID)) return badRequest(ex, "missing_user_id");
var uuid = json.getString(USER_ID); var uuid = json.getString(USER_ID);
if (uuid == null || uuid.isBlank()) return badRequest(ex, "missing_user_id"); if (uuid == null || uuid.isBlank()) return badRequest(ex, "missing_user_id");
if (session.user().uuid().equals(uuid)) return badRequest(ex, "must_not_delete_self"); if (user.uuid().equals(uuid)) return badRequest(ex, "must_not_delete_self");
if (!json.has(CONFIRMED) || !json.getBoolean(CONFIRMED)) return badRequest(ex, "missing_confirmation"); if (!json.has(CONFIRMED) || !json.getBoolean(CONFIRMED)) return badRequest(ex, "missing_confirmation");
Optional<User> targetUser = users.load(uuid); Optional<User> targetUser = users.load(uuid);
if (targetUser.isEmpty()) return badRequest(ex, "unknown_user"); if (targetUser.isEmpty()) return badRequest(ex, "unknown_user");
@ -111,22 +113,47 @@ public class UserController extends Controller {
// post-login paths // post-login paths
var session = optSession.get(); var session = optSession.get();
sessions.extend(session); sessions.extend(session);
var user = session.user();
switch (path) { switch (path) {
case "/": case "/":
return sendUserAndCookie(ex, session); return sendUserAndCookie(ex, session);
case "/add": case "/add":
return addUser(ex, session); return addUser(ex, user);
case "/list": case "/list":
return list(ex, session); return list(ex, user);
case "/password": case "/password":
return updatePassword(ex, session); return updatePassword(ex, user);
case "/permission":
return editPermission(ex, user, false);
case "/update": case "/update":
return updateUser(ex, session); return updateUser(ex, user);
} }
return notFound(ex); return notFound(ex);
} }
private boolean editPermission(HttpExchange ex, User user, boolean drop) throws IOException {
if (!user.hasPermission(MANAGE_USERS)) return sendEmptyResponse(HTTP_FORBIDDEN, ex);
var json = json(ex);
if (!json.has(USER_ID)) return badRequest(ex, "Missing user_id in request!");
if (!json.has(PERMISSION)) return badRequest(ex, "Missing permission in request");
try {
var permission = Permission.valueOf(json.getString(PERMISSION));
var userId = json.getString(USER_ID);
var optUer = users.load(userId);
if (optUer.isEmpty()) return badRequest(ex, "Unknown user id (%s)".formatted(userId));
user = optUer.get();
if (drop) {
user.drop(permission);
} else {
user.add(permission);
}
users.save(user);
return sendEmptyResponse(HTTP_OK, ex);
} catch (IllegalArgumentException iae) {
return badRequest(ex, iae.getMessage());
}
}
private boolean generateResetLink(HttpExchange ex) throws IOException { private boolean generateResetLink(HttpExchange ex) throws IOException {
var idOrEmail = queryParam(ex).get("user"); var idOrEmail = queryParam(ex).get("user");
@ -148,8 +175,7 @@ public class UserController extends Controller {
} }
private boolean list(HttpExchange ex, Session session) throws IOException { private boolean list(HttpExchange ex, User user) throws IOException {
var user = session.user();
if (!user.hasPermission(MANAGE_USERS)) return sendEmptyResponse(HTTP_FORBIDDEN, ex); if (!user.hasPermission(MANAGE_USERS)) return sendEmptyResponse(HTTP_FORBIDDEN, ex);
var json = new JSONObject(); var json = new JSONObject();
users.list().forEach(u -> json.put(u.uuid(), u.map(false))); users.list().forEach(u -> json.put(u.uuid(), u.map(false)));
@ -262,8 +288,7 @@ public class UserController extends Controller {
} }
} }
private boolean updatePassword(HttpExchange ex, Session session) throws IOException { private boolean updatePassword(HttpExchange ex, User user) throws IOException {
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())) {
@ -286,8 +311,7 @@ public class UserController extends Controller {
return sendContent(ex, user.map(false)); return sendContent(ex, user.map(false));
} }
private boolean updateUser(HttpExchange ex, Session session) throws IOException { private boolean updateUser(HttpExchange ex, User user) throws IOException {
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())) {

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

@ -68,3 +68,10 @@ function show(id){
var elem = get(id); var elem = get(id);
if (elem) elem.style.display = ''; if (elem) elem.style.display = '';
} }
function showAll(clazz){
var elems = document.getElementsByTagName('*'), i;
for (i in elems) {
if((' ' + elems[i].className + ' ').indexOf(' ' + clazz + ' ') > -1) elems[i].style.display = '';
}
}

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

@ -179,7 +179,6 @@ function durationUpdate(){
document.addEventListener("DOMContentLoaded", function(event) { // wait until page loaded document.addEventListener("DOMContentLoaded", function(event) { // wait until page loaded
alert('loaded settings.js');
fillForm(); fillForm();
fetch("/api/email/settings",{credentials:'include'}).then(handleSettings); fetch("/api/email/settings",{credentials:'include'}).then(handleSettings);
}); });

32
de.srsoftware.oidc.web/src/main/resources/en/scripts/users.js

@ -1,3 +1,15 @@
function editPermission(userId,permission,drop){
fetch(user_controller+"/permission",{
method : drop ? 'DELETE' : 'POST',
credentials:'include',
body : JSON.stringify({ user_id : userId, permission : permission }),
}).then(handleEdit);
}
function handleEdit(response){
redirect('users.html');
}
function addUser(){ function addUser(){
var pw = getValue('pw'); var pw = getValue('pw');
var pw2 = getValue('pw2'); var pw2 = getValue('pw2');
@ -31,15 +43,28 @@ function handleUsers(response){
for (let id in users){ for (let id in users){
var row = document.createElement("tr"); var row = document.createElement("tr");
var u = users[id]; var u = users[id];
var manage = {
clients : u.permissions.includes('MANAGE_CLIENTS'),
perms : u.permissions.includes('MANAGE_PERMISSIONS'),
smtp : u.permissions.includes('MANAGE_SMTP'),
users : u.permissions.includes('MANAGE_USERS')
};
row.innerHTML = `<td>${u.username}</td> row.innerHTML = `<td>${u.username}</td>
<td>${u.realname}</td> <td>${u.realname}</td>
<td>${u.email}</td> <td>${u.email}</td>
<td>${id}</td> <td>${id}</td>
<td style="display: none" class="permissions">
<button onclick="editPermission('${id}','MANAGE_CLIENTS',${manage.clients})">${manage.clients ?'-':'+'} Manage Clients</button>
<button onclick="editPermission('${id}','MANAGE_PERMISSIONS',${manage.perms})">${manage.perms ?'-':'+'} Manage Permissions</button>
<button onclick="editPermission('${id}','MANAGE_SMTP',${manage.smtp})">${manage.smtp ?'-':'+'} Manage SMTP</button>
<button onclick="editPermission('${id}','MANAGE_USERS',${manage.users})">${manage.users ?'-':'+'} Manage Users</button>
</td>
<td> <td>
<button type="button" onclick="reset_password('${id}')" id="reset-${id}">Reset password</button> <button type="button" onclick="reset_password('${id}')" id="reset-${id}">Reset password</button>
<button id="remove-${u.uuid}" class="danger" onclick="remove('${id}','${u.realname}')" type="button">Remove</button> <button id="remove-${u.uuid}" class="danger" onclick="remove('${id}','${u.realname}')" type="button">Remove</button>
</td>`; </td>`;
bottom.parentNode.insertBefore(row,bottom); bottom.parentNode.insertBefore(row,bottom);
if (user.permissions.includes('MANAGE_PERMISSIONS')) showAll('permissions');
} }
}); });
@ -59,7 +84,7 @@ function handleRemove(response){
function remove(userId,name){ function remove(userId,name){
disable(`remove-${userId}`); disable(`remove-${userId}`);
if (userId == user.uuid) { if (userId == user.uuid) {
//return; return;
} }
setText(`remove-${userId}`,"sent…"); setText(`remove-${userId}`,"sent…");
hideAll('error'); hideAll('error');
@ -77,4 +102,7 @@ function reset_password(userid){
fetch(user_controller+"/reset?user="+userid,{credentials:'include'}).then(() => { disable('reset-'+userid); }); fetch(user_controller+"/reset?user="+userid,{credentials:'include'}).then(() => { disable('reset-'+userid); });
} }
fetch(user_controller+"/list",{method:'POST',credentials:'include'}).then(handleUsers); document.addEventListener("DOMContentLoaded", function(event) { // wait until page loaded
fetch(user_controller+"/list",{method:'POST',credentials:'include'}).then(handleUsers);
});

1
de.srsoftware.oidc.web/src/main/resources/en/users.html

@ -20,6 +20,7 @@
<th>Display name</th> <th>Display name</th>
<th>Email</th> <th>Email</th>
<th>ID</th> <th>ID</th>
<th style="display: none" class="permissions">Permissions</th>
<th>Actions</th> <th>Actions</th>
</tr> </tr>
<tr id="bottom"> <tr id="bottom">

Loading…
Cancel
Save