diff --git a/src/main/java/cz/cvut/kbss/analysis/dao/FaultTreeDao.java b/src/main/java/cz/cvut/kbss/analysis/dao/FaultTreeDao.java index af19e6b9..5556e37b 100755 --- a/src/main/java/cz/cvut/kbss/analysis/dao/FaultTreeDao.java +++ b/src/main/java/cz/cvut/kbss/analysis/dao/FaultTreeDao.java @@ -6,6 +6,7 @@ import cz.cvut.kbss.analysis.model.FaultTree; import cz.cvut.kbss.analysis.model.FaultTreeSummary; import cz.cvut.kbss.analysis.service.IdentifierService; +import cz.cvut.kbss.analysis.service.security.SecurityUtils; import cz.cvut.kbss.analysis.util.Vocabulary; import cz.cvut.kbss.jopa.model.EntityManager; import cz.cvut.kbss.jopa.model.descriptors.EntityDescriptor; @@ -22,8 +23,8 @@ public class FaultTreeDao extends ManagedEntityDao { @Autowired - protected FaultTreeDao(EntityManager em, PersistenceConf config, IdentifierService identifierService) { - super(FaultTree.class, em, config, identifierService); + protected FaultTreeDao(EntityManager em, PersistenceConf config, IdentifierService identifierService, SecurityUtils securityUtils) { + super(FaultTree.class, em, config, identifierService, securityUtils); } public boolean isRootEvent(URI faultEventIri) { diff --git a/src/main/java/cz/cvut/kbss/analysis/dao/ManagedEntityDao.java b/src/main/java/cz/cvut/kbss/analysis/dao/ManagedEntityDao.java index 0ba04767..4e876218 100644 --- a/src/main/java/cz/cvut/kbss/analysis/dao/ManagedEntityDao.java +++ b/src/main/java/cz/cvut/kbss/analysis/dao/ManagedEntityDao.java @@ -23,8 +23,11 @@ public class ManagedEntityDao extends NamedEntityDao public static URI P_CREATOR = URI.create( DC.Terms.CREATOR); public static URI P_LAST_EDITOR = URI.create(Vocabulary.s_c_editor); - protected ManagedEntityDao(Class type, EntityManager em, PersistenceConf config, IdentifierService identifierService) { + protected final SecurityUtils securityUtils; + + protected ManagedEntityDao(Class type, EntityManager em, PersistenceConf config, IdentifierService identifierService, SecurityUtils securityUtils) { super(type, em, config, identifierService); + this.securityUtils = securityUtils; } @@ -41,7 +44,7 @@ protected void setEntityDescriptor(EntityDescriptor descriptor) { } public void setChangedByContext(URI context, Date date){ - UserReference user = SecurityUtils.currentUserReference(); + UserReference user = securityUtils.getCurrentUserReference(); em.createNativeQuery(""" DELETE{ GRAPH ?context{ diff --git a/src/main/java/cz/cvut/kbss/analysis/dao/SystemDao.java b/src/main/java/cz/cvut/kbss/analysis/dao/SystemDao.java index 6b45a6cf..498c66ec 100755 --- a/src/main/java/cz/cvut/kbss/analysis/dao/SystemDao.java +++ b/src/main/java/cz/cvut/kbss/analysis/dao/SystemDao.java @@ -3,6 +3,7 @@ import cz.cvut.kbss.analysis.config.conf.PersistenceConf; import cz.cvut.kbss.analysis.model.System; import cz.cvut.kbss.analysis.service.IdentifierService; +import cz.cvut.kbss.analysis.service.security.SecurityUtils; import cz.cvut.kbss.jopa.model.EntityManager; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; @@ -11,7 +12,7 @@ public class SystemDao extends ManagedEntityDao { @Autowired - protected SystemDao(EntityManager em, PersistenceConf config, IdentifierService identifierService) { - super(System.class, em, config, identifierService); + protected SystemDao(EntityManager em, PersistenceConf config, IdentifierService identifierService, SecurityUtils securityUtils) { + super(System.class, em, config, identifierService, securityUtils); } } diff --git a/src/main/java/cz/cvut/kbss/analysis/service/ComplexManagedEntityRepositoryService.java b/src/main/java/cz/cvut/kbss/analysis/service/ComplexManagedEntityRepositoryService.java index a5690903..5b173376 100644 --- a/src/main/java/cz/cvut/kbss/analysis/service/ComplexManagedEntityRepositoryService.java +++ b/src/main/java/cz/cvut/kbss/analysis/service/ComplexManagedEntityRepositoryService.java @@ -21,16 +21,18 @@ */ public abstract class ComplexManagedEntityRepositoryService extends BaseRepositoryService { protected final UserDao userDao; + protected final SecurityUtils securityUtils; - public ComplexManagedEntityRepositoryService(Validator validator, UserDao userDao) { + public ComplexManagedEntityRepositoryService(Validator validator, UserDao userDao, SecurityUtils securityUtils) { super(validator); this.userDao = userDao; + this.securityUtils = securityUtils; } @Override protected void preUpdate(@NonNull T instance) { super.preUpdate(instance); - UserReference user = SecurityUtils.currentUserReference(); + UserReference user = securityUtils.getCurrentUserReference(); instance.setLastEditor(user); instance.setModified(new Date()); } @@ -38,7 +40,7 @@ protected void preUpdate(@NonNull T instance) { @Override protected void prePersist(@NonNull T instance) { super.prePersist(instance); - UserReference user = SecurityUtils.currentUserReference(); + UserReference user = securityUtils.getCurrentUserReference(); instance.setCreator(user); instance.setCreated(new Date()); } diff --git a/src/main/java/cz/cvut/kbss/analysis/service/FaultTreeRepositoryService.java b/src/main/java/cz/cvut/kbss/analysis/service/FaultTreeRepositoryService.java index 42307e84..40531b41 100755 --- a/src/main/java/cz/cvut/kbss/analysis/service/FaultTreeRepositoryService.java +++ b/src/main/java/cz/cvut/kbss/analysis/service/FaultTreeRepositoryService.java @@ -11,6 +11,7 @@ import cz.cvut.kbss.analysis.model.fta.FTAMinimalCutSetEvaluation; import cz.cvut.kbss.analysis.model.fta.FtaEventType; import cz.cvut.kbss.analysis.model.fta.GateType; +import cz.cvut.kbss.analysis.service.security.SecurityUtils; import cz.cvut.kbss.analysis.service.util.FaultTreeTraversalUtils; import cz.cvut.kbss.analysis.service.util.Pair; import cz.cvut.kbss.analysis.util.Vocabulary; @@ -37,7 +38,6 @@ public class FaultTreeRepositoryService extends ComplexManagedEntityRepositorySe private final FunctionRepositoryService functionRepositoryService; private final IdentifierService identifierService; - private final ThreadLocal> visitedBehaviors = new ThreadLocal<>(); @Autowired @@ -47,9 +47,10 @@ public FaultTreeRepositoryService(@Qualifier("defaultValidator") Validator valid FaultEventRepositoryService faultEventRepositoryService, FunctionRepositoryService functionRepositoryService, IdentifierService identifierService, - UserDao userDao + UserDao userDao, + SecurityUtils securityUtils ) { - super(validator, userDao); + super(validator, userDao, securityUtils); this.faultTreeDao = faultTreeDao; this.faultEventScenarioDao = faultEventScenarioDao; this.faultEventRepositoryService = faultEventRepositoryService; diff --git a/src/main/java/cz/cvut/kbss/analysis/service/SystemRepositoryService.java b/src/main/java/cz/cvut/kbss/analysis/service/SystemRepositoryService.java index feb81289..911fd1d3 100755 --- a/src/main/java/cz/cvut/kbss/analysis/service/SystemRepositoryService.java +++ b/src/main/java/cz/cvut/kbss/analysis/service/SystemRepositoryService.java @@ -8,6 +8,7 @@ import cz.cvut.kbss.analysis.model.FailureMode; import cz.cvut.kbss.analysis.model.Item; import cz.cvut.kbss.analysis.model.System; +import cz.cvut.kbss.analysis.service.security.SecurityUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -33,10 +34,11 @@ public SystemRepositoryService(@Qualifier("defaultValidator") Validator validato SystemDao systemDao, ComponentRepositoryService componentRepositoryService, ComponentDao componentDao, - UserDao userDao + UserDao userDao, + SecurityUtils securityUtils ) { - super(validator, userDao); + super(validator, userDao, securityUtils); this.systemDao = systemDao; this.componentRepositoryService = componentRepositoryService; this.componentDao = componentDao; diff --git a/src/main/java/cz/cvut/kbss/analysis/service/UserRepositoryService.java b/src/main/java/cz/cvut/kbss/analysis/service/UserRepositoryService.java index 9a4d96ae..269d2746 100755 --- a/src/main/java/cz/cvut/kbss/analysis/service/UserRepositoryService.java +++ b/src/main/java/cz/cvut/kbss/analysis/service/UserRepositoryService.java @@ -24,12 +24,18 @@ public class UserRepositoryService extends BaseRepositoryService { private final UserDao userDao; private final PasswordEncoder passwordEncoder; + private final SecurityUtils securityUtils; @Autowired - public UserRepositoryService(@Qualifier("defaultValidator") Validator validator, UserDao userDao, PasswordEncoder passwordEncoder) { + public UserRepositoryService(@Qualifier("defaultValidator") Validator validator, UserDao userDao, PasswordEncoder passwordEncoder, SecurityUtils securityUtils) { super(validator); this.userDao = userDao; this.passwordEncoder = passwordEncoder; + this.securityUtils = securityUtils; + } + + public User getCurrentUser() { + return securityUtils.getCurrentUser(); } @Override @@ -54,7 +60,7 @@ public URI register(User user) { public void updateCurrent(UserUpdateDTO userUpdate) { log.info("> updateCurrent - {}", userUpdate.getUsername()); - User currentUser = SecurityUtils.currentUser(); + User currentUser = getCurrentUser(); if (!currentUser.getUri().equals(userUpdate.getUri())) { log.warn("< updateCurrent - URIs do not match! {} != {}", currentUser.getUri(), userUpdate.getUri()); throw new LogicViolationException("User update uri does not match current user!"); diff --git a/src/main/java/cz/cvut/kbss/analysis/service/security/SecurityUtils.java b/src/main/java/cz/cvut/kbss/analysis/service/security/SecurityUtils.java index f76bb819..d67558be 100755 --- a/src/main/java/cz/cvut/kbss/analysis/service/security/SecurityUtils.java +++ b/src/main/java/cz/cvut/kbss/analysis/service/security/SecurityUtils.java @@ -1,11 +1,18 @@ package cz.cvut.kbss.analysis.service.security; +import cz.cvut.kbss.analysis.config.conf.SecurityConf; +import cz.cvut.kbss.analysis.dao.UserDao; +import cz.cvut.kbss.analysis.exception.EntityNotFoundException; import cz.cvut.kbss.analysis.model.User; import cz.cvut.kbss.analysis.model.UserReference; -import org.springframework.security.core.Authentication; +import org.springframework.security.authentication.AbstractAuthenticationToken; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextImpl; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.oauth2.core.oidc.OidcUserInfo; +import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.stereotype.Service; /** @@ -13,37 +20,73 @@ */ @Service public class SecurityUtils { - - public SecurityUtils() { + + private final UserDao userDao; + + private final SecurityConf config; + + public SecurityUtils(UserDao userDao, SecurityConf config) { + this.userDao = userDao; + this.config = config; // Ensures security context is propagated to additionally spun threads, e.g., used // by @Async methods - SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL); + SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL); // TODO check what it does } /** + * Gets the currently authenticated user. + * If the user is impersonating another user, the impersonated user is returned. + * Otherwise, the currently authenticated user is returned. * - *

It allows to access the currently logged in user without injecting {@code SecurityUtils} - * as a bean. - * - * @return Currently logged in user + * @return */ - public static User currentUser() { - return (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); + public User getCurrentUser() { + final SecurityContext context = SecurityContextHolder.getContext(); + assert context != null; + final Object principal = context.getAuthentication().getPrincipal(); + if (principal instanceof Jwt) { + return resolveAccountFromOAuthPrincipal((Jwt) principal); + } else { + final String username = context.getAuthentication().getName(); + //TODO impersonalization? + return userDao.findByUsername(username).orElseThrow().copy(); + } + } + public UserReference getCurrentUserReference() { + return new UserReference(getCurrentUser()); } - public static UserReference currentUserReference(){ - return new UserReference(currentUser()); + // TODO map role, but I am not sure which changes in the model when be required if I add addRole method to User + private User resolveAccountFromOAuthPrincipal(Jwt principal) { + final OidcUserInfo userInfo = new OidcUserInfo(principal.getClaims()); +// final List roles = new OidcGrantedAuthoritiesExtractor(config).extractRoles(principal); +// var user = userDao.findByUsername(userInfo.getPreferredUsername()); +// roles.stream().map(r -> "ROLE_" + r).forEach(user::addRole); + return userDao.findByUsername(userInfo.getPreferredUsername()).orElseThrow(() -> EntityNotFoundException.create("User", userInfo.getPreferredUsername())); } + + /** - * Sets authentication to the current thread's security context. + * Sets the current security context to the user represented by the provided user details. + *

+ * Note that this method erases credentials from the provided user details for security reasons. + *

+ * This method should be used only when internal authentication is used. * - * @param authentication Currently logged-in user's authentication + * @param userDetails User details */ - public void setCurrentUser(Authentication authentication) { + public static AbstractAuthenticationToken setCurrentUser(UserDetails userDetails) { + final UsernamePasswordAuthenticationToken token = + UsernamePasswordAuthenticationToken.authenticated(userDetails, userDetails.getPassword(), + userDetails.getAuthorities()); + token.setDetails(userDetails); + token.eraseCredentials(); // Do not pass credentials around + final SecurityContext context = new SecurityContextImpl(); - context.setAuthentication(authentication); + context.setAuthentication(token); SecurityContextHolder.setContext(context); + return token; } }