diff --git a/frontend/src/App.svelte b/frontend/src/App.svelte index 00170ef..8ae9e23 100644 --- a/frontend/src/App.svelte +++ b/frontend/src/App.svelte @@ -17,11 +17,8 @@ function loadTheme(name){ if (!name) return; - console.log({theme:name}); const url = `${location.protocol}//${location.host.replace('5173','8080')}/css/${name}.css`; - fetch(url).then(resp => resp.text()).then(css => { - document.getElementById('usercss').innerText = css; - }); + fetch(url).then(resp => resp.text()).then(css => document.getElementById('usercss').innerText = css); } $effect(() => loadTheme(user.theme)); diff --git a/frontend/src/routes/user/List.svelte b/frontend/src/routes/user/List.svelte new file mode 100644 index 0000000..6392dc8 --- /dev/null +++ b/frontend/src/routes/user/List.svelte @@ -0,0 +1,44 @@ + + +
+ {t('user.list')} + + + + + + + + + + + + {#each users as u,i} + + + + + + + + {/each} + + +
{t('user.id')}{t('user.name')}{t('user.email')}{t('user.language')}{t('user.actions')}
{u.id}{u.name}{u.email}{u.language} + Check permissions, add button here +
+
\ No newline at end of file diff --git a/frontend/src/routes/user/User.svelte b/frontend/src/routes/user/User.svelte index d743db4..a37e11c 100644 --- a/frontend/src/routes/user/User.svelte +++ b/frontend/src/routes/user/User.svelte @@ -4,6 +4,7 @@ import ClickInput from '../../Components/ClickInput.svelte'; import ClickSelect from '../../Components/ClickSelect.svelte'; import EditPassword from './EditPassword.svelte'; + import UserList from './List.svelte'; let editPassword = false; async function patch(changeset){ @@ -92,9 +93,6 @@ {#if user.permissions.includes('LIST_USERS')} -
- {t('user.list')} - User list goes here… -
+ {/if} diff --git a/frontend/src/translations.svelte.js b/frontend/src/translations.svelte.js index f28b7dd..3652b1e 100644 --- a/frontend/src/translations.svelte.js +++ b/frontend/src/translations.svelte.js @@ -12,7 +12,7 @@ export function t(key,...args){ let keys = key.split('.'); for (let token of keys){ if (!set[token]){ - console.log('Missing translation for '+key); + console.warn('Missing translation for '+key); return keys[keys.length-1].replaceAll('_',' '); } set = set[token]; diff --git a/frontend/src/user.svelte.js b/frontend/src/user.svelte.js index 385aff8..c3844bb 100644 --- a/frontend/src/user.svelte.js +++ b/frontend/src/user.svelte.js @@ -4,7 +4,6 @@ export const user = $state({ }) export async function checkUser(){ - console.log('checkUser()'); const url = `${location.protocol}//${location.host.replace('5173','8080')}/api/user/whoami`; const response = await fetch(url,{ credentials: 'include' diff --git a/user/src/main/java/de/srsoftware/umbrella/user/UserModule.java b/user/src/main/java/de/srsoftware/umbrella/user/UserModule.java index 0b9e0de..cce888b 100644 --- a/user/src/main/java/de/srsoftware/umbrella/user/UserModule.java +++ b/user/src/main/java/de/srsoftware/umbrella/user/UserModule.java @@ -3,13 +3,14 @@ package de.srsoftware.umbrella.user; import static de.srsoftware.tools.Optionals.*; import static de.srsoftware.umbrella.core.Constants.*; +import static de.srsoftware.umbrella.core.Paths.LIST; import static de.srsoftware.umbrella.core.Paths.LOGOUT; import static de.srsoftware.umbrella.core.ResponseCode.*; import static de.srsoftware.umbrella.user.Constants.*; import static de.srsoftware.umbrella.user.Paths.LOGIN; import static de.srsoftware.umbrella.user.Paths.WHOAMI; +import static de.srsoftware.umbrella.user.model.DbUser.PERMISSION.LIST_USERS; import static de.srsoftware.umbrella.user.model.DbUser.PERMISSION.UPDATE_USERS; -import static java.lang.System.Logger.Level.DEBUG; import static java.lang.System.Logger.Level.WARNING; import static java.time.temporal.ChronoUnit.DAYS; @@ -24,9 +25,9 @@ import java.io.IOException; import java.security.NoSuchAlgorithmException; import java.time.Instant; import java.util.List; -import java.util.Set; +import java.util.Optional; + import org.json.JSONObject; -import org.sqlite.core.DB; public class UserModule extends PathHandler { @@ -63,12 +64,20 @@ public class UserModule extends PathHandler { @Override public boolean doGet(Path path, HttpExchange ex) throws IOException { - var p = path.toString(); - switch (p){ - case LOGOUT: return logout(ex); - case WHOAMI: return getUser(ex); + UmbrellaUser user = null; + var sessionToken = SessionToken.from(ex).map(Token::of); + if (sessionToken.isPresent()) try { + user = users.load(users.load(sessionToken.get())); + } catch (UmbrellaException e) { + LOG.log(WARNING,e); } - return super.doGet(path,ex); + addCors(ex); + return switch (path.toString()) { + case LIST -> getUserList(ex, user); + case LOGOUT -> logout(ex, sessionToken); + case WHOAMI -> getUser(ex, user); + default -> super.doGet(path, ex); + }; } @Override @@ -127,6 +136,19 @@ public class UserModule extends PathHandler { } } + private boolean getUserList(HttpExchange ex, UmbrellaUser user) throws IOException { + + if (user instanceof DbUser dbUser && dbUser.permissions().contains(LIST_USERS)){ + try { + var list = users.list(0, null).stream().map(UmbrellaUser::toMap).toList(); + return sendContent(ex,list); + } catch (UmbrellaException e) { + return sendContent(ex,e.statusCode(),e.getMessage()); + } + } + return sendContent(ex,FORBIDDEN,"You are not allowed to list users!"); + } + private boolean patchPassword(HttpExchange ex, UmbrellaUser requestingUser) throws IOException { if (!(requestingUser instanceof DbUser user)) return sendContent(ex,SERVER_ERROR,"DbUser expected"); JSONObject json; @@ -160,22 +182,12 @@ public class UserModule extends PathHandler { return super.doPost(path, ex); } - private boolean getUser(HttpExchange ex) throws IOException { - addCors(ex); - var sessionToken = SessionToken.from(ex); - if (sessionToken.isEmpty()) return sendEmptyResponse(UNAUTHORIZED,ex); - try { - Session session = users.load(Token.of(sessionToken.get())); - UmbrellaUser user = users.load(session); - return sendContent(ex,OK,user); - } catch (UmbrellaException e) { - return sendContent(ex,e.statusCode(),e.getMessage()); - } + private boolean getUser(HttpExchange ex, UmbrellaUser user) throws IOException { + if (user != null) return sendContent(ex,OK,user); + return sendEmptyResponse(UNAUTHORIZED,ex); } - public boolean logout(HttpExchange ex) throws IOException { - addCors(ex); - var optToken = SessionToken.from(ex).map(Token::of); + public boolean logout(HttpExchange ex, Optional optToken) throws IOException { if (optToken.isPresent()){ var token = optToken.get(); try { diff --git a/web/src/main/resources/web/css/winter.css b/web/src/main/resources/web/css/winter.css index 42f3553..cf43906 100644 --- a/web/src/main/resources/web/css/winter.css +++ b/web/src/main/resources/web/css/winter.css @@ -35,9 +35,14 @@ button{ border-color: darkgray black black darkgray; } footer { - position: absolute; + position: sticky; bottom: 0; width: 100%; text-align: center; - margin: 5px; + padding: 5px; + margin-top: 5px; + background: white; + border-color: cyan; + border-style: dashed; + border-width: 1px 0 0; } \ No newline at end of file