Browse Source

working on login service management

feature/document
Stephan Richter 4 months ago
parent
commit
38081894ef
  1. 2
      frontend/src/App.svelte
  2. 79
      frontend/src/routes/user/EditService.svelte
  3. 3
      frontend/src/routes/user/LoginServices.svelte
  4. 9
      translations/src/main/resources/de.json
  5. 33
      user/src/main/java/de/srsoftware/umbrella/user/UserModule.java
  6. 7
      user/src/main/java/de/srsoftware/umbrella/user/model/LoginService.java

2
frontend/src/App.svelte

@ -3,6 +3,7 @@ @@ -3,6 +3,7 @@
import { loadTranslation } from './translations.svelte.js';
import { user } from './user.svelte.js';
import { Router, Route } from 'svelte-tiny-router';
import EditService from "./routes/user/EditService.svelte";
import Footer from "./Components/Footer.svelte";
import Login from "./Components/Login.svelte";
import Menu from "./Components/Menu.svelte";
@ -31,6 +32,7 @@ @@ -31,6 +32,7 @@
<Menu />
<Route path="/user" component={User} />
<Route path="/user/:user_id/edit" component={UserEdit} />
<Route path="/user/service/:serviceName" component={EditService} />
<Route>
<p>Page not found</p>
</Route>

79
frontend/src/routes/user/EditService.svelte

