oidc alias login

This commit is contained in:
_Bastler 2022-12-27 20:42:23 +01:00
parent f618d1c1a7
commit e20266f147
9 changed files with 109 additions and 31 deletions

View File

@ -20,8 +20,11 @@ import com.google.common.collect.Lists;
import de.bstly.we.model.Permission; import de.bstly.we.model.Permission;
import de.bstly.we.model.QPermission; import de.bstly.we.model.QPermission;
import de.bstly.we.model.QUser; import de.bstly.we.model.QUser;
import de.bstly.we.model.QUserAlias;
import de.bstly.we.model.User; import de.bstly.we.model.User;
import de.bstly.we.model.UserAlias;
import de.bstly.we.repository.PermissionRepository; import de.bstly.we.repository.PermissionRepository;
import de.bstly.we.repository.UserAliasRepository;
import de.bstly.we.repository.UserRepository; import de.bstly.we.repository.UserRepository;
import de.bstly.we.security.model.LocalUserDetails; import de.bstly.we.security.model.LocalUserDetails;
@ -35,9 +38,12 @@ public class LocalUserDetailsService implements UserDetailsService {
private UserRepository userRepository; private UserRepository userRepository;
@Autowired @Autowired
private PermissionRepository permissionRepository; private PermissionRepository permissionRepository;
@Autowired
private UserAliasRepository userAliasRepository;
private QUser qUser = QUser.user; private QUser qUser = QUser.user;
private QPermission qPermission = QPermission.permission; private QPermission qPermission = QPermission.permission;
private QUserAlias qUserAlias = QUserAlias.userAlias;
/* /*
* @see org.springframework.security.core.userdetails.UserDetailsService# * @see org.springframework.security.core.userdetails.UserDetailsService#
@ -46,6 +52,16 @@ public class LocalUserDetailsService implements UserDetailsService {
@Override @Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findOne(qUser.username.equalsIgnoreCase(username)).orElse(null); User user = userRepository.findOne(qUser.username.equalsIgnoreCase(username)).orElse(null);
String alias = null;
if (user == null) {
UserAlias userAlias = userAliasRepository.findOne(qUserAlias.alias.eq(username)).orElse(null);
if (userAlias != null) {
user = userRepository.findOne(qUser.id.eq(userAlias.getTarget())).orElse(null);
alias = username;
}
}
if (user != null) { if (user != null) {
String password = userRepository.findById(user.getId()).get().getPasswordHash(); String password = userRepository.findById(user.getId()).get().getPasswordHash();
@ -64,26 +80,8 @@ public class LocalUserDetailsService implements UserDetailsService {
Set<GrantedAuthority> authorities = getAuthoritiesForUser(user.getId()); Set<GrantedAuthority> authorities = getAuthoritiesForUser(user.getId());
// ignore and use status
// boolean onlyAddons = true;
// for (Permission permission : permissionManager
// .getNotExpiresByTargetIgnoreStart(user.getId())) {
// if (!permission.isAddon()) {
// onlyAddons = false;
// break;
// }
// }
//
// if (authorities.isEmpty()) {
// throw new AccountExpiredException("User is expired: " + username);
// }
//
// if (onlyAddons) {
// throw new AccountExpiredException("User is expired: " + username);
// }
// Create user details // Create user details
LocalUserDetails userDetails = new LocalUserDetails(user.getId(), user.getUsername(), password, LocalUserDetails userDetails = new LocalUserDetails(user.getId(), user.getUsername(), alias, password,
authorities); authorities);
return userDetails; return userDetails;

View File

@ -55,7 +55,7 @@ public class LocalAnonymousAuthenticationFilter extends AnonymousAuthenticationF
@Override @Override
protected Authentication createAuthentication(HttpServletRequest request) { protected Authentication createAuthentication(HttpServletRequest request) {
Authentication authentication = new LocalAnonymousAuthenticationToken( Authentication authentication = new LocalAnonymousAuthenticationToken(
new LocalUserDetails(-1L, LocalAnonymousAuthenticationToken.ANONYMOUS_USERNAME, "", new LocalUserDetails(-1L, LocalAnonymousAuthenticationToken.ANONYMOUS_USERNAME, "", "",
LocalAnonymousAuthenticationToken.AUTHORITIES)); LocalAnonymousAuthenticationToken.AUTHORITIES));
authentication.setAuthenticated(false); authentication.setAuthenticated(false);
return authentication; return authentication;

View File

@ -28,11 +28,12 @@ public class LocalUserDetails extends User {
* @param password the password * @param password the password
* @param authorities the authorities * @param authorities the authorities
*/ */
public LocalUserDetails(Long userId, String username, String password, public LocalUserDetails(Long userId, String username, String alias, String password,
Collection<? extends GrantedAuthority> authorities) { Collection<? extends GrantedAuthority> authorities) {
// Super // Super
super(username, password, authorities); super(username, password, authorities);
this.userId = userId; this.userId = userId;
this.alias = alias;
} }
/** /**

View File

@ -4,12 +4,14 @@
package de.bstly.we.oidc.businesslogic; package de.bstly.we.oidc.businesslogic;
import java.net.URI; import java.net.URI;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
@ -34,6 +36,10 @@ public class OidcAuthorizationManager {
*/ */
private final Map<String, OidcAuthorizationCode> authorizationCodes = Maps.newHashMap(); private final Map<String, OidcAuthorizationCode> authorizationCodes = Maps.newHashMap();
public List<OidcAuthorization> getAuthorizations(Long subject) {
return Lists.newArrayList(oidcAuthorizationRepository.findAll(qOidcAuthorization.subject.eq(subject)));
}
/** /**
* Gets the authorization. * Gets the authorization.
* *

View File

@ -80,6 +80,8 @@ public class OidcTokenManager implements SmartInitializingSingleton {
private QuotaManager quotaManager; private QuotaManager quotaManager;
@Autowired @Autowired
private JwtKeyManager jwtKeyManager; private JwtKeyManager jwtKeyManager;
@Autowired
private OidcClientManager oidcClientManager;
private QOidcToken qOidcToken = QOidcToken.oidcToken; private QOidcToken qOidcToken = QOidcToken.oidcToken;
/* /*
@ -230,6 +232,9 @@ public class OidcTokenManager implements SmartInitializingSingleton {
if (StringUtils.hasText(alias) && client.isAliasAllowed()) { if (StringUtils.hasText(alias) && client.isAliasAllowed()) {
username = alias; username = alias;
if (client.isAliasSubject()) {
claimsSetBuilder.subject(alias);
}
} }
claimsSetBuilder.claim("name", username); claimsSetBuilder.claim("name", username);
@ -265,7 +270,10 @@ public class OidcTokenManager implements SmartInitializingSingleton {
Map<String, String> permissions = Maps.newHashMap(); Map<String, String> permissions = Maps.newHashMap();
for (Permission permission : permissionManager.getNotExpiresByTarget(user.getId())) { for (Permission permission : permissionManager.getNotExpiresByTarget(user.getId())) {
permissions.put(permission.getName(), permission.getExpires().toString()); if (oidcClientManager.getByClientName(permission.getName()) == null
|| permission.getName().equals(client.getClientName())) {
permissions.put(permission.getName(), permission.getExpires().toString());
}
} }
if (!permissions.isEmpty()) { if (!permissions.isEmpty()) {
@ -274,7 +282,9 @@ public class OidcTokenManager implements SmartInitializingSingleton {
Map<String, String> quotas = Maps.newHashMap(); Map<String, String> quotas = Maps.newHashMap();
for (Quota quota : quotaManager.getNotExpiresByTarget(user.getId())) { for (Quota quota : quotaManager.getNotExpiresByTarget(user.getId())) {
quotas.put(quota.getName(), String.valueOf(quota.getValue()) + quota.getUnit()); if (quota.getName().equals(client.getClientName()) && (!username.equals(alias) || client.isAliasQuota())) {
quotas.put(quota.getName(), String.valueOf(quota.getValue()) + quota.getUnit());
}
} }
if (!quotas.isEmpty()) { if (!quotas.isEmpty()) {

View File

@ -5,6 +5,7 @@ package de.bstly.we.oidc.controller;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.util.List;
import java.util.Set; import java.util.Set;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -14,23 +15,29 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.util.UriComponentsBuilder; import org.springframework.web.util.UriComponentsBuilder;
import com.beust.jcommander.internal.Lists;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import de.bstly.we.businesslogic.PermissionManager; import de.bstly.we.businesslogic.PermissionManager;
import de.bstly.we.businesslogic.Permissions; import de.bstly.we.businesslogic.Permissions;
import de.bstly.we.businesslogic.UserAliasManager; import de.bstly.we.businesslogic.UserAliasManager;
import de.bstly.we.controller.BaseController;
import de.bstly.we.controller.support.EntityResponseStatusException;
import de.bstly.we.oidc.businesslogic.OidcAuthorizationManager; import de.bstly.we.oidc.businesslogic.OidcAuthorizationManager;
import de.bstly.we.oidc.businesslogic.OidcClientManager; import de.bstly.we.oidc.businesslogic.OidcClientManager;
import de.bstly.we.oidc.businesslogic.exception.InvalidAuthorizationRequestException; import de.bstly.we.oidc.businesslogic.exception.InvalidAuthorizationRequestException;
@ -39,6 +46,8 @@ import de.bstly.we.oidc.businesslogic.model.OidcAuthorizationErrorCode;
import de.bstly.we.oidc.businesslogic.model.OidcAuthorizationGrantType; import de.bstly.we.oidc.businesslogic.model.OidcAuthorizationGrantType;
import de.bstly.we.oidc.businesslogic.model.OidcAuthorizationRequest; import de.bstly.we.oidc.businesslogic.model.OidcAuthorizationRequest;
import de.bstly.we.oidc.businesslogic.model.OidcAuthorizationResponseType; import de.bstly.we.oidc.businesslogic.model.OidcAuthorizationResponseType;
import de.bstly.we.oidc.controller.model.OidcClientInfo;
import de.bstly.we.oidc.model.OidcAuthorization;
import de.bstly.we.oidc.model.OidcClient; import de.bstly.we.oidc.model.OidcClient;
import de.bstly.we.security.model.LocalUserDetails; import de.bstly.we.security.model.LocalUserDetails;
@ -47,7 +56,7 @@ import de.bstly.we.security.model.LocalUserDetails;
*/ */
@RequestMapping("/oidc/authorize") @RequestMapping("/oidc/authorize")
@RestController @RestController
public class OidcAuthorizationController { public class OidcAuthorizationController extends BaseController {
private Logger logger = LoggerFactory.getLogger(OidcAuthorizationController.class); private Logger logger = LoggerFactory.getLogger(OidcAuthorizationController.class);
@ -318,4 +327,28 @@ public class OidcAuthorizationController {
response.sendRedirect(uriBuilder.build().toUriString()); response.sendRedirect(uriBuilder.build().toUriString());
} }
@PreAuthorize("isAuthenticated()")
@GetMapping("history")
public List<OidcClientInfo> getAuthorizations() {
List<OidcClientInfo> result = Lists.newArrayList();
for (OidcAuthorization authorization : oidcAuthorizationManager.getAuthorizations(getCurrentUserId())) {
OidcClient client = oidcClientManager.get(authorization.getClient());
result.add(oidcClientManager.getClientInfo(client, getCurrentUserId()));
}
return result;
}
@PreAuthorize("isAuthenticated()")
@DeleteMapping("{name}")
public void revokeAuthorization(@PathVariable("name") String clientName) {
OidcClient client = oidcClientManager.getByClientName(clientName);
if (client == null
|| !oidcAuthorizationManager.isAuthorized(client.getId(), getCurrentUserId(), client.getScopes())) {
throw new EntityResponseStatusException(HttpStatus.CONFLICT);
}
oidcAuthorizationManager.removeAuthorization(client.getId(), getCurrentUserId());
}
} }

