🚧 Changes
This commit is contained in:
parent
cf0b994eed
commit
483d34457f
@ -0,0 +1,79 @@
|
|||||||
|
package de.tavolio.auth.httpmechanism;
|
||||||
|
|
||||||
|
import de.tavolio.auth.request.JWTRequest;
|
||||||
|
import de.tavolio.auth.request.UsernamePasswordRequest;
|
||||||
|
import io.quarkus.security.AuthenticationFailedException;
|
||||||
|
import io.quarkus.security.identity.IdentityProviderManager;
|
||||||
|
import io.quarkus.security.identity.SecurityIdentity;
|
||||||
|
import io.quarkus.vertx.http.runtime.security.ChallengeData;
|
||||||
|
import io.quarkus.vertx.http.runtime.security.HttpAuthenticationMechanism;
|
||||||
|
import io.quarkus.vertx.http.runtime.security.HttpSecurityUtils;
|
||||||
|
import io.smallrye.mutiny.Uni;
|
||||||
|
import io.vertx.core.http.HttpHeaders;
|
||||||
|
import io.vertx.ext.web.RoutingContext;
|
||||||
|
import jakarta.annotation.Priority;
|
||||||
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
|
import org.apache.commons.lang3.Strings;
|
||||||
|
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
@Priority(50)
|
||||||
|
@ApplicationScoped
|
||||||
|
public class CustomHttpAuthenticationMechanism implements HttpAuthenticationMechanism
|
||||||
|
{
|
||||||
|
private static final String BASIC = "Basic ";
|
||||||
|
private static final String BEARER = "Bearer ";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Uni<SecurityIdentity> authenticate(RoutingContext context, IdentityProviderManager identityProviderManager)
|
||||||
|
{
|
||||||
|
String realmKey = getRealmKey(context.request().uri());
|
||||||
|
context.put(CustomHttpAuthenticationMechanism.class.getName(), this);
|
||||||
|
String auth = context.request().headers().get(HttpHeaders.AUTHORIZATION);
|
||||||
|
if (Strings.CI.startsWith(auth, BASIC))
|
||||||
|
{
|
||||||
|
String decoded = new String(Base64.getDecoder().decode(Strings.CI.removeStart(auth, BASIC)));
|
||||||
|
String[] sections = decoded.split(":");
|
||||||
|
if (sections.length == 2)
|
||||||
|
{
|
||||||
|
UsernamePasswordRequest request = new UsernamePasswordRequest(realmKey, sections[0], sections[1]);
|
||||||
|
HttpSecurityUtils.setRoutingContextAttribute(request, context);
|
||||||
|
return identityProviderManager.authenticate(request);
|
||||||
|
}
|
||||||
|
return Uni.createFrom().failure(new AuthenticationFailedException());
|
||||||
|
}
|
||||||
|
if (Strings.CI.startsWith(auth, BEARER))
|
||||||
|
{
|
||||||
|
JWTRequest request = new JWTRequest(realmKey, Strings.CI.removeStart(auth, BEARER));
|
||||||
|
HttpSecurityUtils.setRoutingContextAttribute(request, context);
|
||||||
|
return identityProviderManager.authenticate(request);
|
||||||
|
}
|
||||||
|
return Uni.createFrom().nullItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getRealmKey(String uri)
|
||||||
|
{
|
||||||
|
String[] sections = uri.split("/");
|
||||||
|
for (int i = 0; i < sections.length; i++)
|
||||||
|
{
|
||||||
|
if (sections[i].equals("realms"))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return sections[i + 1];
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Uni<ChallengeData> getChallenge(RoutingContext context)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,7 +1,8 @@
|
|||||||
package de.tavolio.oidc.identityproviders;
|
package de.tavolio.auth.identityproviders;
|
||||||
|
|
||||||
import de.tavolio.Role;
|
import de.tavolio.Role;
|
||||||
import de.tavolio.realm.user.UserRepo;
|
import de.tavolio.auth.request.UsernamePasswordRequest;
|
||||||
|
import de.tavolio.realm.RealmService;
|
||||||
import de.tavolio.realm.client.ClientEntity;
|
import de.tavolio.realm.client.ClientEntity;
|
||||||
import de.tavolio.realm.client.ClientRepo;
|
import de.tavolio.realm.client.ClientRepo;
|
||||||
import de.tavolio.superuser.SuperuserEntity;
|
import de.tavolio.superuser.SuperuserEntity;
|
||||||
@ -11,7 +12,6 @@ import io.quarkus.security.AuthenticationFailedException;
|
|||||||
import io.quarkus.security.identity.AuthenticationRequestContext;
|
import io.quarkus.security.identity.AuthenticationRequestContext;
|
||||||
import io.quarkus.security.identity.IdentityProvider;
|
import io.quarkus.security.identity.IdentityProvider;
|
||||||
import io.quarkus.security.identity.SecurityIdentity;
|
import io.quarkus.security.identity.SecurityIdentity;
|
||||||
import io.quarkus.security.identity.request.UsernamePasswordAuthenticationRequest;
|
|
||||||
import io.quarkus.security.runtime.QuarkusPrincipal;
|
import io.quarkus.security.runtime.QuarkusPrincipal;
|
||||||
import io.quarkus.security.runtime.QuarkusSecurityIdentity;
|
import io.quarkus.security.runtime.QuarkusSecurityIdentity;
|
||||||
import io.smallrye.mutiny.Uni;
|
import io.smallrye.mutiny.Uni;
|
||||||
@ -23,7 +23,7 @@ import jakarta.inject.Inject;
|
|||||||
|
|
||||||
@ApplicationScoped
|
@ApplicationScoped
|
||||||
@Priority(1)
|
@Priority(1)
|
||||||
public class BasicAuthIdentityProvider implements IdentityProvider<UsernamePasswordAuthenticationRequest>
|
public class BasicAuthIdentityProvider implements IdentityProvider<UsernamePasswordRequest>
|
||||||
{
|
{
|
||||||
@Inject
|
@Inject
|
||||||
ClientRepo clientRepo;
|
ClientRepo clientRepo;
|
||||||
@ -31,19 +31,22 @@ public class BasicAuthIdentityProvider implements IdentityProvider<UsernamePassw
|
|||||||
@Inject
|
@Inject
|
||||||
SuperuserRepo superuserRepo;
|
SuperuserRepo superuserRepo;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
RealmService realmService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Class<UsernamePasswordAuthenticationRequest> getRequestType()
|
public Class<UsernamePasswordRequest> getRequestType()
|
||||||
{
|
{
|
||||||
return UsernamePasswordAuthenticationRequest.class;
|
return UsernamePasswordRequest.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ActivateRequestContext
|
@ActivateRequestContext
|
||||||
public Uni<SecurityIdentity> authenticate(UsernamePasswordAuthenticationRequest usernamePasswordAuthenticationRequest, AuthenticationRequestContext authenticationRequestContext)
|
public Uni<SecurityIdentity> authenticate(UsernamePasswordRequest usernamePasswordAuthenticationRequest, AuthenticationRequestContext authenticationRequestContext)
|
||||||
{
|
{
|
||||||
return Uni.createFrom().item(() -> {
|
return Uni.createFrom().item(() -> {
|
||||||
String username = usernamePasswordAuthenticationRequest.getUsername();
|
String username = usernamePasswordAuthenticationRequest.getUsername();
|
||||||
String credential = new String(usernamePasswordAuthenticationRequest.getPassword().getPassword());
|
String credential = usernamePasswordAuthenticationRequest.getPassword();
|
||||||
SuperuserEntity superuser = superuserRepo.findById(username);
|
SuperuserEntity superuser = superuserRepo.findById(username);
|
||||||
if (superuser != null)
|
if (superuser != null)
|
||||||
{
|
{
|
||||||
@ -52,7 +55,7 @@ public class BasicAuthIdentityProvider implements IdentityProvider<UsernamePassw
|
|||||||
return (SecurityIdentity) QuarkusSecurityIdentity.builder().setPrincipal(new QuarkusPrincipal(username)).addRole(Role.ROOT.toString()).build();
|
return (SecurityIdentity) QuarkusSecurityIdentity.builder().setPrincipal(new QuarkusPrincipal(username)).addRole(Role.ROOT.toString()).build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ClientEntity client = clientRepo.findById(username);
|
ClientEntity client = clientRepo.findByRealmAndName(realmService.requireByKey(usernamePasswordAuthenticationRequest.getRealmKey()), username);
|
||||||
if (client != null)
|
if (client != null)
|
||||||
{
|
{
|
||||||
if (BcryptUtil.matches(credential, client.getSecret()))
|
if (BcryptUtil.matches(credential, client.getSecret()))
|
||||||
@ -0,0 +1,101 @@
|
|||||||
|
package de.tavolio.auth.identityproviders;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import de.tavolio.Role;
|
||||||
|
import de.tavolio.auth.utils.JwtUtils;
|
||||||
|
import de.tavolio.oidc.IssuerService;
|
||||||
|
import de.tavolio.auth.request.JWTRequest;
|
||||||
|
import de.tavolio.realm.RealmEntity;
|
||||||
|
import de.tavolio.realm.client.ClientEntity;
|
||||||
|
import de.tavolio.realm.client.ClientService;
|
||||||
|
import de.tavolio.realm.key.KeypairEntity;
|
||||||
|
import de.tavolio.realm.key.KeypairRepo;
|
||||||
|
import de.tavolio.realm.user.Permission;
|
||||||
|
import de.tavolio.verify.JwksService;
|
||||||
|
import io.quarkus.security.AuthenticationFailedException;
|
||||||
|
import io.quarkus.security.identity.AuthenticationRequestContext;
|
||||||
|
import io.quarkus.security.identity.IdentityProvider;
|
||||||
|
import io.quarkus.security.identity.SecurityIdentity;
|
||||||
|
import io.quarkus.security.runtime.QuarkusPrincipal;
|
||||||
|
import io.quarkus.security.runtime.QuarkusSecurityIdentity;
|
||||||
|
import io.smallrye.jwt.auth.principal.DefaultJWTParser;
|
||||||
|
import io.smallrye.jwt.auth.principal.JWTAuthContextInfo;
|
||||||
|
import io.smallrye.jwt.auth.principal.ParseException;
|
||||||
|
import io.smallrye.mutiny.Uni;
|
||||||
|
import io.smallrye.mutiny.infrastructure.Infrastructure;
|
||||||
|
import jakarta.annotation.Priority;
|
||||||
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
|
import jakarta.enterprise.context.control.ActivateRequestContext;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
|
import org.eclipse.microprofile.jwt.JsonWebToken;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
@ApplicationScoped
|
||||||
|
@Priority(1)
|
||||||
|
public class ClientIdentityProvider implements IdentityProvider<JWTRequest>
|
||||||
|
{
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(ClientIdentityProvider.class);
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
JwksService jwksService;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
KeypairRepo keypairRepo;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
IssuerService issuerService;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
ClientService clientService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<JWTRequest> getRequestType()
|
||||||
|
{
|
||||||
|
return JWTRequest.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@ActivateRequestContext
|
||||||
|
public Uni<SecurityIdentity> authenticate(JWTRequest tokenAuthenticationRequest, AuthenticationRequestContext authenticationRequestContext)
|
||||||
|
{
|
||||||
|
String raw = tokenAuthenticationRequest.getToken();
|
||||||
|
return Uni.createFrom().item(() -> {
|
||||||
|
String kid = JwtUtils.parseHeader(raw).getKid();
|
||||||
|
KeypairEntity keypairEntity = keypairRepo.findById(kid);
|
||||||
|
if (keypairEntity != null)
|
||||||
|
{
|
||||||
|
PublicKey publicKey = jwksService.findByKid(keypairEntity).toPublicKey();
|
||||||
|
RealmEntity realm = keypairEntity.getRealm();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
JsonWebToken token = new DefaultJWTParser(getContextForRealm(realm.getKey())).verify(raw, publicKey);
|
||||||
|
ClientEntity client = clientService.requireByNameAndRealm(token.getName(), realm);
|
||||||
|
if (client != null)
|
||||||
|
{
|
||||||
|
Set<Permission> permissions = new HashSet<>(client.getPermissions());
|
||||||
|
return (SecurityIdentity) QuarkusSecurityIdentity.builder().setPrincipal(new QuarkusPrincipal(client.getId())).addRole(Role.CLIENT.toString()).addAttribute("permissions", permissions).build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (ParseException e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOG.error("Cannot find key with id {}", kid);
|
||||||
|
throw new AuthenticationFailedException();
|
||||||
|
}).runSubscriptionOn(Infrastructure.getDefaultWorkerPool());
|
||||||
|
}
|
||||||
|
|
||||||
|
private JWTAuthContextInfo getContextForRealm(String realmKey)
|
||||||
|
{
|
||||||
|
JWTAuthContextInfo info = new JWTAuthContextInfo();
|
||||||
|
info.setIssuedBy(issuerService.getIssuer(realmKey));
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
}
|
||||||
39
src/main/java/de/tavolio/auth/request/JWTRequest.java
Normal file
39
src/main/java/de/tavolio/auth/request/JWTRequest.java
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package de.tavolio.auth.request;
|
||||||
|
|
||||||
|
import io.quarkus.security.identity.request.AuthenticationRequest;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class JWTRequest extends RealmScoped implements AuthenticationRequest
|
||||||
|
{
|
||||||
|
private final String token;
|
||||||
|
|
||||||
|
public JWTRequest(String realmKey, String token)
|
||||||
|
{
|
||||||
|
super(realmKey);
|
||||||
|
this.token = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getToken()
|
||||||
|
{
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> T getAttribute(String name)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAttribute(String name, Object value)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> getAttributes()
|
||||||
|
{
|
||||||
|
return Map.of();
|
||||||
|
}
|
||||||
|
}
|
||||||
16
src/main/java/de/tavolio/auth/request/RealmScoped.java
Normal file
16
src/main/java/de/tavolio/auth/request/RealmScoped.java
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package de.tavolio.auth.request;
|
||||||
|
|
||||||
|
public class RealmScoped
|
||||||
|
{
|
||||||
|
private final String realmKey;
|
||||||
|
|
||||||
|
public RealmScoped(String realmKey)
|
||||||
|
{
|
||||||
|
this.realmKey = realmKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRealmKey()
|
||||||
|
{
|
||||||
|
return realmKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
package de.tavolio.auth.request;
|
||||||
|
|
||||||
|
import io.quarkus.security.identity.request.AuthenticationRequest;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class UsernamePasswordRequest extends RealmScoped implements AuthenticationRequest
|
||||||
|
{
|
||||||
|
private final String username;
|
||||||
|
private final String password;
|
||||||
|
|
||||||
|
public UsernamePasswordRequest(String realmKey, String username, String password)
|
||||||
|
{
|
||||||
|
super(realmKey);
|
||||||
|
this.username = username;
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsername()
|
||||||
|
{
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPassword()
|
||||||
|
{
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> T getAttribute(String name)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAttribute(String name, Object value)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> getAttributes()
|
||||||
|
{
|
||||||
|
return Map.of();
|
||||||
|
}
|
||||||
|
}
|
||||||
17
src/main/java/de/tavolio/auth/utils/JwtHeader.java
Normal file
17
src/main/java/de/tavolio/auth/utils/JwtHeader.java
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package de.tavolio.auth.utils;
|
||||||
|
|
||||||
|
public class JwtHeader
|
||||||
|
{
|
||||||
|
public String kid;
|
||||||
|
|
||||||
|
public String getKid()
|
||||||
|
{
|
||||||
|
return kid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JwtHeader setKid(String kid)
|
||||||
|
{
|
||||||
|
this.kid = kid;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
32
src/main/java/de/tavolio/auth/utils/JwtUtils.java
Normal file
32
src/main/java/de/tavolio/auth/utils/JwtUtils.java
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package de.tavolio.auth.utils;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import io.quarkus.security.AuthenticationFailedException;
|
||||||
|
|
||||||
|
import java.util.Base64;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class JwtUtils
|
||||||
|
{
|
||||||
|
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
||||||
|
|
||||||
|
public static JwtHeader parseHeader(String token)
|
||||||
|
{
|
||||||
|
String[] sections = token.split("\\.");
|
||||||
|
if (sections.length == 3)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Map<String, Object> result = OBJECT_MAPPER.readValue(new String(Base64.getUrlDecoder().decode(sections[0])), new TypeReference<Map<String, Object>>(){});
|
||||||
|
return new JwtHeader().setKid((String) result.get("kid"));
|
||||||
|
}
|
||||||
|
catch (JsonProcessingException e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new AuthenticationFailedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
6
src/main/java/de/tavolio/bootstrap/Bootstrapper.java
Normal file
6
src/main/java/de/tavolio/bootstrap/Bootstrapper.java
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package de.tavolio.bootstrap;
|
||||||
|
|
||||||
|
public interface Bootstrapper<O>
|
||||||
|
{
|
||||||
|
void run(String key, O object);
|
||||||
|
}
|
||||||
@ -39,7 +39,8 @@ public class ClientBootstrapper
|
|||||||
.setSecret(Credentials.resolve(client.secret()))
|
.setSecret(Credentials.resolve(client.secret()))
|
||||||
.setRedirectURI(client.redirectURI())
|
.setRedirectURI(client.redirectURI())
|
||||||
.setRealm(realm)
|
.setRealm(realm)
|
||||||
.setPermissions(client.permissions());
|
.setPermissions(client.permissions())
|
||||||
|
.setAllowedGrants(client.allowedGrants());
|
||||||
clientRepo.persist(entity);
|
clientRepo.persist(entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,6 @@ import de.tavolio.bootstrap.model.Audience;
|
|||||||
import de.tavolio.bootstrap.model.Realm;
|
import de.tavolio.bootstrap.model.Realm;
|
||||||
import de.tavolio.realm.RealmEntity;
|
import de.tavolio.realm.RealmEntity;
|
||||||
import de.tavolio.realm.RealmRepo;
|
import de.tavolio.realm.RealmRepo;
|
||||||
import de.tavolio.realm.RealmService;
|
|
||||||
import de.tavolio.realm.audience.AudienceStrategyEntity;
|
import de.tavolio.realm.audience.AudienceStrategyEntity;
|
||||||
import de.tavolio.realm.audience.AudienceStrategyRepo;
|
import de.tavolio.realm.audience.AudienceStrategyRepo;
|
||||||
import jakarta.enterprise.context.ApplicationScoped;
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
@ -17,9 +16,6 @@ import java.util.UUID;
|
|||||||
@ApplicationScoped
|
@ApplicationScoped
|
||||||
public class RealmBootstrapService
|
public class RealmBootstrapService
|
||||||
{
|
{
|
||||||
@Inject
|
|
||||||
RealmService realmService;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
KeyBootstrapper keyBootstrapper;
|
KeyBootstrapper keyBootstrapper;
|
||||||
|
|
||||||
@ -34,6 +30,8 @@ public class RealmBootstrapService
|
|||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
AudienceStrategyRepo audienceStrategyRepo;
|
AudienceStrategyRepo audienceStrategyRepo;
|
||||||
|
@Inject
|
||||||
|
RoleBootstrapper roleBootstrapper;
|
||||||
|
|
||||||
public void bootstrap(Map.Entry<String, Realm> realmEntry)
|
public void bootstrap(Map.Entry<String, Realm> realmEntry)
|
||||||
{
|
{
|
||||||
@ -42,6 +40,7 @@ public class RealmBootstrapService
|
|||||||
keyBootstrapper.bootstrap(realm, realmValue.key());
|
keyBootstrapper.bootstrap(realm, realmValue.key());
|
||||||
clientBootstrapper.bootstrap(realm, realmValue.clients());
|
clientBootstrapper.bootstrap(realm, realmValue.clients());
|
||||||
userBootstrapper.bootstrap(realm, realmValue.users());
|
userBootstrapper.bootstrap(realm, realmValue.users());
|
||||||
|
roleBootstrapper.run(realm, realmValue.roles());
|
||||||
}
|
}
|
||||||
|
|
||||||
public RealmEntity run(String key, Realm realm)
|
public RealmEntity run(String key, Realm realm)
|
||||||
@ -53,7 +52,7 @@ public class RealmBootstrapService
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
RealmEntity newRealm = new RealmEntity().setKey(key).setName(realm.name());
|
RealmEntity newRealm = new RealmEntity().setKey(key).setName(realm.name()).setLifetime(Optional.ofNullable(realm.lifetime()).orElse(60 * 30));
|
||||||
realmRepo.persist(newRealm);
|
realmRepo.persist(newRealm);
|
||||||
bootstrapAudience(newRealm, realm.audience());
|
bootstrapAudience(newRealm, realm.audience());
|
||||||
return newRealm;
|
return newRealm;
|
||||||
|
|||||||
38
src/main/java/de/tavolio/bootstrap/RoleBootstrapper.java
Normal file
38
src/main/java/de/tavolio/bootstrap/RoleBootstrapper.java
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package de.tavolio.bootstrap;
|
||||||
|
|
||||||
|
import de.tavolio.bootstrap.model.Role;
|
||||||
|
import de.tavolio.realm.RealmEntity;
|
||||||
|
import de.tavolio.realm.role.RoleEntity;
|
||||||
|
import de.tavolio.realm.role.RoleRepo;
|
||||||
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@ApplicationScoped
|
||||||
|
public class RoleBootstrapper
|
||||||
|
{
|
||||||
|
@Inject
|
||||||
|
RoleRepo roleRepo;
|
||||||
|
|
||||||
|
public void run(RealmEntity realm, List<Role> roles)
|
||||||
|
{
|
||||||
|
for (Role role : roles)
|
||||||
|
{
|
||||||
|
run(realm, role);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void run(RealmEntity realm, Role role)
|
||||||
|
{
|
||||||
|
RoleEntity existingRole = roleRepo.findByRealmAndName(realm, role.name());
|
||||||
|
if (existingRole == null)
|
||||||
|
{
|
||||||
|
RoleEntity newRole = new RoleEntity().setId(UUID.randomUUID().toString());
|
||||||
|
newRole.setName(role.name());
|
||||||
|
newRole.setRealm(realm);
|
||||||
|
roleRepo.persist(newRole);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,12 +1,13 @@
|
|||||||
package de.tavolio.bootstrap.model;
|
package de.tavolio.bootstrap.model;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import de.tavolio.realm.client.Grant;
|
||||||
import de.tavolio.realm.user.Permission;
|
import de.tavolio.realm.user.Permission;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public record Client(@JsonProperty("secret") Credential secret, @JsonProperty("redirect-uri") String redirectURI, @JsonProperty("permissions") Set<String> permissionList)
|
public record Client(@JsonProperty("secret") Credential secret, @JsonProperty("redirect-uri") String redirectURI, @JsonProperty("permissions") Set<String> permissionList, @JsonProperty("allowed-grants") Set<Grant> allowedGrants)
|
||||||
{
|
{
|
||||||
public Set<Permission> permissions()
|
public Set<Permission> permissions()
|
||||||
{
|
{
|
||||||
|
|||||||
@ -3,6 +3,6 @@ package de.tavolio.bootstrap.model;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public record Realm(String name, Key key, Audience audience, Map<String, Client> clients, Map<String, Role> roles, List<String> permissions, List<User> users)
|
public record Realm(String name, Key key, Integer lifetime, Audience audience, Map<String, Client> clients, List<Role> roles, List<String> permissions, List<User> users)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
package de.tavolio.bootstrap.model;
|
package de.tavolio.bootstrap.model;
|
||||||
|
|
||||||
import java.util.List;
|
public record Role(String name)
|
||||||
|
|
||||||
public record Role(List<String> permissions)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -1,30 +0,0 @@
|
|||||||
package de.tavolio.oidc;
|
|
||||||
|
|
||||||
public enum GrantType
|
|
||||||
{
|
|
||||||
AUTH_CODE("authorization_code"),
|
|
||||||
CLIENT_CREDENTIALS("client_credentials");
|
|
||||||
|
|
||||||
private final String value;
|
|
||||||
|
|
||||||
GrantType(String value)
|
|
||||||
{
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getValue()
|
|
||||||
{
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static GrantType fromValue(String value)
|
|
||||||
{
|
|
||||||
for (GrantType type : GrantType.values())
|
|
||||||
{
|
|
||||||
if (type.value.equalsIgnoreCase(value))
|
|
||||||
{
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
} throw new IllegalArgumentException("Unknown grant type: " + value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -9,8 +9,9 @@ import de.tavolio.realm.RealmEntity;
|
|||||||
import de.tavolio.realm.RealmService;
|
import de.tavolio.realm.RealmService;
|
||||||
import de.tavolio.realm.client.ClientEntity;
|
import de.tavolio.realm.client.ClientEntity;
|
||||||
import de.tavolio.realm.client.ClientService;
|
import de.tavolio.realm.client.ClientService;
|
||||||
|
import de.tavolio.realm.client.Grant;
|
||||||
import io.quarkus.security.Authenticated;
|
import io.quarkus.security.Authenticated;
|
||||||
import jakarta.annotation.security.RolesAllowed;
|
import io.quarkus.security.identity.SecurityIdentity;
|
||||||
import jakarta.enterprise.context.RequestScoped;
|
import jakarta.enterprise.context.RequestScoped;
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
import jakarta.transaction.Transactional;
|
import jakarta.transaction.Transactional;
|
||||||
@ -46,6 +47,9 @@ public class OidcResource
|
|||||||
@Inject
|
@Inject
|
||||||
RealmService realmService;
|
RealmService realmService;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
SecurityIdentity identity;
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("/certs")
|
@Path("/certs")
|
||||||
public Map<String, Object> certs()
|
public Map<String, Object> certs()
|
||||||
@ -58,7 +62,7 @@ public class OidcResource
|
|||||||
public Response auth(@QueryParam("client_id") String clientId, @FormParam("email") String email, @FormParam("password") String password)
|
public Response auth(@QueryParam("client_id") String clientId, @FormParam("email") String email, @FormParam("password") String password)
|
||||||
{
|
{
|
||||||
RealmEntity realm = realmService.requireByKey(realmKey);
|
RealmEntity realm = realmService.requireByKey(realmKey);
|
||||||
ClientEntity client = clientService.requireByIdAndRealm(clientId, realm);
|
ClientEntity client = clientService.requireByNameAndRealm(clientId, realm);
|
||||||
if (client != null)
|
if (client != null)
|
||||||
{
|
{
|
||||||
String code = authorizationService.generateBySessionCreation(realmKey, clientId, new AuthorizationCreation(email, password));
|
String code = authorizationService.generateBySessionCreation(realmKey, clientId, new AuthorizationCreation(email, password));
|
||||||
@ -73,14 +77,36 @@ public class OidcResource
|
|||||||
@Transactional
|
@Transactional
|
||||||
public TokenResponse token(@FormParam("grant_type") String grantType, @FormParam("code") String code) throws NoSuchAlgorithmException, InvalidKeySpecException
|
public TokenResponse token(@FormParam("grant_type") String grantType, @FormParam("code") String code) throws NoSuchAlgorithmException, InvalidKeySpecException
|
||||||
{
|
{
|
||||||
if (GrantType.AUTH_CODE.equals(GrantType.fromValue(grantType)))
|
Grant grant = getGrant(grantType);
|
||||||
|
|
||||||
|
RealmEntity realm = realmService.getCurrent();
|
||||||
|
ClientEntity client = clientService.requireByNameAndRealm(identity.getPrincipal().getName(), realm);
|
||||||
|
if (!client.getAllowedGrants().contains(grant))
|
||||||
|
{
|
||||||
|
throw new ForbiddenException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Grant.AUTHORIZATION_CODE.equals(grant))
|
||||||
{
|
{
|
||||||
return userTokenService.getToken(realmKey, code);
|
return userTokenService.getToken(realmKey, code);
|
||||||
}
|
}
|
||||||
if (GrantType.CLIENT_CREDENTIALS.equals(GrantType.fromValue(grantType)))
|
if (Grant.CLIENT_CREDENTIALS.equals(grant))
|
||||||
{
|
{
|
||||||
return clientTokenService.getToken(realmKey);
|
return clientTokenService.getToken(realmKey);
|
||||||
}
|
}
|
||||||
throw new RuntimeException();
|
throw new RuntimeException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Grant getGrant(String grantType)
|
||||||
|
{
|
||||||
|
if ("authorization_code".equals(grantType))
|
||||||
|
{
|
||||||
|
return Grant.AUTHORIZATION_CODE;
|
||||||
|
}
|
||||||
|
if ("client_credentials".equals(grantType))
|
||||||
|
{
|
||||||
|
return Grant.CLIENT_CREDENTIALS;
|
||||||
|
}
|
||||||
|
throw new RuntimeException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -39,7 +39,7 @@ public class AuthorizationService
|
|||||||
{
|
{
|
||||||
|
|
||||||
RealmEntity realm = realmService.requireByKey(realmKey);
|
RealmEntity realm = realmService.requireByKey(realmKey);
|
||||||
ClientEntity client = clientService.requireByIdAndRealm(clientId, realm);
|
ClientEntity client = clientService.requireByNameAndRealm(clientId, realm);
|
||||||
Optional<UserEntity> accountEntityOptional = userRepo.findOptionalByRealmAndEmail(realm, authorizationCreation.email());
|
Optional<UserEntity> accountEntityOptional = userRepo.findOptionalByRealmAndEmail(realm, authorizationCreation.email());
|
||||||
if (accountEntityOptional.isPresent())
|
if (accountEntityOptional.isPresent())
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,127 +0,0 @@
|
|||||||
package de.tavolio.oidc.identityproviders;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
|
||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import de.tavolio.Role;
|
|
||||||
import de.tavolio.oidc.IssuerService;
|
|
||||||
import de.tavolio.realm.RealmEntity;
|
|
||||||
import de.tavolio.realm.client.ClientEntity;
|
|
||||||
import de.tavolio.realm.client.ClientService;
|
|
||||||
import de.tavolio.realm.key.KeypairEntity;
|
|
||||||
import de.tavolio.realm.key.KeypairRepo;
|
|
||||||
import de.tavolio.realm.user.Permission;
|
|
||||||
import de.tavolio.verify.JwksService;
|
|
||||||
import io.quarkus.security.AuthenticationFailedException;
|
|
||||||
import io.quarkus.security.identity.AuthenticationRequestContext;
|
|
||||||
import io.quarkus.security.identity.IdentityProvider;
|
|
||||||
import io.quarkus.security.identity.SecurityIdentity;
|
|
||||||
import io.quarkus.security.identity.request.TokenAuthenticationRequest;
|
|
||||||
import io.quarkus.security.runtime.QuarkusPrincipal;
|
|
||||||
import io.quarkus.security.runtime.QuarkusSecurityIdentity;
|
|
||||||
import io.smallrye.jwt.auth.principal.DefaultJWTParser;
|
|
||||||
import io.smallrye.jwt.auth.principal.JWTAuthContextInfo;
|
|
||||||
import io.smallrye.jwt.auth.principal.ParseException;
|
|
||||||
import io.smallrye.mutiny.Uni;
|
|
||||||
import io.smallrye.mutiny.infrastructure.Infrastructure;
|
|
||||||
import jakarta.annotation.Priority;
|
|
||||||
import jakarta.enterprise.context.ApplicationScoped;
|
|
||||||
import jakarta.enterprise.context.control.ActivateRequestContext;
|
|
||||||
import jakarta.inject.Inject;
|
|
||||||
import org.eclipse.microprofile.jwt.JsonWebToken;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.security.PublicKey;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
@ApplicationScoped
|
|
||||||
@Priority(1)
|
|
||||||
public class ClientIdentityProvider implements IdentityProvider<TokenAuthenticationRequest>
|
|
||||||
{
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(ClientIdentityProvider.class);
|
|
||||||
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
JwksService jwksService;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
KeypairRepo keypairRepo;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
IssuerService issuerService;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
ClientService clientService;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Class<TokenAuthenticationRequest> getRequestType()
|
|
||||||
{
|
|
||||||
return TokenAuthenticationRequest.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@ActivateRequestContext
|
|
||||||
public Uni<SecurityIdentity> authenticate(TokenAuthenticationRequest tokenAuthenticationRequest, AuthenticationRequestContext authenticationRequestContext)
|
|
||||||
{
|
|
||||||
String raw = tokenAuthenticationRequest.getToken().getToken();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Object kid = getHeader(raw).get("kid");
|
|
||||||
if (kid instanceof String keyId)
|
|
||||||
{
|
|
||||||
return Uni.createFrom().item(() -> {
|
|
||||||
KeypairEntity keypairEntity = keypairRepo.findById(keyId);
|
|
||||||
if (keypairEntity != null)
|
|
||||||
{
|
|
||||||
PublicKey publicKey = jwksService.findByKid(keypairEntity).toPublicKey();
|
|
||||||
RealmEntity realm = keypairEntity.getRealm();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
JsonWebToken token = new DefaultJWTParser(getContextForRealm(realm.getKey())).verify(raw, publicKey);
|
|
||||||
ClientEntity client = clientService.requireByIdAndRealm(token.getName(), realm);
|
|
||||||
if (client != null)
|
|
||||||
{
|
|
||||||
Set<Permission> permissions = new HashSet<>(client.getPermissions());
|
|
||||||
return (SecurityIdentity) QuarkusSecurityIdentity.builder().setPrincipal(new QuarkusPrincipal(client.getId())).addRole(Role.CLIENT.toString()).addAttribute("permissions", permissions).build();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (ParseException e)
|
|
||||||
{
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LOG.error("Cannot find key with id {}", kid);
|
|
||||||
throw new AuthenticationFailedException();
|
|
||||||
}).runSubscriptionOn(Infrastructure.getDefaultWorkerPool());
|
|
||||||
}
|
|
||||||
return Uni.createFrom().nullItem();
|
|
||||||
}
|
|
||||||
catch (JsonProcessingException e)
|
|
||||||
{
|
|
||||||
return Uni.createFrom().failure(new AuthenticationFailedException());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map<String, Object> getHeader(String raw) throws JsonProcessingException
|
|
||||||
{
|
|
||||||
return OBJECT_MAPPER.readValue(new String(Base64.getUrlDecoder().decode(section(raw))), new TypeReference<Map<String, Object>>(){});
|
|
||||||
}
|
|
||||||
|
|
||||||
private String section(String raw)
|
|
||||||
{
|
|
||||||
String[] sections = raw.split("\\.");
|
|
||||||
if (sections.length == 3)
|
|
||||||
{
|
|
||||||
return sections[0];
|
|
||||||
}
|
|
||||||
throw new RuntimeException();
|
|
||||||
}
|
|
||||||
|
|
||||||
private JWTAuthContextInfo getContextForRealm(String realmKey)
|
|
||||||
{
|
|
||||||
JWTAuthContextInfo info = new JWTAuthContextInfo();
|
|
||||||
info.setIssuedBy(issuerService.getIssuer(realmKey));
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -32,13 +32,13 @@ public class ClientTokenService
|
|||||||
public TokenResponse getToken(String realmKey)
|
public TokenResponse getToken(String realmKey)
|
||||||
{
|
{
|
||||||
RealmEntity realm = realmService.requireByKey(realmKey);
|
RealmEntity realm = realmService.requireByKey(realmKey);
|
||||||
ClientEntity client = clientService.requireByIdAndRealm(identity.getPrincipal().getName(), realm);
|
ClientEntity client = clientService.requireByNameAndRealm(identity.getPrincipal().getName(), realm);
|
||||||
if (client != null)
|
if (client != null)
|
||||||
{
|
{
|
||||||
KeypairEntity keypair = realm.getKeys().getFirst();
|
KeypairEntity keypair = realm.getKeys().getFirst();
|
||||||
PrivateKey signingKey = KeypairEntity.toPrivateKey(keypair);
|
PrivateKey signingKey = KeypairEntity.toPrivateKey(keypair);
|
||||||
ZonedDateTime expiresAt = ZonedDateTime.now().plusYears(1);
|
ZonedDateTime expiresAt = ZonedDateTime.now().plusYears(1);
|
||||||
String token = clientTokenGenerator.generateAccessToken(realmKey, client.getId(), expiresAt, signingKey, keypair.getId());
|
String token = clientTokenGenerator.generateAccessToken(realmKey, client.getName(), expiresAt, signingKey, keypair.getId());
|
||||||
return new TokenResponse().setAccessToken(token).setTokenType("Bearer").setExpiresAt(expiresAt.toInstant().getEpochSecond());
|
return new TokenResponse().setAccessToken(token).setTokenType("Bearer").setExpiresAt(expiresAt.toInstant().getEpochSecond());
|
||||||
}
|
}
|
||||||
throw new BadRequestException();
|
throw new BadRequestException();
|
||||||
|
|||||||
@ -47,11 +47,11 @@ public class UserTokenService
|
|||||||
String principal = identity.getPrincipal().getName();
|
String principal = identity.getPrincipal().getName();
|
||||||
RealmEntity realm = realmService.requireByKey(realmKey);
|
RealmEntity realm = realmService.requireByKey(realmKey);
|
||||||
CodeEntity entity = codeRepo.findByRealmAndId(realm, code);
|
CodeEntity entity = codeRepo.findByRealmAndId(realm, code);
|
||||||
if (entity != null && !ZonedDateTime.now().isAfter(entity.getExpiresAt()) && principal.equals(entity.getClient().getId()))
|
if (entity != null && !ZonedDateTime.now().isAfter(entity.getExpiresAt()) && principal.equals(entity.getClient().getName()))
|
||||||
{
|
{
|
||||||
KeypairEntity keypair = realm.getKeys().getFirst();
|
KeypairEntity keypair = realm.getKeys().getFirst();
|
||||||
PrivateKey signingKey = KeypairEntity.toPrivateKey(keypair);
|
PrivateKey signingKey = KeypairEntity.toPrivateKey(keypair);
|
||||||
ZonedDateTime expiresAt = ZonedDateTime.now().plusYears(1);
|
ZonedDateTime expiresAt = ZonedDateTime.now().plusSeconds(realm.getLifetime());
|
||||||
TokenResponse response = new TokenResponse()
|
TokenResponse response = new TokenResponse()
|
||||||
.setAccessToken(userTokenGenerator.generateAccessToken(realm.getKey(), principal, entity.getAccount().getId(), expiresAt, signingKey, keypair.getId()))
|
.setAccessToken(userTokenGenerator.generateAccessToken(realm.getKey(), principal, entity.getAccount().getId(), expiresAt, signingKey, keypair.getId()))
|
||||||
.setRefreshToken(UUID.randomUUID().toString())
|
.setRefreshToken(UUID.randomUUID().toString())
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
package de.tavolio.realm;
|
package de.tavolio.realm;
|
||||||
|
|
||||||
public record Realm(String key, String name)
|
public record Realm(String key, String name, int lifetime)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import de.tavolio.realm.user.UserEntity;
|
|||||||
import de.tavolio.realm.client.ClientEntity;
|
import de.tavolio.realm.client.ClientEntity;
|
||||||
import de.tavolio.realm.code.CodeEntity;
|
import de.tavolio.realm.code.CodeEntity;
|
||||||
import de.tavolio.realm.role.RoleEntity;
|
import de.tavolio.realm.role.RoleEntity;
|
||||||
|
import jakarta.enterprise.inject.Vetoed;
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
import org.hibernate.annotations.OnDelete;
|
import org.hibernate.annotations.OnDelete;
|
||||||
import org.hibernate.annotations.OnDeleteAction;
|
import org.hibernate.annotations.OnDeleteAction;
|
||||||
@ -15,6 +16,7 @@ import java.util.List;
|
|||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "realm")
|
@Table(name = "realm")
|
||||||
|
@Vetoed
|
||||||
public class RealmEntity
|
public class RealmEntity
|
||||||
{
|
{
|
||||||
@Id
|
@Id
|
||||||
@ -26,6 +28,8 @@ public class RealmEntity
|
|||||||
@Column(name = "realm_name")
|
@Column(name = "realm_name")
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
|
private int lifetime;
|
||||||
|
|
||||||
@OneToMany(mappedBy = "realm")
|
@OneToMany(mappedBy = "realm")
|
||||||
@OnDelete(action = OnDeleteAction.CASCADE)
|
@OnDelete(action = OnDeleteAction.CASCADE)
|
||||||
private List<KeypairEntity> keys = new ArrayList<>();
|
private List<KeypairEntity> keys = new ArrayList<>();
|
||||||
@ -83,6 +87,17 @@ public class RealmEntity
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getLifetime()
|
||||||
|
{
|
||||||
|
return lifetime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RealmEntity setLifetime(int lifetime)
|
||||||
|
{
|
||||||
|
this.lifetime = lifetime;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public List<KeypairEntity> getKeys()
|
public List<KeypairEntity> getKeys()
|
||||||
{
|
{
|
||||||
return keys;
|
return keys;
|
||||||
|
|||||||
16
src/main/java/de/tavolio/realm/RealmMapper.java
Normal file
16
src/main/java/de/tavolio/realm/RealmMapper.java
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package de.tavolio.realm;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class RealmMapper
|
||||||
|
{
|
||||||
|
public static List<Realm> map(List<RealmEntity> entities)
|
||||||
|
{
|
||||||
|
return entities.stream().map(RealmMapper::map).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Realm map(RealmEntity entity)
|
||||||
|
{
|
||||||
|
return new Realm(entity.getKey(), entity.getName(), entity.getLifetime());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,6 +3,8 @@ package de.tavolio.realm;
|
|||||||
import de.tavolio.Role;
|
import de.tavolio.Role;
|
||||||
import de.tavolio.oidc.OidcConfigurationResource;
|
import de.tavolio.oidc.OidcConfigurationResource;
|
||||||
import de.tavolio.oidc.OidcResource;
|
import de.tavolio.oidc.OidcResource;
|
||||||
|
import de.tavolio.realm.assignment.Assignment;
|
||||||
|
import de.tavolio.realm.assignment.AssignmentResource;
|
||||||
import de.tavolio.realm.client.ClientResource;
|
import de.tavolio.realm.client.ClientResource;
|
||||||
import de.tavolio.realm.role.RoleResource;
|
import de.tavolio.realm.role.RoleResource;
|
||||||
import de.tavolio.realm.user.UserResource;
|
import de.tavolio.realm.user.UserResource;
|
||||||
@ -11,6 +13,7 @@ import jakarta.annotation.security.RolesAllowed;
|
|||||||
import jakarta.enterprise.inject.spi.CDI;
|
import jakarta.enterprise.inject.spi.CDI;
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
import jakarta.ws.rs.GET;
|
import jakarta.ws.rs.GET;
|
||||||
|
import jakarta.ws.rs.POST;
|
||||||
import jakarta.ws.rs.Path;
|
import jakarta.ws.rs.Path;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -21,19 +24,28 @@ public class RealmResource
|
|||||||
@Inject
|
@Inject
|
||||||
RealmRepo repo;
|
RealmRepo repo;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
RealmService realmService;
|
||||||
|
|
||||||
@Path("/{realm-key}")
|
@Path("/{realm-key}")
|
||||||
public RealmSubResource realms()
|
public RealmSubResource realms()
|
||||||
{
|
{
|
||||||
return CDI.current().select(RealmSubResource.class).get();
|
return CDI.current().select(RealmSubResource.class).get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@RolesAllowed("ROOT")
|
@RolesAllowed({"ROOT", "CLIENT"})
|
||||||
@Path("/{realm-key}/users")
|
@Path("/{realm-key}/users")
|
||||||
public UserResource accounts()
|
public UserResource accounts()
|
||||||
{
|
{
|
||||||
return CDI.current().select(UserResource.class).get();
|
return CDI.current().select(UserResource.class).get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Path("/{realm-key}/users/{user-id}/role-assignments")
|
||||||
|
public AssignmentResource assignments()
|
||||||
|
{
|
||||||
|
return CDI.current().select(AssignmentResource.class).get();
|
||||||
|
}
|
||||||
|
|
||||||
@RolesAllowed("ROOT")
|
@RolesAllowed("ROOT")
|
||||||
@Path("/{realm-key}/clients")
|
@Path("/{realm-key}/clients")
|
||||||
public ClientResource clients()
|
public ClientResource clients()
|
||||||
@ -63,6 +75,13 @@ public class RealmResource
|
|||||||
@GET
|
@GET
|
||||||
public List<Realm> get()
|
public List<Realm> get()
|
||||||
{
|
{
|
||||||
return repo.listAll().stream().map(item -> new Realm(item.getKey(), item.getName())).toList();
|
return RealmMapper.map(repo.listAll());
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@RolesAllowed("ROOT")
|
||||||
|
public Realm post(RealmCreation realmCreation)
|
||||||
|
{
|
||||||
|
return realmService.create(realmCreation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,13 @@
|
|||||||
package de.tavolio.realm;
|
package de.tavolio.realm;
|
||||||
|
|
||||||
import de.tavolio.bootstrap.model.Realm;
|
|
||||||
import de.tavolio.realm.key.KeypairEntity;
|
import de.tavolio.realm.key.KeypairEntity;
|
||||||
import jakarta.enterprise.context.ApplicationScoped;
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
import jakarta.transaction.Transactional;
|
import jakarta.transaction.Transactional;
|
||||||
|
import jakarta.ws.rs.InternalServerErrorException;
|
||||||
import jakarta.ws.rs.NotFoundException;
|
import jakarta.ws.rs.NotFoundException;
|
||||||
|
import jakarta.ws.rs.core.UriInfo;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import java.security.KeyPair;
|
import java.security.KeyPair;
|
||||||
import java.security.KeyPairGenerator;
|
import java.security.KeyPairGenerator;
|
||||||
@ -22,6 +24,9 @@ public class RealmService
|
|||||||
@Inject
|
@Inject
|
||||||
RealmRepo repo;
|
RealmRepo repo;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
UriInfo context;
|
||||||
|
|
||||||
public RealmEntity requireByKey(String key)
|
public RealmEntity requireByKey(String key)
|
||||||
{
|
{
|
||||||
RealmEntity realm = repo.findByKey(key);
|
RealmEntity realm = repo.findByKey(key);
|
||||||
@ -32,13 +37,23 @@ public class RealmService
|
|||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RealmEntity getCurrent()
|
||||||
|
{
|
||||||
|
String realmKey = context.getPathParameters().getFirst("realm-key");
|
||||||
|
if (!StringUtils.isBlank(realmKey))
|
||||||
|
{
|
||||||
|
return requireByKey(realmKey);
|
||||||
|
}
|
||||||
|
throw new InternalServerErrorException();
|
||||||
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public RealmEntity create(RealmCreation creation)
|
public Realm create(RealmCreation creation)
|
||||||
{
|
{
|
||||||
RealmEntity realm = new RealmEntity();
|
RealmEntity realm = new RealmEntity();
|
||||||
realm.setKey(creation.key());
|
realm.setKey(creation.key());
|
||||||
realm.setName(creation.name());
|
realm.setName(creation.name());
|
||||||
repo.persist(realm);
|
repo.persist(realm);
|
||||||
return realm;
|
return RealmMapper.map(realm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,16 +4,15 @@ import io.quarkus.security.Authenticated;
|
|||||||
import jakarta.annotation.security.RolesAllowed;
|
import jakarta.annotation.security.RolesAllowed;
|
||||||
import jakarta.enterprise.context.RequestScoped;
|
import jakarta.enterprise.context.RequestScoped;
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
|
import jakarta.transaction.Transactional;
|
||||||
import jakarta.ws.rs.DELETE;
|
import jakarta.ws.rs.DELETE;
|
||||||
import jakarta.ws.rs.GET;
|
import jakarta.ws.rs.GET;
|
||||||
|
import jakarta.ws.rs.POST;
|
||||||
import jakarta.ws.rs.PathParam;
|
import jakarta.ws.rs.PathParam;
|
||||||
|
|
||||||
@RequestScoped
|
@RequestScoped
|
||||||
public class RealmSubResource
|
public class RealmSubResource
|
||||||
{
|
{
|
||||||
@PathParam("realm-key")
|
|
||||||
String key;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
RealmRepo repo;
|
RealmRepo repo;
|
||||||
|
|
||||||
@ -23,14 +22,14 @@ public class RealmSubResource
|
|||||||
@GET
|
@GET
|
||||||
public Realm get()
|
public Realm get()
|
||||||
{
|
{
|
||||||
RealmEntity realm = service.requireByKey(key);
|
return RealmMapper.map(service.getCurrent());
|
||||||
return new Realm(realm.getKey(), realm.getName());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@RolesAllowed("ROOT")
|
@RolesAllowed("ROOT")
|
||||||
@DELETE
|
@DELETE
|
||||||
|
@Transactional
|
||||||
public void delete()
|
public void delete()
|
||||||
{
|
{
|
||||||
repo.delete(service.requireByKey(key));
|
repo.delete(service.getCurrent());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,9 @@
|
|||||||
|
package de.tavolio.realm.assignment;
|
||||||
|
|
||||||
|
import de.tavolio.realm.role.Role;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public record Assignment(List<Role> assigned, List<Role> unassigned)
|
||||||
|
{
|
||||||
|
}
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
package de.tavolio.realm.assignment;
|
||||||
|
|
||||||
|
import de.tavolio.realm.role.RoleEntity;
|
||||||
|
import de.tavolio.realm.user.UserEntity;
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "assignment")
|
||||||
|
public class AssignmentEntity
|
||||||
|
{
|
||||||
|
@Id
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "user_id")
|
||||||
|
private UserEntity user;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "role_id")
|
||||||
|
private RoleEntity role;
|
||||||
|
|
||||||
|
public String getId()
|
||||||
|
{
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AssignmentEntity setId(String id)
|
||||||
|
{
|
||||||
|
this.id = id;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserEntity getUser()
|
||||||
|
{
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AssignmentEntity setUser(UserEntity user)
|
||||||
|
{
|
||||||
|
this.user = user;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RoleEntity getRole()
|
||||||
|
{
|
||||||
|
return role;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AssignmentEntity setRole(RoleEntity role)
|
||||||
|
{
|
||||||
|
this.role = role;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
package de.tavolio.realm.assignment;
|
||||||
|
|
||||||
|
import de.tavolio.realm.RealmEntity;
|
||||||
|
import de.tavolio.realm.role.RoleEntity;
|
||||||
|
import de.tavolio.realm.user.UserEntity;
|
||||||
|
import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase;
|
||||||
|
import io.quarkus.panache.common.Parameters;
|
||||||
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@ApplicationScoped
|
||||||
|
public class AssignmentRepo implements PanacheRepositoryBase<AssignmentEntity, String>
|
||||||
|
{
|
||||||
|
public AssignmentEntity findByRoleAndUser(RoleEntity role, UserEntity user)
|
||||||
|
{
|
||||||
|
return find("role = :role AND user = :user", Parameters.with("role", role).and("user", user)).firstResult();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
package de.tavolio.realm.assignment;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public record AssignmentRequest(List<String> assign, List<String> unassign)
|
||||||
|
{
|
||||||
|
}
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
package de.tavolio.realm.assignment;
|
||||||
|
|
||||||
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
|
import jakarta.enterprise.context.RequestScoped;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
|
import jakarta.ws.rs.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@ApplicationScoped
|
||||||
|
public class AssignmentResource
|
||||||
|
{
|
||||||
|
@Inject
|
||||||
|
AssignmentService assignmentService;
|
||||||
|
|
||||||
|
@GET
|
||||||
|
public Assignment get(@PathParam("user-id") String userId)
|
||||||
|
{
|
||||||
|
return assignmentService.get(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PUT
|
||||||
|
public void post(@PathParam("user-id") String userId, AssignmentRequest request)
|
||||||
|
{
|
||||||
|
if (request.assign() != null)
|
||||||
|
{
|
||||||
|
assignmentService.assign(userId, request.assign());
|
||||||
|
}
|
||||||
|
if (request.unassign() != null)
|
||||||
|
{
|
||||||
|
assignmentService.unassign(userId, request.unassign());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,98 @@
|
|||||||
|
package de.tavolio.realm.assignment;
|
||||||
|
|
||||||
|
import de.tavolio.realm.RealmEntity;
|
||||||
|
import de.tavolio.realm.RealmService;
|
||||||
|
import de.tavolio.realm.role.Role;
|
||||||
|
import de.tavolio.realm.role.RoleEntity;
|
||||||
|
import de.tavolio.realm.role.RoleRepo;
|
||||||
|
import de.tavolio.realm.role.RoleService;
|
||||||
|
import de.tavolio.realm.user.UserEntity;
|
||||||
|
import de.tavolio.realm.user.UserRepo;
|
||||||
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
|
import jakarta.transaction.Transactional;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@ApplicationScoped
|
||||||
|
public class AssignmentService
|
||||||
|
{
|
||||||
|
@Inject
|
||||||
|
RoleRepo roleRepo;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
RealmService realmService;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
UserRepo userRepo;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
AssignmentRepo assignmentRepo;
|
||||||
|
|
||||||
|
public Assignment get(String userId)
|
||||||
|
{
|
||||||
|
RealmEntity realm = realmService.getCurrent();
|
||||||
|
UserEntity user = userRepo.findByRealmAndId(realm, userId);
|
||||||
|
List<Role> assigned = new LinkedList<>();
|
||||||
|
List<Role> unassigned = new LinkedList<>();
|
||||||
|
for (RoleEntity role : roleRepo.findByRealm(realm))
|
||||||
|
{
|
||||||
|
if (isAssigned(user, role))
|
||||||
|
{
|
||||||
|
assigned.add(new Role(role.getId(), role.getName()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
unassigned.add(new Role(role.getId(), role.getName()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new Assignment(assigned, unassigned);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void assign(String userId, List<String> roles)
|
||||||
|
{
|
||||||
|
RealmEntity realm = realmService.getCurrent();
|
||||||
|
UserEntity user = userRepo.findByRealmAndId(realm, userId);
|
||||||
|
for (String roleName : roles)
|
||||||
|
{
|
||||||
|
RoleEntity role = roleRepo.findByRealmAndName(realm, roleName);
|
||||||
|
assign(user, role);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void assign(UserEntity user, RoleEntity role)
|
||||||
|
{
|
||||||
|
AssignmentEntity assignment = new AssignmentEntity().setId(UUID.randomUUID().toString());
|
||||||
|
assignment.setUser(user);
|
||||||
|
assignment.setRole(role);
|
||||||
|
assignmentRepo.persist(assignment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void unassign(String userId, List<String> roles)
|
||||||
|
{
|
||||||
|
RealmEntity realm = realmService.getCurrent();
|
||||||
|
UserEntity user = userRepo.findByRealmAndId(realm, userId);
|
||||||
|
for (String roleName : roles)
|
||||||
|
{
|
||||||
|
RoleEntity role = roleRepo.findByRealmAndName(realm, roleName);
|
||||||
|
AssignmentEntity assignment = assignmentRepo.findByRoleAndUser(role, user);
|
||||||
|
assignmentRepo.delete(assignment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isAssigned(UserEntity user, RoleEntity role)
|
||||||
|
{
|
||||||
|
for (AssignmentEntity assignment : role.getAssignments())
|
||||||
|
{
|
||||||
|
if (assignment.getUser().getId().equals(user.getId()))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -43,7 +43,7 @@ public class ClientEntity implements RealmScoped
|
|||||||
@CollectionTable(name = "allowed_grants", joinColumns = @JoinColumn(name = "client_id"))
|
@CollectionTable(name = "allowed_grants", joinColumns = @JoinColumn(name = "client_id"))
|
||||||
@Column(name = "grant_name")
|
@Column(name = "grant_name")
|
||||||
@Enumerated(EnumType.STRING)
|
@Enumerated(EnumType.STRING)
|
||||||
private Set<Grant> allowedGrants = new HashSet<Grant>();
|
private Set<Grant> allowedGrants = new HashSet<>();
|
||||||
|
|
||||||
public String getId()
|
public String getId()
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,13 +1,11 @@
|
|||||||
package de.tavolio.realm.client;
|
package de.tavolio.realm.client;
|
||||||
|
|
||||||
import de.tavolio.realm.RealmEntity;
|
import de.tavolio.realm.RealmEntity;
|
||||||
import de.tavolio.realm.RealmRepo;
|
|
||||||
import de.tavolio.realm.RealmService;
|
import de.tavolio.realm.RealmService;
|
||||||
import io.quarkus.elytron.security.common.BcryptUtil;
|
import io.quarkus.elytron.security.common.BcryptUtil;
|
||||||
import jakarta.enterprise.context.ApplicationScoped;
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
import jakarta.transaction.Transactional;
|
import jakarta.transaction.Transactional;
|
||||||
import jakarta.ws.rs.DELETE;
|
|
||||||
import jakarta.ws.rs.NotFoundException;
|
import jakarta.ws.rs.NotFoundException;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
@ -22,9 +20,9 @@ public class ClientService
|
|||||||
@Inject
|
@Inject
|
||||||
RealmService realmService;
|
RealmService realmService;
|
||||||
|
|
||||||
public ClientEntity requireByIdAndRealm(String clientId, RealmEntity realm)
|
public ClientEntity requireByNameAndRealm(String clientId, RealmEntity realm)
|
||||||
{
|
{
|
||||||
ClientEntity client = clientRepo.findByRealmAndId(realm, clientId);
|
ClientEntity client = clientRepo.findByRealmAndName(realm, clientId);
|
||||||
if (client != null)
|
if (client != null)
|
||||||
{
|
{
|
||||||
return client;
|
return client;
|
||||||
|
|||||||
@ -2,5 +2,5 @@ package de.tavolio.realm.client;
|
|||||||
|
|
||||||
public enum Grant
|
public enum Grant
|
||||||
{
|
{
|
||||||
CLIENT_CREDENTIALS, AUTHORIZATION, PASSWORD
|
CLIENT_CREDENTIALS, AUTHORIZATION_CODE, PASSWORD
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package de.tavolio.realm.role;
|
|||||||
|
|
||||||
import de.tavolio.realm.RealmEntity;
|
import de.tavolio.realm.RealmEntity;
|
||||||
import de.tavolio.realm.RealmScoped;
|
import de.tavolio.realm.RealmScoped;
|
||||||
|
import de.tavolio.realm.assignment.AssignmentEntity;
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -21,6 +22,9 @@ public class RoleEntity implements RealmScoped
|
|||||||
@JoinColumn(name = "realm_id")
|
@JoinColumn(name = "realm_id")
|
||||||
private RealmEntity realm;
|
private RealmEntity realm;
|
||||||
|
|
||||||
|
@OneToMany(mappedBy = "role")
|
||||||
|
private List<AssignmentEntity> assignments;
|
||||||
|
|
||||||
public String getId()
|
public String getId()
|
||||||
{
|
{
|
||||||
return id;
|
return id;
|
||||||
@ -53,4 +57,15 @@ public class RoleEntity implements RealmScoped
|
|||||||
this.realm = realm;
|
this.realm = realm;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<AssignmentEntity> getAssignments()
|
||||||
|
{
|
||||||
|
return assignments;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RoleEntity setAssignments(List<AssignmentEntity> assignments)
|
||||||
|
{
|
||||||
|
this.assignments = assignments;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,4 +15,9 @@ public class RoleRepo implements PanacheRepositoryBase<RoleEntity, String>
|
|||||||
{
|
{
|
||||||
return list("realm = :realm", Parameters.with("realm", realm));
|
return list("realm = :realm", Parameters.with("realm", realm));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RoleEntity findByRealmAndName(RealmEntity realm, String name)
|
||||||
|
{
|
||||||
|
return find("realm = :realm AND name = :name", Parameters.with("realm", realm).and("name", name)).firstResult();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,5 +2,5 @@ package de.tavolio.realm.user;
|
|||||||
|
|
||||||
public enum Permission
|
public enum Permission
|
||||||
{
|
{
|
||||||
USER_VIEW, USER_CREATE
|
USER_VIEW, USER_DELETE, USER_CREATE
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package de.tavolio.realm.user;
|
package de.tavolio.realm.user;
|
||||||
|
|
||||||
import de.tavolio.realm.RealmScoped;
|
import de.tavolio.realm.RealmScoped;
|
||||||
|
import de.tavolio.realm.assignment.AssignmentEntity;
|
||||||
import de.tavolio.realm.code.CodeEntity;
|
import de.tavolio.realm.code.CodeEntity;
|
||||||
import de.tavolio.realm.RealmEntity;
|
import de.tavolio.realm.RealmEntity;
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
@ -34,6 +35,9 @@ public class UserEntity implements RealmScoped
|
|||||||
@OneToMany(mappedBy = "account")
|
@OneToMany(mappedBy = "account")
|
||||||
private List<CodeEntity> codes;
|
private List<CodeEntity> codes;
|
||||||
|
|
||||||
|
@OneToMany(mappedBy = "user")
|
||||||
|
private List<AssignmentEntity> assignments;
|
||||||
|
|
||||||
public static UserEntity init()
|
public static UserEntity init()
|
||||||
{
|
{
|
||||||
return new UserEntity().setId(UUID.randomUUID().toString());
|
return new UserEntity().setId(UUID.randomUUID().toString());
|
||||||
@ -126,4 +130,15 @@ public class UserEntity implements RealmScoped
|
|||||||
this.codes = codes;
|
this.codes = codes;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<AssignmentEntity> getAssignments()
|
||||||
|
{
|
||||||
|
return assignments;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserEntity setAssignments(List<AssignmentEntity> assignments)
|
||||||
|
{
|
||||||
|
this.assignments = assignments;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -53,7 +53,7 @@ public class UserResource
|
|||||||
@Path("/{id}")
|
@Path("/{id}")
|
||||||
public void delete(@PathParam("id") String id)
|
public void delete(@PathParam("id") String id)
|
||||||
{
|
{
|
||||||
permissionService.hasPermission(Permission.USER_VIEW);
|
permissionService.hasPermission(Permission.USER_DELETE);
|
||||||
userService.delete(realmKey, id);
|
userService.delete(realmKey, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -25,7 +25,7 @@ quarkus.flyway.enabled=false
|
|||||||
quarkus.flyway.migrate-at-start=true
|
quarkus.flyway.migrate-at-start=true
|
||||||
|
|
||||||
quarkus.http.access-log.enabled=true
|
quarkus.http.access-log.enabled=true
|
||||||
quarkus.http.auth.basic=true
|
quarkus.http.auth.basic=false
|
||||||
|
|
||||||
%dev.io.verifoo.http.origin=http://localhost:8089
|
%dev.io.verifoo.http.origin=http://localhost:8089
|
||||||
|
|
||||||
|
|||||||
@ -8,17 +8,22 @@ realms:
|
|||||||
key:
|
key:
|
||||||
type: EC
|
type: EC
|
||||||
alg: P256
|
alg: P256
|
||||||
|
roles:
|
||||||
|
- name: ADMIN
|
||||||
|
- name: USER
|
||||||
clients:
|
clients:
|
||||||
backend:
|
backend:
|
||||||
secret:
|
secret:
|
||||||
bcrypt: $2a$12$1oYS45e/nXP1OeMgdZZAKeEixarRDzbBGZd0xOnEQQMKlOKwVMrX.
|
bcrypt: $2a$12$1oYS45e/nXP1OeMgdZZAKeEixarRDzbBGZd0xOnEQQMKlOKwVMrX.
|
||||||
redirect-uri: http://localhost:8080/callback
|
redirect-uri: http://localhost:8080/auth/callback
|
||||||
permissions:
|
permissions:
|
||||||
- USER_VIEW
|
- USER_VIEW
|
||||||
|
allowed-grants:
|
||||||
|
- AUTHORIZATION_CODE
|
||||||
analytics-backend:
|
analytics-backend:
|
||||||
secret:
|
secret:
|
||||||
bcrypt: $2a$12$1oYS45e/nXP1OeMgdZZAKeEixarRDzbBGZd0xOnEQQMKlOKwVMrX.
|
bcrypt: $2a$12$1oYS45e/nXP1OeMgdZZAKeEixarRDzbBGZd0xOnEQQMKlOKwVMrX.
|
||||||
redirect-uri: http://localhost:8080/callback
|
redirect-uri: http://localhost:8080/auth/callback
|
||||||
permissions:
|
permissions:
|
||||||
- USER_VIEW
|
- USER_VIEW
|
||||||
users:
|
users:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user