diff --git a/core/src/main/java/de/bstly/we/businesslogic/UserAliasManager.java b/core/src/main/java/de/bstly/we/businesslogic/UserAliasManager.java index 6be4bc2..da3b5e0 100644 --- a/core/src/main/java/de/bstly/we/businesslogic/UserAliasManager.java +++ b/core/src/main/java/de/bstly/we/businesslogic/UserAliasManager.java @@ -64,6 +64,16 @@ public class UserAliasManager implements UserDataProvider { return userAliasRepository.findOne(qUserAlias.alias.eq(alias)).orElse(null); } + /** + * + * @param userId + * @param alias + * @return + */ + public boolean hasAlias(Long userId, String alias) { + return userAliasRepository.exists(qUserAlias.target.eq(userId).and(qUserAlias.alias.eq(alias))); + } + /** * Gets the all by target. * diff --git a/core/src/main/java/de/bstly/we/controller/AuthenticationController.java b/core/src/main/java/de/bstly/we/controller/AuthenticationController.java index a4c44e5..daa9cb9 100755 --- a/core/src/main/java/de/bstly/we/controller/AuthenticationController.java +++ b/core/src/main/java/de/bstly/we/controller/AuthenticationController.java @@ -4,6 +4,7 @@ package de.bstly.we.controller; import java.io.IOException; +import java.util.Optional; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -13,7 +14,9 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.validation.Errors; import org.springframework.web.bind.annotation.GetMapping; @@ -22,12 +25,14 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import de.bstly.we.businesslogic.UserAliasManager; import de.bstly.we.businesslogic.UserManager; import de.bstly.we.controller.model.PasswordResetModel; import de.bstly.we.controller.support.EntityResponseStatusException; import de.bstly.we.controller.support.RequestBodyErrors; import de.bstly.we.controller.validation.PasswordModelValidator; import de.bstly.we.model.User; +import de.bstly.we.security.model.LocalUserDetails; /** * The Class AuthenticationController. @@ -40,6 +45,8 @@ public class AuthenticationController extends BaseController { private UserManager userManager; @Autowired private PasswordModelValidator passwordModelValidator; + @Autowired + private UserAliasManager userAliasManager; /** * Me. @@ -102,7 +109,25 @@ public class AuthenticationController extends BaseController { user = userManager.setPassword(user.getId(), passwordResetModel.getPassword()); user.setResetToken(null); userManager.update(user); + } + @PreAuthorize("authentication.authenticated") + @PostMapping("/alias") + public void setAlias(@RequestBody Optional alias, HttpServletRequest req, HttpServletResponse resp) { + if (alias.isPresent() && !userAliasManager.hasAlias(getCurrentUserId(), alias.get())) { + throw new EntityResponseStatusException(HttpStatus.FORBIDDEN); + } + + SecurityContext context = SecurityContextHolder.getContext(); + Authentication authentication = context.getAuthentication(); + + if (authentication != null && authentication.getPrincipal() instanceof LocalUserDetails) { + LocalUserDetails details = (LocalUserDetails) authentication.getPrincipal(); + details.setAlias(alias.orElse(null)); + Authentication newAuthentication = new UsernamePasswordAuthenticationToken(details, + authentication.getCredentials(), details.getAuthorities()); + context.setAuthentication(newAuthentication); + } } } diff --git a/core/src/main/java/de/bstly/we/controller/PermissionManagementController.java b/core/src/main/java/de/bstly/we/controller/PermissionManagementController.java index bc0ba35..3e56f24 100644 --- a/core/src/main/java/de/bstly/we/controller/PermissionManagementController.java +++ b/core/src/main/java/de/bstly/we/controller/PermissionManagementController.java @@ -3,6 +3,7 @@ */ package de.bstly.we.controller; +import java.time.Instant; import java.util.List; import java.util.Optional; @@ -92,6 +93,23 @@ public class PermissionManagementController extends BaseController { permission.getStarts(), permission.getExpires()); } + @PreAuthorize("hasRole('ROLE_ADMIN')") + @PostMapping("list") + public List createPermissions(@RequestBody List permissions, + @RequestParam("target") Optional target, @RequestParam("starts") Optional starts, + @RequestParam("expires") Optional expires) { + List result = Lists.newArrayList(); + for (Permission permission : permissions) { + permission.setId(null); + permission.setTarget(target.orElse(permission.getTarget())); + permission.setStarts(starts.orElse(permission.getStarts())); + permission.setExpires(expires.orElse(permission.getExpires())); + result.add(permissionManager.create(permission.getTarget(), permission.getName(), permission.isAddon(), + permission.getStarts(), permission.getExpires())); + } + return result; + } + /** * Update permission. * @@ -116,13 +134,15 @@ public class PermissionManagementController extends BaseController { */ @PreAuthorize("hasRole('ROLE_ADMIN')") @PatchMapping("list") - public List updatePermissions(@RequestBody List permissions) { + public List updatePermissions(@RequestBody List permissions, + @RequestParam("starts") Optional starts, @RequestParam("expires") Optional expires) { List result = Lists.newArrayList(); for (Permission permission : permissions) { if (permission.getId() == null) { throw new EntityResponseStatusException(HttpStatus.CONFLICT); } - + permission.setStarts(starts.orElse(permission.getStarts())); + permission.setExpires(expires.orElse(permission.getExpires())); result.add(permissionManager.update(permission)); } return result; diff --git a/core/src/main/java/de/bstly/we/controller/QuotaManagementController.java b/core/src/main/java/de/bstly/we/controller/QuotaManagementController.java index 3f6d503..bb4b20e 100644 --- a/core/src/main/java/de/bstly/we/controller/QuotaManagementController.java +++ b/core/src/main/java/de/bstly/we/controller/QuotaManagementController.java @@ -109,6 +109,22 @@ public class QuotaManagementController extends BaseController { quota.isDisposable()); } + @PreAuthorize("hasRole('ROLE_ADMIN')") + @PostMapping("/list") + public List createQuotaList(@RequestBody List quotas, @RequestParam("target") Optional target, + @RequestParam("value") Optional value) { + List result = Lists.newArrayList(); + for (Quota quota : quotas) { + quota.setId(null); + quota.setTarget(target.orElse(quota.getTarget())); + quota.setValue(value.orElse(quota.getValue())); + result.add(quotaManager.create(quota.getTarget(), quota.getName(), quota.getValue(), quota.getUnit(), + quota.isDisposable())); + } + + return result; + } + /** * Update quota. * @@ -133,7 +149,7 @@ public class QuotaManagementController extends BaseController { */ @PreAuthorize("hasRole('ROLE_ADMIN')") @PatchMapping("/list") - public List updateQuotaList(@RequestBody List quotas) { + public List updateQuotaList(@RequestBody List quotas, @RequestParam("value") Optional value) { List result = Lists.newArrayList(); @@ -141,6 +157,7 @@ public class QuotaManagementController extends BaseController { if (quotaManager.get(quota.getTarget(), quota.getName()) == null) { throw new EntityResponseStatusException(HttpStatus.CONFLICT); } + quota.setValue(value.orElse(quota.getValue())); result.add(quotaManager.update(quota)); } diff --git a/core/src/main/java/de/bstly/we/security/model/LocalUserDetails.java b/core/src/main/java/de/bstly/we/security/model/LocalUserDetails.java index 68072b4..7c6a723 100755 --- a/core/src/main/java/de/bstly/we/security/model/LocalUserDetails.java +++ b/core/src/main/java/de/bstly/we/security/model/LocalUserDetails.java @@ -18,6 +18,7 @@ public class LocalUserDetails extends User { */ private static final long serialVersionUID = 1L; private Long userId; + private String alias; /** * Instantiates a new local user details. @@ -52,4 +53,18 @@ public class LocalUserDetails extends User { this.userId = userId; } + /** + * @return the alias + */ + public String getAlias() { + return alias; + } + + /** + * @param alias the alias to set + */ + public void setAlias(String alias) { + this.alias = alias; + } + } diff --git a/oidc/src/main/java/de/bstly/we/oidc/businesslogic/OidcAuthorizationManager.java b/oidc/src/main/java/de/bstly/we/oidc/businesslogic/OidcAuthorizationManager.java index d8913c1..ff8345b 100644 --- a/oidc/src/main/java/de/bstly/we/oidc/businesslogic/OidcAuthorizationManager.java +++ b/oidc/src/main/java/de/bstly/we/oidc/businesslogic/OidcAuthorizationManager.java @@ -120,6 +120,16 @@ public class OidcAuthorizationManager { return authorizationCode; } + /** + * + * @param authorizationCode + * @return + */ + public OidcAuthorizationCode setCode(OidcAuthorizationCode authorizationCode) { + authorizationCodes.put(authorizationCode.getCode(), authorizationCode); + return authorizationCode; + } + /** * Gets the code. * diff --git a/oidc/src/main/java/de/bstly/we/oidc/businesslogic/OidcTokenManager.java b/oidc/src/main/java/de/bstly/we/oidc/businesslogic/OidcTokenManager.java index 28881df..56413d5 100644 --- a/oidc/src/main/java/de/bstly/we/oidc/businesslogic/OidcTokenManager.java +++ b/oidc/src/main/java/de/bstly/we/oidc/businesslogic/OidcTokenManager.java @@ -144,6 +144,7 @@ public class OidcTokenManager implements SmartInitializingSingleton { */ public OidcToken createToken(OidcClient client, Long userId, String refreshToken) { OidcToken token = new OidcToken(); + token.setClient(client.getId()); token.setUserId(userId); token.setAccessToken(RandomStringUtils.random(ACCESS_TOKEN_LENGTH, true, true)); if (StringUtils.hasText(refreshToken)) { @@ -166,8 +167,8 @@ public class OidcTokenManager implements SmartInitializingSingleton { * @return the oidc token * @throws JOSEException the JOSE exception */ - public OidcToken createTokenWithIdToken(OidcClient client, Long userId, String nonce, Set scopes, - String issuer, String sid) throws JOSEException { + public OidcToken createTokenWithIdToken(OidcClient client, Long userId, String alias, String nonce, + Set scopes, String issuer, String sid) throws JOSEException { User user = userManager.get(userId); Assert.notNull(user, "User does not exist!"); @@ -175,10 +176,11 @@ public class OidcTokenManager implements SmartInitializingSingleton { client.getAuthorizationGrantTypes().contains(OidcAuthorizationGrantType.refresh_token)); token.setUserId(user.getId()); + token.setAlias(alias); token.setAccessToken(RandomStringUtils.random(ACCESS_TOKEN_LENGTH, true, true)); token.setExpiresIn(client.getTokenLifetime()); - Builder claimsSetBuilder = createUserClaims(client, user); + Builder claimsSetBuilder = createUserClaims(client, user, alias); claimsSetBuilder.issuer(issuer); claimsSetBuilder.audience(client.getClientId()); @@ -219,13 +221,17 @@ public class OidcTokenManager implements SmartInitializingSingleton { * @param user the user * @return the builder */ - public Builder createUserClaims(OidcClient client, User user) { + public Builder createUserClaims(OidcClient client, User user, String alias) { Builder claimsSetBuilder = new Builder(); claimsSetBuilder.subject(String.valueOf(user.getId())); String username = user.getUsername(); + if (StringUtils.hasText(alias) && client.isAliasAllowed()) { + username = alias; + } + claimsSetBuilder.claim("name", username); claimsSetBuilder.claim("username", username); claimsSetBuilder.claim("preferred_username", username); diff --git a/oidc/src/main/java/de/bstly/we/oidc/businesslogic/model/OidcAuthorizationCode.java b/oidc/src/main/java/de/bstly/we/oidc/businesslogic/model/OidcAuthorizationCode.java index 27fa80c..457c796 100644 --- a/oidc/src/main/java/de/bstly/we/oidc/businesslogic/model/OidcAuthorizationCode.java +++ b/oidc/src/main/java/de/bstly/we/oidc/businesslogic/model/OidcAuthorizationCode.java @@ -26,6 +26,7 @@ public class OidcAuthorizationCode { private final Long userId; private final String nonce; private String springSession; + private String alias; /** * Instantiates a new oidc authorization code. @@ -127,4 +128,18 @@ public class OidcAuthorizationCode { this.springSession = springSession; } + /** + * @return the alias + */ + public String getAlias() { + return alias; + } + + /** + * @param alias the alias to set + */ + public void setAlias(String alias) { + this.alias = alias; + } + } diff --git a/oidc/src/main/java/de/bstly/we/oidc/controller/OidcAuthorizationController.java b/oidc/src/main/java/de/bstly/we/oidc/controller/OidcAuthorizationController.java index 8c3bb1a..a64cdcb 100644 --- a/oidc/src/main/java/de/bstly/we/oidc/controller/OidcAuthorizationController.java +++ b/oidc/src/main/java/de/bstly/we/oidc/controller/OidcAuthorizationController.java @@ -30,6 +30,7 @@ import com.google.common.collect.Sets; import de.bstly.we.businesslogic.PermissionManager; import de.bstly.we.businesslogic.Permissions; +import de.bstly.we.businesslogic.UserAliasManager; import de.bstly.we.oidc.businesslogic.OidcAuthorizationManager; import de.bstly.we.oidc.businesslogic.OidcClientManager; import de.bstly.we.oidc.businesslogic.exception.InvalidAuthorizationRequestException; @@ -56,6 +57,8 @@ public class OidcAuthorizationController { private OidcClientManager oidcClientManager; @Autowired private OidcAuthorizationManager oidcAuthorizationManager; + @Autowired + private UserAliasManager userAliasManager; @Value("${oidcAuthorizationFormUrl:/oidc/authorize/form}") private String authorizationFormUrl; @@ -169,16 +172,30 @@ public class OidcAuthorizationController { "code does not match user", state); } + if (StringUtils.hasText(principal.getAlias())) { + if (!userAliasManager.hasAlias(principal.getUserId(), principal.getAlias())) { + throw new InvalidAuthorizationRequestException(redirectUri, + OidcAuthorizationErrorCode.ACCOUNT_SELECTION_REQUIRED, "alias does not match user", state); + } + authorizationCode.setAlias(principal.getAlias()); + oidcAuthorizationManager.setCode(authorizationCode); + } + oidcAuthorizationManager.authorize(client.getId(), principal.getUserId(), scopes); } else { if (client.isAuthorize() && !oidcAuthorizationManager.isAuthorized(client.getId(), principal.getUserId(), scopes) - || prompts.contains("consent")) { + || prompts.contains("consent") + || client.isAliasAllowed() && !userAliasManager.getAllByTarget(principal.getUserId()).isEmpty()) { uriBuilder = UriComponentsBuilder.fromUriString(authorizationFormUrl); uriBuilder.queryParam("client_id", clientId); uriBuilder.queryParam("response_type", responseType); uriBuilder.queryParam("redirect_uri", redirectUri); uriBuilder.queryParam("scope", scope); + uriBuilder.queryParam("authorize", client.isAuthorize() + && !oidcAuthorizationManager.isAuthorized(client.getId(), principal.getUserId(), scopes)); + uriBuilder.queryParam("alias", + client.isAliasAllowed() && !userAliasManager.getAllByTarget(principal.getUserId()).isEmpty()); if (StringUtils.hasText(nonce)) { uriBuilder.queryParam("nonce", nonce); diff --git a/oidc/src/main/java/de/bstly/we/oidc/controller/OidcTokenController.java b/oidc/src/main/java/de/bstly/we/oidc/controller/OidcTokenController.java index d9fc948..9dd73a7 100644 --- a/oidc/src/main/java/de/bstly/we/oidc/controller/OidcTokenController.java +++ b/oidc/src/main/java/de/bstly/we/oidc/controller/OidcTokenController.java @@ -176,7 +176,7 @@ public class OidcTokenController { try { String sid = oidcSessionManager.createSid(); token = oidcTokenManager.createTokenWithIdToken(client, authorizationCode.getUserId(), - authorizationCode.getNonce(), scopes, issuer, sid); + authorizationCode.getAlias(), authorizationCode.getNonce(), scopes, issuer, sid); oidcSessionManager.createSession(client.getId(), authorizationCode.getUserId(), token.getIdToken(), sid, authorizationCode.getSpringSession()); } catch (JOSEException e) { diff --git a/oidc/src/main/java/de/bstly/we/oidc/controller/OidcUserInfoController.java b/oidc/src/main/java/de/bstly/we/oidc/controller/OidcUserInfoController.java index 870223d..819e203 100644 --- a/oidc/src/main/java/de/bstly/we/oidc/controller/OidcUserInfoController.java +++ b/oidc/src/main/java/de/bstly/we/oidc/controller/OidcUserInfoController.java @@ -23,6 +23,7 @@ import de.bstly.we.oidc.businesslogic.OidcClientManager; import de.bstly.we.oidc.businesslogic.OidcTokenManager; import de.bstly.we.oidc.model.OidcClient; import de.bstly.we.oidc.model.OidcToken; +import de.bstly.we.security.model.LocalUserDetails; /** * The Class OidcUserInfoController. @@ -50,6 +51,13 @@ public class OidcUserInfoController extends BaseController { Authentication auth = SecurityContextHolder.getContext().getAuthentication(); Long userId = getCurrentUserId(); + + String alias = null; + + if (auth.getPrincipal() != null && auth.getPrincipal() instanceof LocalUserDetails) { + alias = ((LocalUserDetails) auth.getPrincipal()).getAlias(); + } + OidcClient client = null; if (!auth.isAuthenticated()) { if (authorizationHeader == null) { @@ -63,6 +71,7 @@ public class OidcUserInfoController extends BaseController { throw new EntityResponseStatusException(HttpStatus.UNAUTHORIZED); } userId = token.getUserId(); + alias = token.getAlias(); client = oidcClientManager.get(token.getClient()); } @@ -72,7 +81,7 @@ public class OidcUserInfoController extends BaseController { throw new EntityResponseStatusException(HttpStatus.CONFLICT); } - Builder claimsSetBuilder = oidcTokenManager.createUserClaims(client, user); + Builder claimsSetBuilder = oidcTokenManager.createUserClaims(client, user, alias); throw new EntityResponseStatusException(claimsSetBuilder.build().toJSONObject(), HttpStatus.OK); diff --git a/oidc/src/main/java/de/bstly/we/oidc/model/OidcClient.java b/oidc/src/main/java/de/bstly/we/oidc/model/OidcClient.java index 8bc05a8..820deb5 100644 --- a/oidc/src/main/java/de/bstly/we/oidc/model/OidcClient.java +++ b/oidc/src/main/java/de/bstly/we/oidc/model/OidcClient.java @@ -71,6 +71,8 @@ public class OidcClient { private boolean backchannelLogoutSessionRequired; @Column(name = "authorize", columnDefinition = "boolean default false") private boolean authorize; + @Column(name = "alias_allowed", columnDefinition = "boolean default false") + private boolean aliasAllowed; @Column(name = "always_permitted", columnDefinition = "boolean default false") private boolean alwaysPermitted; @Column(name = "category") @@ -348,6 +350,20 @@ public class OidcClient { this.authorize = authorize; } + /** + * @return the aliasAllowed + */ + public boolean isAliasAllowed() { + return aliasAllowed; + } + + /** + * @param aliasAllowed the aliasAllowed to set + */ + public void setAliasAllowed(boolean aliasAllowed) { + this.aliasAllowed = aliasAllowed; + } + /** * Checks if is always permitted. * diff --git a/oidc/src/main/java/de/bstly/we/oidc/model/OidcToken.java b/oidc/src/main/java/de/bstly/we/oidc/model/OidcToken.java index e1fe9d5..d000d39 100644 --- a/oidc/src/main/java/de/bstly/we/oidc/model/OidcToken.java +++ b/oidc/src/main/java/de/bstly/we/oidc/model/OidcToken.java @@ -34,6 +34,8 @@ public class OidcToken { private Instant created; @Column(name = "user_id") private Long userId; + @Column(name = "alias") + private String alias; @Column(name = "client_id") private Long client; @Column(name = "access_token") @@ -103,6 +105,20 @@ public class OidcToken { this.userId = userId; } + /** + * @return the alias + */ + public String getAlias() { + return alias; + } + + /** + * @param alias the alias to set + */ + public void setAlias(String alias) { + this.alias = alias; + } + /** * Gets the client. * diff --git a/pom.xml b/pom.xml index 500628d..5839dc1 100755 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ UTF-8 11 2.19.0 - 2.0.2-SNAPSHOT + 2.0.3-SNAPSHOT