View File

@ -35,7 +35,7 @@ import de.bstly.we.oidc.repository.OidcClientRepository;
*/ */
@RestController @RestController
@RequestMapping("/oidc/clients") @RequestMapping("/oidc/clients")
public class OIDCClientController extends BaseController { public class OidcClientController extends BaseController {
@Autowired @Autowired
private OidcClientManager registeredClientService; private OidcClientManager registeredClientService;

View File

@ -60,8 +60,6 @@ public class OidcSessionController extends BaseController {
@Autowired @Autowired
private UserManager userManager; private UserManager userManager;
@Autowired @Autowired
private OidcClientManager oidcRegisteredClientManager;
@Autowired
private PermissionManager permissionManager; private PermissionManager permissionManager;
@Value("${loginUrl:/login}") @Value("${loginUrl:/login}")
@ -80,13 +78,13 @@ public class OidcSessionController extends BaseController {
Long oidcClientId = session.getClientId(); Long oidcClientId = session.getClientId();
OidcClientInfo clientInfo = clients.get(oidcClientId); OidcClientInfo clientInfo = clients.get(oidcClientId);
if (clientInfo == null) { if (clientInfo == null) {
OidcClient client = oidcRegisteredClientManager.get(oidcClientId); OidcClient client = oidcClientManager.get(oidcClientId);
if (client == null || !client.isAlwaysPermitted() if (client == null || !client.isAlwaysPermitted()
&& (!permissionManager.hasPermission(getCurrentUserId(), client.getClientName()) && (!permissionManager.hasPermission(getCurrentUserId(), client.getClientName())
|| !permissionManager.hasPermission(getCurrentUserId(), Permissions.ROLE_ADMIN))) { || !permissionManager.hasPermission(getCurrentUserId(), Permissions.ROLE_ADMIN))) {
continue; continue;
} }
clientInfo = oidcRegisteredClientManager.getClientInfo(client, getCurrentUserId()); clientInfo = oidcClientManager.getClientInfo(client, getCurrentUserId());
} }
clientInfo.getSessions().add(session); clientInfo.getSessions().add(session);
clients.put(session.getClientId(), clientInfo); clients.put(session.getClientId(), clientInfo);
@ -104,14 +102,14 @@ public class OidcSessionController extends BaseController {
@PreAuthorize("authentication.authenticated") @PreAuthorize("authentication.authenticated")
@GetMapping("/{target}") @GetMapping("/{target}")
public OidcClientInfo getSessionsForTarget(@PathVariable("target") Long target) { public OidcClientInfo getSessionsForTarget(@PathVariable("target") Long target) {
OidcClient client = oidcRegisteredClientManager.get(target); OidcClient client = oidcClientManager.get(target);
if (client == null || !client.isAlwaysPermitted() if (client == null || !client.isAlwaysPermitted()
&& (!permissionManager.hasPermission(getCurrentUserId(), client.getClientName()) && (!permissionManager.hasPermission(getCurrentUserId(), client.getClientName())
|| !permissionManager.hasPermission(getCurrentUserId(), Permissions.ROLE_ADMIN))) { || !permissionManager.hasPermission(getCurrentUserId(), Permissions.ROLE_ADMIN))) {
throw new EntityResponseStatusException(HttpStatus.FORBIDDEN); throw new EntityResponseStatusException(HttpStatus.FORBIDDEN);
} }
OidcClientInfo clientInfo = oidcRegisteredClientManager.getClientInfo(client, getCurrentUserId()); OidcClientInfo clientInfo = oidcClientManager.getClientInfo(client, getCurrentUserId());
clientInfo.getSessions().addAll(oidcSessionManager.getAllByTargetAndSubject(target, getCurrentUserId())); clientInfo.getSessions().addAll(oidcSessionManager.getAllByTargetAndSubject(target, getCurrentUserId()));
return clientInfo; return clientInfo;
} }

View File

@ -73,6 +73,10 @@ public class OidcClient {
private boolean authorize; private boolean authorize;
@Column(name = "alias_allowed", columnDefinition = "boolean default false") @Column(name = "alias_allowed", columnDefinition = "boolean default false")
private boolean aliasAllowed; private boolean aliasAllowed;
@Column(name = "alias_quota", columnDefinition = "boolean default false")
private boolean aliasQuota;
@Column(name = "alias_sub", columnDefinition = "boolean default true")
private boolean aliasSubject;
@Column(name = "always_permitted", columnDefinition = "boolean default false") @Column(name = "always_permitted", columnDefinition = "boolean default false")
private boolean alwaysPermitted; private boolean alwaysPermitted;
@Column(name = "category") @Column(name = "category")
@ -364,6 +368,34 @@ public class OidcClient {
this.aliasAllowed = aliasAllowed; this.aliasAllowed = aliasAllowed;
} }
/**
* @return the aliasQuota
*/
public boolean isAliasQuota() {
return aliasQuota;
}
/**
* @param aliasQuota the aliasQuota to set
*/
public void setAliasQuota(boolean aliasQuota) {
this.aliasQuota = aliasQuota;
}
/**
* @return the aliasSubject
*/
public boolean isAliasSubject() {
return aliasSubject;
}
/**
* @param aliasSubject the aliasSubject to set
*/
public void setAliasSubject(boolean aliasSubject) {
this.aliasSubject = aliasSubject;
}
/** /**
* Checks if is always permitted. * Checks if is always permitted.
* *