-
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
pipinet
committed
Jan 24, 2024
1 parent
ca97455
commit b8d6f47
Showing
2 changed files
with
272 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
270 changes: 270 additions & 0 deletions
270
security/src/main/java/com/qwlabs/security/LazyRolesSecurityIdentity.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,270 @@ | ||
package com.qwlabs.security; | ||
|
||
import com.google.common.base.Suppliers; | ||
import com.google.common.collect.Sets; | ||
import io.quarkus.security.credential.Credential; | ||
import io.quarkus.security.identity.SecurityIdentity; | ||
import io.smallrye.mutiny.Uni; | ||
|
||
import java.security.Permission; | ||
import java.security.Principal; | ||
import java.util.ArrayList; | ||
import java.util.Collections; | ||
import java.util.HashMap; | ||
import java.util.HashSet; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Objects; | ||
import java.util.Set; | ||
import java.util.function.Function; | ||
import java.util.function.Supplier; | ||
|
||
public class LazyRolesSecurityIdentity implements SecurityIdentity { | ||
private final Principal principal; | ||
private final Set<String> roles; | ||
private final Supplier<Set<String>> lazyRoles; | ||
private final Set<Credential> credentials; | ||
private final Map<String, Object> attributes; | ||
private final List<Function<Permission, Uni<Boolean>>> permissionCheckers; | ||
private final boolean anonymous; | ||
private final Supplier<Set<String>> finalRoles = Suppliers.memoize(this::loadFinalRoles); | ||
|
||
private Set<String> loadFinalRoles() { | ||
if (Objects.isNull(lazyRoles)) { | ||
return roles; | ||
} | ||
if (roles.isEmpty()) { | ||
return roles; | ||
} | ||
Set<String> result = Sets.newHashSet(roles); | ||
result.addAll(lazyRoles.get()); | ||
return result; | ||
} | ||
|
||
private LazyRolesSecurityIdentity(Builder builder) { | ||
this.principal = builder.principal; | ||
this.roles = Collections.unmodifiableSet(builder.roles); | ||
this.lazyRoles = builder.lazyRoles; | ||
this.credentials = Collections.unmodifiableSet(builder.credentials); | ||
this.attributes = Collections.unmodifiableMap(builder.attributes); | ||
this.permissionCheckers = Collections.unmodifiableList(builder.permissionCheckers); | ||
this.anonymous = builder.anonymous; | ||
} | ||
|
||
@Override | ||
public Principal getPrincipal() { | ||
return principal; | ||
} | ||
|
||
@Override | ||
public boolean isAnonymous() { | ||
return anonymous; | ||
} | ||
|
||
@Override | ||
public Set<String> getRoles() { | ||
return finalRoles.get(); | ||
} | ||
|
||
@Override | ||
public boolean hasRole(String role) { | ||
return roles.contains(role); | ||
} | ||
|
||
@Override | ||
public <T extends Credential> T getCredential(Class<T> credentialType) { | ||
for (Credential i : credentials) { | ||
if (credentialType.isAssignableFrom(i.getClass())) { | ||
return (T) i; | ||
} | ||
} | ||
return null; | ||
} | ||
|
||
@Override | ||
public Set<Credential> getCredentials() { | ||
return credentials; | ||
} | ||
|
||
@Override | ||
public <T> T getAttribute(String name) { | ||
return (T) attributes.get(name); | ||
} | ||
|
||
@Override | ||
public Map<String, Object> getAttributes() { | ||
return attributes; | ||
} | ||
|
||
@Override | ||
public Uni<Boolean> checkPermission(Permission permission) { | ||
if (permissionCheckers.isEmpty()) { | ||
return Uni.createFrom().item(false); | ||
} | ||
List<Uni<Boolean>> results = new ArrayList<>(permissionCheckers.size()); | ||
for (Function<Permission, Uni<Boolean>> checker : permissionCheckers) { | ||
Uni<Boolean> res = checker.apply(permission); | ||
if (res != null) { | ||
results.add(res); | ||
} | ||
} | ||
if (results.isEmpty()) { | ||
return Uni.createFrom().item(false); | ||
} | ||
if (results.size() == 1) { | ||
return results.get(0); | ||
} | ||
return Uni.combine().all().unis(results).combinedWith(new Function<List<?>, Boolean>() { | ||
@Override | ||
public Boolean apply(List<?> o) { | ||
Boolean result = null; | ||
//if any are true we return true | ||
//otherwise if all are null we return null | ||
//if some are false and some null we return false | ||
for (Object i : o) { | ||
if (i != null) { | ||
boolean val = (boolean) i; | ||
if (val) { | ||
return true; | ||
} | ||
result = false; | ||
} | ||
} | ||
return result; | ||
} | ||
}); | ||
|
||
} | ||
|
||
public static Builder builder() { | ||
return new Builder(); | ||
} | ||
|
||
public static Builder builder(SecurityIdentity identity) { | ||
Builder builder = new Builder() | ||
.addAttributes(identity.getAttributes()) | ||
.addCredentials(identity.getCredentials()) | ||
.addRoles(identity.getRoles()) | ||
.addPermissionChecker(new Function<Permission, Uni<Boolean>>() { | ||
@Override | ||
public Uni<Boolean> apply(Permission permission) { | ||
// sustain previous permission checks | ||
return identity.checkPermission(permission); | ||
} | ||
}) | ||
.setPrincipal(identity.getPrincipal()) | ||
.setAnonymous(identity.isAnonymous()); | ||
return builder; | ||
} | ||
|
||
public static class Builder { | ||
|
||
Principal principal; | ||
Set<String> roles = new HashSet<>(); | ||
Supplier<Set<String>> lazyRoles; | ||
Set<Credential> credentials = new HashSet<>(); | ||
Map<String, Object> attributes = new HashMap<>(); | ||
List<Function<Permission, Uni<Boolean>>> permissionCheckers = new ArrayList<>(); | ||
private boolean anonymous; | ||
boolean built = false; | ||
|
||
public Builder setPrincipal(Principal principal) { | ||
if (built) { | ||
throw new IllegalStateException(); | ||
} | ||
this.principal = principal; | ||
return this; | ||
} | ||
|
||
public Builder setLazyRoles(Supplier<Set<String>> lazyRoles) { | ||
if (built) { | ||
throw new IllegalStateException(); | ||
} | ||
this.lazyRoles = lazyRoles; | ||
return this; | ||
} | ||
|
||
public Builder addRole(String role) { | ||
if (built) { | ||
throw new IllegalStateException(); | ||
} | ||
this.roles.add(role); | ||
return this; | ||
} | ||
|
||
public Builder addRoles(Set<String> roles) { | ||
if (built) { | ||
throw new IllegalStateException(); | ||
} | ||
this.roles.addAll(roles); | ||
return this; | ||
} | ||
|
||
public Builder addCredential(Credential credential) { | ||
if (built) { | ||
throw new IllegalStateException(); | ||
} | ||
credentials.add(credential); | ||
return this; | ||
} | ||
|
||
public Builder addCredentials(Set<Credential> credentials) { | ||
if (built) { | ||
throw new IllegalStateException(); | ||
} | ||
this.credentials.addAll(credentials); | ||
return this; | ||
} | ||
|
||
public Builder addAttribute(String key, Object value) { | ||
if (built) { | ||
throw new IllegalStateException(); | ||
} | ||
attributes.put(key, value); | ||
return this; | ||
} | ||
|
||
public Builder addAttributes(Map<String, Object> attributes) { | ||
if (built) { | ||
throw new IllegalStateException(); | ||
} | ||
this.attributes.putAll(attributes); | ||
return this; | ||
} | ||
|
||
public Builder addPermissionChecker(Function<Permission, Uni<Boolean>> function) { | ||
if (built) { | ||
throw new IllegalStateException(); | ||
} | ||
permissionCheckers.add(function); | ||
return this; | ||
} | ||
|
||
public Builder addPermissionCheckers(List<Function<Permission, Uni<Boolean>>> functions) { | ||
if (built) { | ||
throw new IllegalStateException(); | ||
} | ||
if (functions != null) { | ||
permissionCheckers.addAll(functions); | ||
} | ||
return this; | ||
} | ||
|
||
public Builder setAnonymous(boolean anonymous) { | ||
if (built) { | ||
throw new IllegalStateException(); | ||
} | ||
this.anonymous = anonymous; | ||
return this; | ||
} | ||
|
||
public LazyRolesSecurityIdentity build() { | ||
if (principal == null && !anonymous) { | ||
throw new IllegalStateException("Principal is null but anonymous status is false"); | ||
} | ||
|
||
built = true; | ||
return new LazyRolesSecurityIdentity(this); | ||
} | ||
} | ||
} |