@ -0,0 +1,79 @@ @@ -0,0 +1,79 @@
<script>
import { onMount } from 'svelte';
import { t } from '../../translations.svelte.js';
import { useTinyRouter } from 'svelte-tiny-router';
let { serviceName } = $props();
let service = $state({})
let caption = $state(t('user.save_service'));
let message = $state(t('user.loading_data'));
let router = useTinyRouter();
let disabled = $state(false);
onMount(async () => {
const url = `${location.protocol}//${location.host.replace('5173','8080')}/api/user/service/${serviceName}`;
const resp = await fetch(url,{credentials:'include'});
if (resp.ok){
const json = await resp.json();
for (let key of Object.keys(json)) service[key] = json[key];
} else {
message = await resp.text();
if (!message) message = t(resp);
}
});
async function update(){
caption = t('user.data_sent');
const url = `${location.protocol}//${location.host.replace('5173','8080')}/api/user/service/${serviceName}`;
const resp = await fetch(url,{
credentials: 'include',
method: 'PATCH',
body: JSON.stringify(service)
});
if (resp.ok){
caption = t('user.saved');
router.navigate('/user');
} else {
caption = await resp.text();
if (!caption) caption = t(resp);
}
}
</script>
<fieldset>
<legend>{t('user.edit_service',serviceName)}</legend>
{#if service.name}
<table>
<tbody>
<tr>
<th>{t('user.name')}</th>
<td>
<input type="text" bind:value={service.name} />
</td>
</tr>
<tr>
<th>{t('user.client_id')}</th>
<td>
<input type="text" bind:value={service.client_id} />
</td>
</tr>
<tr>
<th>{t('user.client_secret')}</th>
<td>
<input type="text" bind:value={service.client_secret} />
</td>
</tr>
<tr>
<th>{t('user.base_url')}</th>
<td>
<input type="text" bind:value={service.url} />
</td>
</tr>
</tbody>
</table>
<button onclick={update} {disabled}>{caption}</button>
<button onclick={() => router.navigate('/user')} {disabled}>{t('user.abort')}</button>
{:else}
{message}
{/if}
</fieldset>

3
frontend/src/routes/user/LoginServices.svelte

@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
for (let service of json) services.push(service);
}
});
</script>
<fieldset tabindex="0">
@ -34,7 +35,7 @@ @@ -34,7 +35,7 @@
<td>
<button>{t('user.connect_service')}</button>
{#if user.permissions.includes('MANAGE_LOGIN_SERVICES')}
<button>{t('user.edit')}</button>
<button onclick={() => router.navigate(`/user/service/${service}`)}>{t('user.edit')}</button>
<button>{t('user.delete')}</button>
{/if}
</td>

9
translations/src/main/resources/de.json

@ -18,12 +18,16 @@ @@ -18,12 +18,16 @@
"tutorial": "Tutorial"
},
"status" : {
"403": "Zugriff verweigert"
"403": "Zugriff verweigert",
"501": "Nicht implementiert"
},
"user" : {
"actions": "Aktionen",
"abort": "abbrechen",
"add_login_service": "Login-Service anlegen",
"base_url": "Basis-URL",
"client_id": "Client-ID",
"client_secret": "Client-Geheimnis",
"connect_service": "mit Service verbinden",
"CREATE_USERS": "NUTZER ANLEGEN",
"data_sent": "Daten übermittelt",
@ -32,6 +36,7 @@ @@ -32,6 +36,7 @@
"edit": "Bearbeiten",
"editing": "Nutzer {0} bearbeiten",
"edit_password": "Passwort ändern",
"edit_service": "Login-Service \"{0}\" bearbeiten",
"email": "E-Mail",
"failed": "fehlgeschlagen",
"id": "Id",
@ -42,6 +47,7 @@ @@ -42,6 +47,7 @@
"LIST_USERS": "NUTZER AUFLISTEN",
"loading_data": "Daten werden geladen…",
"login": "Login",
"login_services": "Login-Services",
"MANAGE_LOGIN_SERVICES": "LOGIN-SERVICES VERWALTEN",
"mismatch": "ungleich",
"must_not_be_empty": "darf nicht leer sein",
@ -52,6 +58,7 @@ @@ -52,6 +58,7 @@
"permissions": "Berechtigungen",
"repeat_new_password": "Wiederholung",
"saved": "gespeichert",
"save_service": "Service speichern",
"save_user": "Nutzer speichern",
"service": "Service",
"theme": "Design",

33
user/src/main/java/de/srsoftware/umbrella/user/UserModule.java

@ -6,6 +6,7 @@ import static de.srsoftware.umbrella.core.Constants.*; @@ -6,6 +6,7 @@ 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.core.ResponseCode.HTTP_NOT_IMPLEMENTED;
import static de.srsoftware.umbrella.core.ResponseCode.HTTP_SERVER_ERROR;
import static de.srsoftware.umbrella.user.Constants.*;
import static de.srsoftware.umbrella.user.Paths.*;
@ -14,12 +15,14 @@ import static de.srsoftware.umbrella.user.model.DbUser.PERMISSION; @@ -14,12 +15,14 @@ import static de.srsoftware.umbrella.user.model.DbUser.PERMISSION;
import static de.srsoftware.umbrella.user.model.DbUser.PERMISSION.*;
import static java.lang.System.Logger.Level.WARNING;
import static java.net.HttpURLConnection.*;
import static java.text.MessageFormat.format;
import static java.time.temporal.ChronoUnit.DAYS;
import com.sun.net.httpserver.HttpExchange;
import de.srsoftware.tools.Path;
import de.srsoftware.tools.PathHandler;
import de.srsoftware.tools.SessionToken;
import de.srsoftware.umbrella.core.ResponseCode;
import de.srsoftware.umbrella.core.UmbrellaException;
import de.srsoftware.umbrella.user.api.LoginServiceDb;
import de.srsoftware.umbrella.user.api.UserDb;
@ -121,6 +124,7 @@ public class UserModule extends PathHandler { @@ -121,6 +124,7 @@ public class UserModule extends PathHandler {
try {
if (head == null || head.isBlank()) return sendContent(ex, HTTP_UNPROCESSABLE,"User id missing!");
if (PASSWORD.equals(head)) return patchPassword(ex,requestingUser);
if (SERVICE.equals(head)) return patchService(ex,path.pop(),requestingUser);
userId = Long.parseLong(head);
} catch (NumberFormatException e) {
return sendContent(ex, HTTP_UNPROCESSABLE,"Invalid user id: "+head);
@ -174,10 +178,22 @@ public class UserModule extends PathHandler { @@ -174,10 +178,22 @@ public class UserModule extends PathHandler {
return switch (head){
case BUTTONS -> getOidcButtons(ex);
case LIST -> getServiceList(ex,user);
case null, default -> super.doGet(path,ex);
case null -> super.doGet(path,ex);
default -> getService(ex,user,head);
};
}
private boolean getService(HttpExchange ex, UmbrellaUser user, String serviceId) throws IOException {
if (!(user instanceof DbUser dbUser && dbUser.permissions().contains(MANAGE_LOGIN_SERVICES))) return sendEmptyResponse(HTTP_FORBIDDEN,ex);
try {
return sendContent(ex,logins.loadLoginService(serviceId).toMap());
} catch (UmbrellaException e) {
return sendContent(ex,e.statusCode(),e.getMessage());
} catch (IOException e) {
return sendContent(ex,HTTP_SERVER_ERROR,e.getMessage());
}
}
private boolean getOidcButtons(HttpExchange ex) throws IOException {
try {
var services = logins.listLoginServices().stream().map(LoginService::name);
@ -268,6 +284,21 @@ public class UserModule extends PathHandler { @@ -268,6 +284,21 @@ public class UserModule extends PathHandler {
}
}
private boolean patchService(HttpExchange ex, String serviceName, UmbrellaUser requestingUser) throws IOException {
if (!(requestingUser instanceof DbUser user && user.permissions().contains(MANAGE_LOGIN_SERVICES))) return sendEmptyResponse(HTTP_FORBIDDEN,ex);
try {
var json = json(ex);
if (!json.has(NAME) || !(json.get(NAME) instanceof String name) || name.isBlank()) return sendContent(ex,HTTP_UNPROCESSABLE,format(ERROR_MISSING_FIELD,NAME));
if (!json.has(URL) || !(json.get(URL) instanceof String url) || url.isBlank()) return sendContent(ex,HTTP_UNPROCESSABLE,format(ERROR_MISSING_FIELD,URL));
if (!json.has(CLIENT_ID) || !(json.get(CLIENT_ID) instanceof String clientId) || clientId.isBlank()) return sendContent(ex,HTTP_UNPROCESSABLE,format(ERROR_MISSING_FIELD,CLIENT_ID));
if (!json.has(CLIENT_SECRET) || !(json.get(CLIENT_SECRET) instanceof String secret) || secret.isBlank()) return sendContent(ex,HTTP_UNPROCESSABLE,format(ERROR_MISSING_FIELD,CLIENT_SECRET));
var service = logins.save(new LoginService(name,url,clientId,secret, DEFAULT_FIELD));
return sendContent(ex,service.toMap());
} catch (UmbrellaException e) {
return sendContent(ex,e.statusCode(),e.getMessage());
}
}
private boolean postLogin(HttpExchange ex) throws IOException {
var json = json(ex);
if (!(json.has(USERNAME) && json.get(USERNAME) instanceof String username)) return sendContent(ex, HTTP_UNPROCESSABLE,"Username missing");

7
user/src/main/java/de/srsoftware/umbrella/user/model/LoginService.java

@ -5,6 +5,7 @@ package de.srsoftware.umbrella.user.model; @@ -5,6 +5,7 @@ package de.srsoftware.umbrella.user.model;
import static de.srsoftware.tools.Strings.base64;
import static de.srsoftware.umbrella.core.Constants.NAME;
import static de.srsoftware.umbrella.core.Constants.URL;
import static de.srsoftware.umbrella.user.Constants.*;
import static java.nio.charset.StandardCharsets.UTF_8;
import de.srsoftware.tools.Mappable;
@ -18,9 +19,9 @@ public record LoginService(String name, String url, String clientId, String clie @@ -18,9 +19,9 @@ public record LoginService(String name, String url, String clientId, String clie
var map = new HashMap<String,Object>();
map.put(NAME,name);
map.put(URL,url);
map.put("clientId",clientId);
map.put("clientSecret",clientSecret);
map.put("userInfoField",userInfoField);
map.put(CLIENT_ID,clientId);
map.put(CLIENT_SECRET,clientSecret);
map.put(USER_INFO_FIELD,userInfoField);
return map;
}

Loading…
Cancel
Save