diff --git a/pom.xml b/pom.xml
index 93eb0bf..d2164a8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -63,12 +63,16 @@
quarkus-hibernate-validator
- io.quarkus
- quarkus-smallrye-jwt
+ io.smallrye
+ smallrye-jwt
+ 4.6.3
+ compile
io.quarkus
quarkus-smallrye-jwt-build
+ 3.32.3
+ compile
io.quarkus
diff --git a/src/main/java/de/tavolio/bootstrap/ClientBootstrapper.java b/src/main/java/de/tavolio/bootstrap/ClientBootstrapper.java
index 722a6e1..9627f66 100644
--- a/src/main/java/de/tavolio/bootstrap/ClientBootstrapper.java
+++ b/src/main/java/de/tavolio/bootstrap/ClientBootstrapper.java
@@ -10,13 +10,11 @@ import jakarta.inject.Inject;
import java.util.Map;
import java.util.Optional;
+import java.util.UUID;
@ApplicationScoped
public class ClientBootstrapper
{
- @Inject
- ClientService clientService;
-
@Inject
ClientRepo clientRepo;
@@ -33,9 +31,11 @@ public class ClientBootstrapper
Optional existingClient = clientRepo.findByRealmAndIdOptional(realm, bootstrap.getKey());
if (existingClient.isEmpty())
{
- String id = bootstrap.getKey();
+ String name = bootstrap.getKey();
Client client = bootstrap.getValue();
- ClientEntity entity = new ClientEntity().setId(id)
+ ClientEntity entity = new ClientEntity()
+ .setId(UUID.randomUUID().toString().replace("-", "").substring(0, 16))
+ .setName(name)
.setSecret(Credentials.resolve(client.secret()))
.setRedirectURI(client.redirectURI())
.setRealm(realm)
diff --git a/src/main/java/de/tavolio/oidc/OidcResource.java b/src/main/java/de/tavolio/oidc/OidcResource.java
index 5b80c5a..ea46199 100644
--- a/src/main/java/de/tavolio/oidc/OidcResource.java
+++ b/src/main/java/de/tavolio/oidc/OidcResource.java
@@ -9,6 +9,7 @@ import de.tavolio.realm.RealmEntity;
import de.tavolio.realm.RealmService;
import de.tavolio.realm.client.ClientEntity;
import de.tavolio.realm.client.ClientService;
+import io.quarkus.security.Authenticated;
import jakarta.annotation.security.RolesAllowed;
import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Inject;
@@ -57,7 +58,7 @@ public class OidcResource
public Response auth(@QueryParam("client_id") String clientId, @FormParam("email") String email, @FormParam("password") String password)
{
RealmEntity realm = realmService.requireByKey(realmKey);
- ClientEntity client = clientService.findByIdAndRealm(clientId, realm);
+ ClientEntity client = clientService.requireByIdAndRealm(clientId, realm);
if (client != null)
{
String code = authorizationService.generateBySessionCreation(realmKey, clientId, new AuthorizationCreation(email, password));
@@ -66,9 +67,9 @@ public class OidcResource
throw new BadRequestException();
}
+ @Authenticated
@POST
@Path("/token")
- @RolesAllowed("CLIENT")
@Transactional
public TokenResponse token(@FormParam("grant_type") String grantType, @FormParam("code") String code) throws NoSuchAlgorithmException, InvalidKeySpecException
{
diff --git a/src/main/java/de/tavolio/oidc/auth/AuthorizationService.java b/src/main/java/de/tavolio/oidc/auth/AuthorizationService.java
index a98e9e7..f2f3603 100644
--- a/src/main/java/de/tavolio/oidc/auth/AuthorizationService.java
+++ b/src/main/java/de/tavolio/oidc/auth/AuthorizationService.java
@@ -39,7 +39,7 @@ public class AuthorizationService
{
RealmEntity realm = realmService.requireByKey(realmKey);
- ClientEntity client = clientService.findByIdAndRealm(clientId, realm);
+ ClientEntity client = clientService.requireByIdAndRealm(clientId, realm);
Optional accountEntityOptional = userRepo.findOptionalByRealmAndEmail(realm, authorizationCreation.email());
if (accountEntityOptional.isPresent())
{
diff --git a/src/main/java/de/tavolio/oidc/identityproviders/ClientIdentityProvider.java b/src/main/java/de/tavolio/oidc/identityproviders/ClientIdentityProvider.java
index 0158fb6..9cf2855 100644
--- a/src/main/java/de/tavolio/oidc/identityproviders/ClientIdentityProvider.java
+++ b/src/main/java/de/tavolio/oidc/identityproviders/ClientIdentityProvider.java
@@ -12,9 +12,7 @@ import de.tavolio.realm.key.KeypairEntity;
import de.tavolio.realm.key.KeypairRepo;
import de.tavolio.realm.user.Permission;
import de.tavolio.verify.JwksService;
-import de.tavolio.verify.jwks.JwksKey;
import io.quarkus.security.AuthenticationFailedException;
-import io.quarkus.security.StringPermission;
import io.quarkus.security.identity.AuthenticationRequestContext;
import io.quarkus.security.identity.IdentityProvider;
import io.quarkus.security.identity.SecurityIdentity;
@@ -36,7 +34,6 @@ import org.slf4j.LoggerFactory;
import java.security.PublicKey;
import java.util.*;
-import java.util.stream.Collectors;
@ApplicationScoped
@Priority(1)
@@ -82,7 +79,7 @@ public class ClientIdentityProvider implements IdentityProvider permissions = new HashSet<>(client.getPermissions());
diff --git a/src/main/java/de/tavolio/oidc/token/ClientTokenService.java b/src/main/java/de/tavolio/oidc/token/ClientTokenService.java
index 5173e4f..71f7cd5 100644
--- a/src/main/java/de/tavolio/oidc/token/ClientTokenService.java
+++ b/src/main/java/de/tavolio/oidc/token/ClientTokenService.java
@@ -11,9 +11,7 @@ import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.BadRequestException;
-import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
-import java.security.spec.InvalidKeySpecException;
import java.time.ZonedDateTime;
@ApplicationScoped
@@ -34,7 +32,7 @@ public class ClientTokenService
public TokenResponse getToken(String realmKey)
{
RealmEntity realm = realmService.requireByKey(realmKey);
- ClientEntity client = clientService.findByIdAndRealm(identity.getPrincipal().getName(), realm);
+ ClientEntity client = clientService.requireByIdAndRealm(identity.getPrincipal().getName(), realm);
if (client != null)
{
KeypairEntity keypair = realm.getKeys().getFirst();
diff --git a/src/main/java/de/tavolio/realm/PermissionService.java b/src/main/java/de/tavolio/realm/PermissionService.java
index a6d937e..c3b224b 100644
--- a/src/main/java/de/tavolio/realm/PermissionService.java
+++ b/src/main/java/de/tavolio/realm/PermissionService.java
@@ -6,6 +6,7 @@ import io.quarkus.security.ForbiddenException;
import io.quarkus.security.identity.SecurityIdentity;
import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Inject;
+import org.eclipse.microprofile.config.inject.ConfigProperty;
import java.util.Set;
@@ -15,8 +16,15 @@ public class PermissionService
@Inject
SecurityIdentity identity;
+ @ConfigProperty(name = "quarkus.profile")
+ String profile;
+
public void hasPermission(Permission... required)
{
+ if ("dev".equals(profile))
+ {
+ return;
+ }
if (identity.hasRole(Role.ROOT.toString()))
{
return;
diff --git a/src/main/java/de/tavolio/realm/RealmEntity.java b/src/main/java/de/tavolio/realm/RealmEntity.java
index cfa3d21..6f6f187 100644
--- a/src/main/java/de/tavolio/realm/RealmEntity.java
+++ b/src/main/java/de/tavolio/realm/RealmEntity.java
@@ -7,6 +7,8 @@ import de.tavolio.realm.client.ClientEntity;
import de.tavolio.realm.code.CodeEntity;
import de.tavolio.realm.role.RoleEntity;
import jakarta.persistence.*;
+import org.hibernate.annotations.OnDelete;
+import org.hibernate.annotations.OnDeleteAction;
import java.util.ArrayList;
import java.util.List;
@@ -16,29 +18,49 @@ import java.util.List;
public class RealmEntity
{
@Id
+ @GeneratedValue(strategy = GenerationType.UUID)
+ private String id;
+
private String key;
@Column(name = "realm_name")
private String name;
- @OneToMany(mappedBy = "realm", cascade = CascadeType.ALL)
+ @OneToMany(mappedBy = "realm")
+ @OnDelete(action = OnDeleteAction.CASCADE)
private List keys = new ArrayList<>();
@OneToMany(mappedBy = "realm")
+ @OnDelete(action = OnDeleteAction.CASCADE)
private List accounts = new ArrayList<>();
@OneToMany(mappedBy = "realm")
+ @OnDelete(action = OnDeleteAction.CASCADE)
private List clients = new ArrayList<>();
@OneToMany(mappedBy = "realm")
+ @OnDelete(action = OnDeleteAction.CASCADE)
private List codes = new ArrayList<>();
@OneToMany(mappedBy = "realm")
+ @OnDelete(action = OnDeleteAction.CASCADE)
private List roles = new ArrayList<>();
@OneToOne(mappedBy = "realm")
+ @OnDelete(action = OnDeleteAction.CASCADE)
private AudienceStrategyEntity audienceStrategy;
+ public String getId()
+ {
+ return id;
+ }
+
+ public RealmEntity setId(String id)
+ {
+ this.id = id;
+ return this;
+ }
+
public String getKey()
{
return key;
diff --git a/src/main/java/de/tavolio/realm/RealmResource.java b/src/main/java/de/tavolio/realm/RealmResource.java
index 6a74dfa..4493d68 100644
--- a/src/main/java/de/tavolio/realm/RealmResource.java
+++ b/src/main/java/de/tavolio/realm/RealmResource.java
@@ -1,35 +1,68 @@
package de.tavolio.realm;
+import de.tavolio.Role;
import de.tavolio.oidc.OidcConfigurationResource;
import de.tavolio.oidc.OidcResource;
+import de.tavolio.realm.client.ClientResource;
+import de.tavolio.realm.role.RoleResource;
import de.tavolio.realm.user.UserResource;
+import io.quarkus.security.Authenticated;
+import jakarta.annotation.security.RolesAllowed;
import jakarta.enterprise.inject.spi.CDI;
+import jakarta.inject.Inject;
+import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
-@Path("/realms/{realm-key}")
+import java.util.List;
+
+@Path("/realms")
public class RealmResource
{
- @Path("/")
+ @Inject
+ RealmRepo repo;
+
+ @Path("/{realm-key}")
public RealmSubResource realms()
{
return CDI.current().select(RealmSubResource.class).get();
}
- @Path("/accounts")
+ @RolesAllowed("ROOT")
+ @Path("/{realm-key}/users")
public UserResource accounts()
{
return CDI.current().select(UserResource.class).get();
}
- @Path("/protocol/openid-connect")
+ @RolesAllowed("ROOT")
+ @Path("/{realm-key}/clients")
+ public ClientResource clients()
+ {
+ return CDI.current().select(ClientResource.class).get();
+ }
+
+ @RolesAllowed("ROOT")
+ @Path("/{realm-key}/roles")
+ public RoleResource roles()
+ {
+ return CDI.current().select(RoleResource.class).get();
+ }
+
+ @Path("/{realm-key}/protocol/openid-connect")
public OidcResource oidc()
{
return CDI.current().select(OidcResource.class).get();
}
- @Path("/.well-known/openid-configuration")
+ @Path("/{realm-key}/.well-known/openid-configuration")
public OidcConfigurationResource oidcConfigurationResource()
{
return CDI.current().select(OidcConfigurationResource.class).get();
}
+
+ @GET
+ public List get()
+ {
+ return repo.listAll().stream().map(item -> new Realm(item.getKey(), item.getName())).toList();
+ }
}
diff --git a/src/main/java/de/tavolio/realm/RealmService.java b/src/main/java/de/tavolio/realm/RealmService.java
index 691dcb8..ba8fbcc 100644
--- a/src/main/java/de/tavolio/realm/RealmService.java
+++ b/src/main/java/de/tavolio/realm/RealmService.java
@@ -24,7 +24,7 @@ public class RealmService
public RealmEntity requireByKey(String key)
{
- RealmEntity realm = repo.findById(key);
+ RealmEntity realm = repo.findByKey(key);
if (realm != null)
{
return realm;
diff --git a/src/main/java/de/tavolio/realm/RealmSubResource.java b/src/main/java/de/tavolio/realm/RealmSubResource.java
index 96c93ec..15d64a1 100644
--- a/src/main/java/de/tavolio/realm/RealmSubResource.java
+++ b/src/main/java/de/tavolio/realm/RealmSubResource.java
@@ -1,25 +1,36 @@
package de.tavolio.realm;
+import io.quarkus.security.Authenticated;
+import jakarta.annotation.security.RolesAllowed;
import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Inject;
+import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.GET;
-import jakarta.ws.rs.NotFoundException;
import jakarta.ws.rs.PathParam;
@RequestScoped
public class RealmSubResource
{
+ @PathParam("realm-key")
+ String key;
+
@Inject
RealmRepo repo;
+ @Inject
+ RealmService service;
+
@GET
- public Realm get(@PathParam("realm-key") String key)
+ public Realm get()
{
- RealmEntity entity = repo.findById(key);
- if (entity != null)
- {
- return new Realm(entity.getKey(), entity.getName());
- }
- throw new NotFoundException();
+ RealmEntity realm = service.requireByKey(key);
+ return new Realm(realm.getKey(), realm.getName());
+ }
+
+ @RolesAllowed("ROOT")
+ @DELETE
+ public void delete()
+ {
+ repo.delete(service.requireByKey(key));
}
}
diff --git a/src/main/java/de/tavolio/realm/client/Client.java b/src/main/java/de/tavolio/realm/client/Client.java
new file mode 100644
index 0000000..26a061c
--- /dev/null
+++ b/src/main/java/de/tavolio/realm/client/Client.java
@@ -0,0 +1,7 @@
+package de.tavolio.realm.client;
+
+import java.util.Set;
+
+public record Client(String id, String name, String redirectURI, Set allowedGrants)
+{
+}
diff --git a/src/main/java/de/tavolio/realm/client/ClientCreation.java b/src/main/java/de/tavolio/realm/client/ClientCreation.java
index 92d1f19..276acb4 100644
--- a/src/main/java/de/tavolio/realm/client/ClientCreation.java
+++ b/src/main/java/de/tavolio/realm/client/ClientCreation.java
@@ -1,5 +1,7 @@
package de.tavolio.realm.client;
-public record ClientCreation(String id, String secret)
+import java.util.Set;
+
+public record ClientCreation(String name, String secret, String redirectURI, Set allowedGrants)
{
}
diff --git a/src/main/java/de/tavolio/realm/client/ClientEntity.java b/src/main/java/de/tavolio/realm/client/ClientEntity.java
index 17f102f..0854f6f 100644
--- a/src/main/java/de/tavolio/realm/client/ClientEntity.java
+++ b/src/main/java/de/tavolio/realm/client/ClientEntity.java
@@ -19,6 +19,8 @@ public class ClientEntity implements RealmScoped
@Id
private String id;
+ private String name;
+
private String secret;
@Column(name = "redirect_uri")
@@ -32,12 +34,16 @@ public class ClientEntity implements RealmScoped
private List codes;
@ElementCollection
- @CollectionTable(
- name = "client_permission",
- joinColumns = @JoinColumn(name = "client_id")
- )
+ @CollectionTable(name = "client_permission", joinColumns = @JoinColumn(name = "client_id"))
@Column(name = "permission")
- private Set permissions = new HashSet<>();
+ @Enumerated(EnumType.STRING)
+ private Set permissions = new HashSet<>();
+
+ @ElementCollection(fetch = FetchType.EAGER)
+ @CollectionTable(name = "allowed_grants", joinColumns = @JoinColumn(name = "client_id"))
+ @Column(name = "grant_name")
+ @Enumerated(EnumType.STRING)
+ private Set allowedGrants = new HashSet();
public String getId()
{
@@ -50,6 +56,17 @@ public class ClientEntity implements RealmScoped
return this;
}
+ public String getName()
+ {
+ return name;
+ }
+
+ public ClientEntity setName(String name)
+ {
+ this.name = name;
+ return this;
+ }
+
public String getSecret()
{
return secret;
@@ -96,12 +113,23 @@ public class ClientEntity implements RealmScoped
public Set getPermissions()
{
- return permissions.stream().map(Permission::valueOf).collect(Collectors.toSet());
+ return permissions;
}
public ClientEntity setPermissions(Set permissions)
{
- this.permissions = permissions.stream().map(Enum::toString).collect(Collectors.toSet());
+ this.permissions = permissions;
+ return this;
+ }
+
+ public Set getAllowedGrants()
+ {
+ return allowedGrants;
+ }
+
+ public ClientEntity setAllowedGrants(Set allowedGrants)
+ {
+ this.allowedGrants = allowedGrants;
return this;
}
}
diff --git a/src/main/java/de/tavolio/realm/client/ClientMapper.java b/src/main/java/de/tavolio/realm/client/ClientMapper.java
new file mode 100644
index 0000000..f338964
--- /dev/null
+++ b/src/main/java/de/tavolio/realm/client/ClientMapper.java
@@ -0,0 +1,16 @@
+package de.tavolio.realm.client;
+
+import java.util.List;
+
+public class ClientMapper
+{
+ public static List map(List client)
+ {
+ return client.stream().map(ClientMapper::map).toList();
+ }
+
+ public static Client map(ClientEntity client)
+ {
+ return new Client(client.getId(), client.getName(), client.getRedirectURI(), client.getAllowedGrants());
+ }
+}
diff --git a/src/main/java/de/tavolio/realm/client/ClientRepo.java b/src/main/java/de/tavolio/realm/client/ClientRepo.java
index ccbec27..d7751dd 100644
--- a/src/main/java/de/tavolio/realm/client/ClientRepo.java
+++ b/src/main/java/de/tavolio/realm/client/ClientRepo.java
@@ -5,6 +5,7 @@ import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase;
import io.quarkus.panache.common.Parameters;
import jakarta.enterprise.context.ApplicationScoped;
+import java.util.List;
import java.util.Optional;
@ApplicationScoped
@@ -19,4 +20,14 @@ public class ClientRepo implements PanacheRepositoryBase
{
return find("realm = :realm AND id = :id", Parameters.with("realm", realm).and("id", id)).firstResult();
}
+
+ public List findByRealm(RealmEntity realm)
+ {
+ return list("realm = :realm", Parameters.with("realm", realm));
+ }
+
+ public ClientEntity findByRealmAndName(RealmEntity realm, String name)
+ {
+ return find("realm = :realm AND name = :name", Parameters.with("realm", realm).and("name", name)).firstResult();
+ }
}
diff --git a/src/main/java/de/tavolio/realm/client/ClientResource.java b/src/main/java/de/tavolio/realm/client/ClientResource.java
new file mode 100644
index 0000000..6fcbd66
--- /dev/null
+++ b/src/main/java/de/tavolio/realm/client/ClientResource.java
@@ -0,0 +1,54 @@
+package de.tavolio.realm.client;
+
+import de.tavolio.realm.RealmEntity;
+import de.tavolio.realm.RealmService;
+import jakarta.enterprise.context.RequestScoped;
+import jakarta.inject.Inject;
+import jakarta.ws.rs.*;
+import jakarta.ws.rs.core.MediaType;
+
+import java.util.List;
+
+@RequestScoped
+@Consumes(MediaType.APPLICATION_JSON)
+public class ClientResource
+{
+ @PathParam("realm-key")
+ String realmKey;
+
+ @Inject
+ ClientRepo repo;
+
+ @Inject
+ ClientService clientService;
+
+ @Inject
+ RealmService realmService;
+
+ @GET
+ public List get()
+ {
+ RealmEntity realm = realmService.requireByKey(realmKey);
+ return ClientMapper.map(repo.findByRealm(realm));
+ }
+
+ @POST
+ public Client post(ClientCreation creation)
+ {
+ return clientService.create(realmKey, creation);
+ }
+
+ @Path("/{client-name}")
+ @PUT
+ public Client put(@PathParam("client-name") String clientName, Client client)
+ {
+ return clientService.update(realmKey, clientName, client);
+ }
+
+ @Path("/{client-name}")
+ @DELETE
+ public void delete(@PathParam("client-name") String clientName)
+ {
+ clientService.delete(realmKey, clientName);
+ }
+}
diff --git a/src/main/java/de/tavolio/realm/client/ClientService.java b/src/main/java/de/tavolio/realm/client/ClientService.java
index d352693..ad0d0b2 100644
--- a/src/main/java/de/tavolio/realm/client/ClientService.java
+++ b/src/main/java/de/tavolio/realm/client/ClientService.java
@@ -1,17 +1,17 @@
package de.tavolio.realm.client;
-import de.tavolio.bootstrap.Credentials;
-import de.tavolio.bootstrap.model.Client;
import de.tavolio.realm.RealmEntity;
import de.tavolio.realm.RealmRepo;
+import de.tavolio.realm.RealmService;
import io.quarkus.elytron.security.common.BcryptUtil;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
+import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.NotFoundException;
import org.apache.commons.lang3.StringUtils;
-import java.util.Map;
+import java.util.UUID;
@ApplicationScoped
public class ClientService
@@ -20,9 +20,9 @@ public class ClientService
ClientRepo clientRepo;
@Inject
- RealmRepo realmRepo;
+ RealmService realmService;
- public ClientEntity findByIdAndRealm(String clientId, RealmEntity realm)
+ public ClientEntity requireByIdAndRealm(String clientId, RealmEntity realm)
{
ClientEntity client = clientRepo.findByRealmAndId(realm, clientId);
if (client != null)
@@ -33,17 +33,46 @@ public class ClientService
}
@Transactional
- public ClientEntity create(String realmKey, ClientCreation client)
+ public Client create(String realmKey, ClientCreation client)
{
- RealmEntity realm = realmRepo.findByKey(realmKey);
- if (realm != null)
+ RealmEntity realm = realmService.requireByKey(realmKey);
+ ClientEntity entity = new ClientEntity();
+ entity.setId(UUID.randomUUID().toString().replace("-", "").substring(0, 16));
+ entity.setName(client.name());
+ if (!StringUtils.isBlank(client.secret()))
{
- ClientEntity entity = new ClientEntity();
- entity.setId(client.id());
entity.setSecret(BcryptUtil.bcryptHash(client.secret()));
- entity.setRealm(realm);
- clientRepo.persist(entity);
- return entity;
+ }
+ entity.setAllowedGrants(client.allowedGrants());
+ entity.setRedirectURI(client.redirectURI());
+ entity.setRealm(realm);
+ clientRepo.persist(entity);
+ return ClientMapper.map(entity);
+ }
+
+ @Transactional
+ public void delete(String realmKey, String clientName)
+ {
+ RealmEntity realm = realmService.requireByKey(realmKey);
+ ClientEntity client = clientRepo.findByRealmAndName(realm, clientName);
+ if (client != null)
+ {
+ clientRepo.delete(client);
+ }
+ }
+
+ @Transactional
+ public Client update(String realmKey, String clientName, Client client)
+ {
+ RealmEntity realm = realmService.requireByKey(realmKey);
+ ClientEntity existing = clientRepo.findByRealmAndName(realm, clientName);
+ if (existing != null)
+ {
+ existing.setName(client.name());
+ existing.setRedirectURI(client.redirectURI());
+ existing.setAllowedGrants(client.allowedGrants());
+ clientRepo.persist(existing);
+ return ClientMapper.map(existing);
}
throw new NotFoundException();
}
diff --git a/src/main/java/de/tavolio/realm/client/Grant.java b/src/main/java/de/tavolio/realm/client/Grant.java
new file mode 100644
index 0000000..818f3dd
--- /dev/null
+++ b/src/main/java/de/tavolio/realm/client/Grant.java
@@ -0,0 +1,6 @@
+package de.tavolio.realm.client;
+
+public enum Grant
+{
+ CLIENT_CREDENTIALS, AUTHORIZATION, PASSWORD
+}
diff --git a/src/main/java/de/tavolio/realm/role/Role.java b/src/main/java/de/tavolio/realm/role/Role.java
new file mode 100644
index 0000000..aa87957
--- /dev/null
+++ b/src/main/java/de/tavolio/realm/role/Role.java
@@ -0,0 +1,5 @@
+package de.tavolio.realm.role;
+
+public record Role(String id, String name)
+{
+}
diff --git a/src/main/java/de/tavolio/realm/role/RoleCreation.java b/src/main/java/de/tavolio/realm/role/RoleCreation.java
new file mode 100644
index 0000000..47e654b
--- /dev/null
+++ b/src/main/java/de/tavolio/realm/role/RoleCreation.java
@@ -0,0 +1,5 @@
+package de.tavolio.realm.role;
+
+public record RoleCreation(String name)
+{
+}
diff --git a/src/main/java/de/tavolio/realm/role/RoleEntity.java b/src/main/java/de/tavolio/realm/role/RoleEntity.java
index 8039d75..b79eb11 100644
--- a/src/main/java/de/tavolio/realm/role/RoleEntity.java
+++ b/src/main/java/de/tavolio/realm/role/RoleEntity.java
@@ -21,14 +21,6 @@ public class RoleEntity implements RealmScoped
@JoinColumn(name = "realm_id")
private RealmEntity realm;
- @ElementCollection
- @CollectionTable(
- name = "permission",
- joinColumns = @JoinColumn(name = "role_id")
- )
- @Column(name = "permission")
- private List permissions = new ArrayList<>();
-
public String getId()
{
return id;
@@ -51,17 +43,6 @@ public class RoleEntity implements RealmScoped
return this;
}
- public List getPermissions()
- {
- return permissions;
- }
-
- public RoleEntity setPermissions(List permissions)
- {
- this.permissions = permissions;
- return this;
- }
-
public RealmEntity getRealm()
{
return realm;
@@ -72,16 +53,4 @@ public class RoleEntity implements RealmScoped
this.realm = realm;
return this;
}
-
- public boolean hasPermission(String permission)
- {
- for (String value : permissions)
- {
- if (permission.equals(value))
- {
- return true;
- }
- }
- return false;
- }
}
diff --git a/src/main/java/de/tavolio/realm/role/RoleRepo.java b/src/main/java/de/tavolio/realm/role/RoleRepo.java
index 7650ba4..581704e 100644
--- a/src/main/java/de/tavolio/realm/role/RoleRepo.java
+++ b/src/main/java/de/tavolio/realm/role/RoleRepo.java
@@ -5,13 +5,14 @@ import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase;
import io.quarkus.panache.common.Parameters;
import jakarta.enterprise.context.ApplicationScoped;
+import java.util.List;
import java.util.Optional;
@ApplicationScoped
public class RoleRepo implements PanacheRepositoryBase
{
- public Optional findByNameAndRealmOptional(String name, RealmEntity realm)
+ public List findByRealm(RealmEntity realm)
{
- return find("realm = :realm AND name = :name", Parameters.with("realm", realm).and("name", name)).firstResultOptional();
+ return list("realm = :realm", Parameters.with("realm", realm));
}
}
diff --git a/src/main/java/de/tavolio/realm/role/RoleResource.java b/src/main/java/de/tavolio/realm/role/RoleResource.java
new file mode 100644
index 0000000..e682b9d
--- /dev/null
+++ b/src/main/java/de/tavolio/realm/role/RoleResource.java
@@ -0,0 +1,31 @@
+package de.tavolio.realm.role;
+
+import jakarta.enterprise.context.RequestScoped;
+import jakarta.inject.Inject;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.PathParam;
+
+import java.util.List;
+
+@RequestScoped
+public class RoleResource
+{
+ @PathParam("realm-key")
+ String realmKey;
+
+ @Inject
+ RoleService roleService;
+
+ @GET
+ public List get()
+ {
+ return roleService.findByRealm(realmKey);
+ }
+
+ @POST
+ public Role post(RoleCreation creation)
+ {
+ return roleService.create(realmKey, creation);
+ }
+}
diff --git a/src/main/java/de/tavolio/realm/role/RoleService.java b/src/main/java/de/tavolio/realm/role/RoleService.java
new file mode 100644
index 0000000..01422a4
--- /dev/null
+++ b/src/main/java/de/tavolio/realm/role/RoleService.java
@@ -0,0 +1,50 @@
+package de.tavolio.realm.role;
+
+import de.tavolio.realm.RealmEntity;
+import de.tavolio.realm.RealmResource;
+import de.tavolio.realm.RealmService;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
+import jakarta.transaction.Transactional;
+
+import java.util.List;
+import java.util.UUID;
+
+@ApplicationScoped
+public class RoleService
+{
+ @Inject
+ RoleRepo repo;
+
+ @Inject
+ RealmService realmService;
+
+ @Transactional
+ public Role create(String realmKey, RoleCreation roleCreation)
+ {
+ RealmEntity realm = realmService.requireByKey(realmKey);
+
+ RoleEntity role = new RoleEntity();
+ role.setId(UUID.randomUUID().toString());
+ role.setName(roleCreation.name());
+ role.setRealm(realm);
+ repo.persist(role);
+ return map(role);
+ }
+
+ public List findByRealm(String realmKey)
+ {
+ RealmEntity realm = realmService.requireByKey(realmKey);
+ return map(repo.findByRealm(realm));
+ }
+
+ private List map(List entities)
+ {
+ return entities.stream().map(this::map).toList();
+ }
+
+ private Role map(RoleEntity entity)
+ {
+ return new Role(entity.getId(), entity.getName());
+ }
+}
diff --git a/src/main/java/de/tavolio/realm/user/UserRepo.java b/src/main/java/de/tavolio/realm/user/UserRepo.java
index ca81a37..a006f7c 100644
--- a/src/main/java/de/tavolio/realm/user/UserRepo.java
+++ b/src/main/java/de/tavolio/realm/user/UserRepo.java
@@ -20,4 +20,14 @@ public class UserRepo implements PanacheRepositoryBase
{
return list("id IN :ids", Parameters.with("ids", ids));
}
+
+ public List findByRealm(RealmEntity realm)
+ {
+ return list("realm = :realm", Parameters.with("realm", realm));
+ }
+
+ public UserEntity findByRealmAndId(RealmEntity realm, String id)
+ {
+ return find("realm = :realm AND id = :id", Parameters.with("realm", realm).and("id", id)).firstResult();
+ }
}
diff --git a/src/main/java/de/tavolio/realm/user/UserResource.java b/src/main/java/de/tavolio/realm/user/UserResource.java
index 2d87e79..4e7c8ec 100644
--- a/src/main/java/de/tavolio/realm/user/UserResource.java
+++ b/src/main/java/de/tavolio/realm/user/UserResource.java
@@ -6,10 +6,7 @@ import de.tavolio.realm.user.dto.UserCreation;
import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Inject;
import jakarta.validation.Valid;
-import jakarta.ws.rs.GET;
-import jakarta.ws.rs.POST;
-import jakarta.ws.rs.Path;
-import jakarta.ws.rs.PathParam;
+import jakarta.ws.rs.*;
import org.jboss.logging.Logger;
import java.util.List;
@@ -41,7 +38,7 @@ public class UserResource
public List get()
{
permissionService.hasPermission(Permission.USER_VIEW);
- return userService.get();
+ return userService.get(realmKey);
}
@GET
@@ -52,6 +49,14 @@ public class UserResource
return userService.getUser(id);
}
+ @DELETE
+ @Path("/{id}")
+ public void delete(@PathParam("id") String id)
+ {
+ permissionService.hasPermission(Permission.USER_VIEW);
+ userService.delete(realmKey, id);
+ }
+
@POST
@Path("/search")
public Map get(List ids)
diff --git a/src/main/java/de/tavolio/realm/user/UserService.java b/src/main/java/de/tavolio/realm/user/UserService.java
index 0a4cdd2..7cc7148 100644
--- a/src/main/java/de/tavolio/realm/user/UserService.java
+++ b/src/main/java/de/tavolio/realm/user/UserService.java
@@ -1,6 +1,7 @@
package de.tavolio.realm.user;
import de.tavolio.AuthenticationService;
+import de.tavolio.realm.RealmService;
import de.tavolio.realm.user.dto.User;
import de.tavolio.realm.user.dto.UserCreation;
import de.tavolio.realm.RealmEntity;
@@ -35,6 +36,9 @@ public class UserService
@Inject
RealmRepo realmRepo;
+ @Inject
+ RealmService realmService;
+
@Transactional
public User create(String realmId, UserCreation account)
{
@@ -54,21 +58,15 @@ public class UserService
throw new NotFoundException();
}
- public List get()
+ public List get(String realmKey)
{
- return userMapper.map(userRepo.listAll());
+ RealmEntity realm = realmService.requireByKey(realmKey);
+ return userMapper.map(userRepo.findByRealm(realm));
}
public User getUser(String id)
{
- UserEntity account = authenticationService.requireUser();
- UserEntity requestedAccount = userRepo.findById(id);
- if (requestedAccount != null && requestedAccount.getId().equals(account.getId()))
- {
- return userMapper.map(authenticationService.requireUser());
- }
- LOG.errorf("Cannot access account");
- throw new UnauthorizedException();
+ return userMapper.map(userRepo.findById(id));
}
public Map findByIds(List ids)
@@ -80,4 +78,15 @@ public class UserService
}
return accounts;
}
+
+ @Transactional
+ public void delete(String realmKey, String id)
+ {
+ RealmEntity realm = realmService.requireByKey(realmKey);
+ UserEntity user = userRepo.findByRealmAndId(realm, id);
+ if (user != null)
+ {
+ userRepo.delete(user);
+ }
+ }
}
diff --git a/src/main/resources/bootstrap.yaml b/src/main/resources/bootstrap.yaml
index 775a866..691431f 100644
--- a/src/main/resources/bootstrap.yaml
+++ b/src/main/resources/bootstrap.yaml
@@ -1,6 +1,7 @@
realms:
maven:
name: MavenVault
+ lifetime: 3600
audience:
strategy: static
value: https://maven.dinauer.dev/api
@@ -14,6 +15,12 @@ realms:
redirect-uri: http://localhost:8080/callback
permissions:
- USER_VIEW
+ analytics-backend:
+ secret:
+ bcrypt: $2a$12$1oYS45e/nXP1OeMgdZZAKeEixarRDzbBGZd0xOnEQQMKlOKwVMrX.
+ redirect-uri: http://localhost:8080/callback
+ permissions:
+ - USER_VIEW
users:
- email: andreas.j.dinauer@gmail.com
first-name: Andreas