From 615953117d1e64efc3d935a02f59bc3edccf6c5d Mon Sep 17 00:00:00 2001 From: Andreas Svanberg Date: Wed, 6 Nov 2024 15:38:35 +0100 Subject: [PATCH 1/3] Switch to Spring Security for authentication and allow local OAuth 2 log in Instead of storing the current user in the Wicket session, let Spring Security handle it. The CurrentUser implementation has been changed to look it up from there instead of the Wicket session. Also enable, in addition to Shibboleth (pre-authenticated remote user), OAuth 2 login which removes the need for the locally modified web.xml with a faked remote user. The Docker Compose file has been updated to run a OAuth 2 container for this type of login. --- README.md | 4 + .../scipro/system/AuthenticationContext.java | 15 +++ .../impl/ImporterTransactionsImpl.java | 1 + docker-compose.yml | 11 +++ .../scipro/CurrentUserFromWicketSession.java | 13 --- .../dsv/scipro/loginlogout/pages/SSOPage.java | 8 +- .../security/auth/MockRemoteUserFilter.java | 70 ------------- .../su/dsv/scipro/session/SciProSession.java | 14 ++- .../java/se/su/dsv/scipro/SciProTest.java | 5 +- war/pom.xml | 4 + .../war/CurrentUserFromSpringSecurity.java | 98 +++++++++++++++++++ .../main/java/se/su/dsv/scipro/war/Main.java | 6 -- .../dsv/scipro/war/WicketConfiguration.java | 30 ++++++ war/src/main/resources/application.properties | 11 +++ 14 files changed, 188 insertions(+), 102 deletions(-) create mode 100644 core/src/main/java/se/su/dsv/scipro/system/AuthenticationContext.java delete mode 100644 view/src/main/java/se/su/dsv/scipro/CurrentUserFromWicketSession.java delete mode 100755 view/src/main/java/se/su/dsv/scipro/security/auth/MockRemoteUserFilter.java create mode 100644 war/src/main/java/se/su/dsv/scipro/war/CurrentUserFromSpringSecurity.java diff --git a/README.md b/README.md index 749d07cb72..50ed89f290 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,7 @@ +## Working with the web GUI (Wicket) +The web GUI is protected by OAuth 2 log in. Run the Docker Compose containers with +`docker compose up` to start the authorization server to be able to log in. + ## Working with the API The API is protected by OAuth 2 acting as a [resource server](https://www.oauth.com/oauth2-servers/the-resource-server/) verifying tokens using [token introspection](https://datatracker.ietf.org/doc/html/rfc7662). diff --git a/core/src/main/java/se/su/dsv/scipro/system/AuthenticationContext.java b/core/src/main/java/se/su/dsv/scipro/system/AuthenticationContext.java new file mode 100644 index 0000000000..9f46604358 --- /dev/null +++ b/core/src/main/java/se/su/dsv/scipro/system/AuthenticationContext.java @@ -0,0 +1,15 @@ +package se.su.dsv.scipro.system; + +/** + * Information about the current authentication context. + *

+ * The difference between this and {@link CurrentUser} is that a user can be + * authenticated without being a user in the system. This can happen when a + * user logs in for the first time via SSO. The {@link #set(User)} method can + * be used if the user can be imported based on the {@link #getPrincipalName()}. + */ +public interface AuthenticationContext extends CurrentUser { + void set(User user); + + String getPrincipalName(); +} diff --git a/daisy-integration/src/main/java/se/su/dsv/scipro/daisyExternal/impl/ImporterTransactionsImpl.java b/daisy-integration/src/main/java/se/su/dsv/scipro/daisyExternal/impl/ImporterTransactionsImpl.java index 9e6ea05554..eaa367e3d0 100644 --- a/daisy-integration/src/main/java/se/su/dsv/scipro/daisyExternal/impl/ImporterTransactionsImpl.java +++ b/daisy-integration/src/main/java/se/su/dsv/scipro/daisyExternal/impl/ImporterTransactionsImpl.java @@ -157,6 +157,7 @@ public class ImporterTransactionsImpl implements ImporterTransactions { username.setUsername(completeUsername); username.setUser(local); userNameService.save(username); + local.getUsernames().add(username); } } } diff --git a/docker-compose.yml b/docker-compose.yml index 637455a39e..aac221e274 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,3 +13,14 @@ services: - CLIENT_REDIRECT_URI=http://localhost:59732/ - RESOURCE_SERVER_ID=scipro-api-client - RESOURCE_SERVER_SECRET=scipro-api-secret + oauth2-wicket: + build: + context: https://github.com/dsv-su/toker.git + dockerfile: embedded.Dockerfile + restart: on-failure + ports: + - '59734:8080' + environment: + - CLIENT_ID=scipro + - CLIENT_SECRET=s3cr3t + - CLIENT_REDIRECT_URI=http://localhost:8080/login/oauth2/code/scipro diff --git a/view/src/main/java/se/su/dsv/scipro/CurrentUserFromWicketSession.java b/view/src/main/java/se/su/dsv/scipro/CurrentUserFromWicketSession.java deleted file mode 100644 index 808992e771..0000000000 --- a/view/src/main/java/se/su/dsv/scipro/CurrentUserFromWicketSession.java +++ /dev/null @@ -1,13 +0,0 @@ -package se.su.dsv.scipro; - -import org.apache.wicket.Session; -import se.su.dsv.scipro.session.SciProSession; -import se.su.dsv.scipro.system.CurrentUser; -import se.su.dsv.scipro.system.User; - -public class CurrentUserFromWicketSession implements CurrentUser { - @Override - public User get() { - return Session.exists() ? SciProSession.get().getUser() : null; - } -} diff --git a/view/src/main/java/se/su/dsv/scipro/loginlogout/pages/SSOPage.java b/view/src/main/java/se/su/dsv/scipro/loginlogout/pages/SSOPage.java index 5ebf9f0f0f..371c58afe8 100644 --- a/view/src/main/java/se/su/dsv/scipro/loginlogout/pages/SSOPage.java +++ b/view/src/main/java/se/su/dsv/scipro/loginlogout/pages/SSOPage.java @@ -4,11 +4,12 @@ import se.su.dsv.scipro.basepages.PublicPage; import se.su.dsv.scipro.security.auth.Authorization; import se.su.dsv.scipro.session.SciProSession; import se.su.dsv.scipro.system.User; +import se.su.dsv.scipro.system.AuthenticationContext; import se.su.dsv.scipro.system.UserImportService; import se.su.dsv.scipro.system.UserService; import jakarta.inject.Inject; -import jakarta.servlet.http.HttpServletRequest; + import java.util.Optional; import java.util.Set; @@ -21,8 +22,11 @@ public class SSOPage extends PublicPage { @Inject private UserService userService; + @Inject + private AuthenticationContext authenticationContext; + public SSOPage() { - String remoteUserName = ((HttpServletRequest) getRequest().getContainerRequest()).getRemoteUser(); + String remoteUserName = authenticationContext.getPrincipalName(); User user = userService.findByUsername(remoteUserName); if (user != null) { diff --git a/view/src/main/java/se/su/dsv/scipro/security/auth/MockRemoteUserFilter.java b/view/src/main/java/se/su/dsv/scipro/security/auth/MockRemoteUserFilter.java deleted file mode 100755 index b67ea8916d..0000000000 --- a/view/src/main/java/se/su/dsv/scipro/security/auth/MockRemoteUserFilter.java +++ /dev/null @@ -1,70 +0,0 @@ -package se.su.dsv.scipro.security.auth; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import jakarta.servlet.*; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletRequestWrapper; -import java.io.IOException; - -/** - * Throw-away implementation of a servlet filter, main task is to fake the getRemoteUser() call for the request chain. - */ -public final class MockRemoteUserFilter implements Filter { - private static final Logger LOGGER = LoggerFactory.getLogger(MockRemoteUserFilter.class); - //Default value unless supplied via init parameter - private String fakedUser = "SOME_GUY"; - private FilterConfig cfg = null; - /** - * Default constructor. - */ - public MockRemoteUserFilter() { - } - /** - * @see Filter#destroy() - */ - @Override - public void destroy() { - cfg = null; - } - /** - * Wraps the passed request and alters the behavior of getRemoteUser() for later links of the chain. - * @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain) - */ - @Override - public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { - LOGGER.debug("Faking external authentication user: " + fakedUser); - if(cfg != null){ - HttpServletRequestWrapper wrapper = new ModifiedRemoteUserRequestWrapper((HttpServletRequest)request,fakedUser); - // pass the request along the filter chain - chain.doFilter(wrapper, response); - return; - } - chain.doFilter(request, response); - } - /** - * @see Filter#init(FilterConfig) - */ - @Override - public void init(FilterConfig fConfig) { - cfg = fConfig; - if(cfg!=null){ - fakedUser = cfg.getInitParameter("fakedUser"); - } - } - /** - * Private RequestWrapper, of no interest to anyone outside of this class. - */ - static class ModifiedRemoteUserRequestWrapper extends HttpServletRequestWrapper{ - private final String fakedUser; - ModifiedRemoteUserRequestWrapper(final HttpServletRequest request,final String fakedUser){ - super(request); - this.fakedUser = fakedUser; - } - @Override - public String getRemoteUser(){ - return fakedUser; - } - } -} diff --git a/view/src/main/java/se/su/dsv/scipro/session/SciProSession.java b/view/src/main/java/se/su/dsv/scipro/session/SciProSession.java index 6d7d573de8..c0184059ff 100755 --- a/view/src/main/java/se/su/dsv/scipro/session/SciProSession.java +++ b/view/src/main/java/se/su/dsv/scipro/session/SciProSession.java @@ -1,18 +1,18 @@ package se.su.dsv.scipro.session; -import org.apache.wicket.MetaDataKey; import org.apache.wicket.Session; import org.apache.wicket.injection.Injector; import org.apache.wicket.protocol.http.WebSession; import org.apache.wicket.request.Request; import se.su.dsv.scipro.security.auth.roles.IRole; import se.su.dsv.scipro.security.auth.roles.Roles; +import se.su.dsv.scipro.system.AuthenticationContext; import se.su.dsv.scipro.system.ProjectModule; import se.su.dsv.scipro.system.SystemModule; import se.su.dsv.scipro.system.User; -import se.su.dsv.scipro.system.UserService; import jakarta.inject.Inject; + import java.util.Collections; import java.util.HashSet; import java.util.Locale; @@ -20,10 +20,8 @@ import java.util.Set; public class SciProSession extends WebSession { - private final static MetaDataKey LOGGED_IN_USER_ID = new MetaDataKey<>() {}; - @Inject - private UserService userService; + private AuthenticationContext authenticationContext; private Set projectModules = new HashSet<>(); private Set systemModules = new HashSet<>(); @@ -38,15 +36,15 @@ public class SciProSession extends WebSession { } public synchronized void setUser(User user) { - setMetaData(LOGGED_IN_USER_ID, user.getId()); + authenticationContext.set(user); } public synchronized User getUser() { - return isLoggedIn() ? userService.findOne(getMetaData(LOGGED_IN_USER_ID)) : null; + return authenticationContext.get(); } public synchronized boolean isLoggedIn() { - return getMetaData(LOGGED_IN_USER_ID) != null; + return authenticationContext.get() != null; } public synchronized boolean authorizedForRole(Roles role) { diff --git a/view/src/test/java/se/su/dsv/scipro/SciProTest.java b/view/src/test/java/se/su/dsv/scipro/SciProTest.java index 2b469c39ec..0b9bbef520 100755 --- a/view/src/test/java/se/su/dsv/scipro/SciProTest.java +++ b/view/src/test/java/se/su/dsv/scipro/SciProTest.java @@ -111,12 +111,10 @@ import se.su.dsv.scipro.springdata.services.UnitService; import se.su.dsv.scipro.springdata.services.UserProfileService; import se.su.dsv.scipro.supervisor.pages.SupervisorStartPage; import se.su.dsv.scipro.survey.SurveyService; -import se.su.dsv.scipro.system.CurrentUser; import se.su.dsv.scipro.system.ExternalResourceService; import se.su.dsv.scipro.system.FooterAddressRepo; import se.su.dsv.scipro.system.FooterLinkService; import se.su.dsv.scipro.system.GenericService; -import se.su.dsv.scipro.system.Lifecycle; import se.su.dsv.scipro.system.PasswordRepo; import se.su.dsv.scipro.system.PasswordService; import se.su.dsv.scipro.system.ProjectModule; @@ -124,6 +122,7 @@ import se.su.dsv.scipro.system.ProjectTypeService; import se.su.dsv.scipro.system.ResearchAreaService; import se.su.dsv.scipro.system.SystemModule; import se.su.dsv.scipro.system.User; +import se.su.dsv.scipro.system.AuthenticationContext; import se.su.dsv.scipro.system.UserImportService; import se.su.dsv.scipro.system.UserNameService; import se.su.dsv.scipro.system.UserSearchService; @@ -298,7 +297,7 @@ public abstract class SciProTest { @Mock protected ChecklistAnswerService checklistAnswerService; @Mock - protected CurrentUser currentUser; + protected AuthenticationContext authenticationContext; @Mock private Scheduler scheduler; @Mock diff --git a/war/pom.xml b/war/pom.xml index 2c4ad17242..456eeae393 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -41,6 +41,10 @@ spring-boot-starter-tomcat provided + + org.springframework.boot + spring-boot-starter-oauth2-client + org.springframework spring-orm diff --git a/war/src/main/java/se/su/dsv/scipro/war/CurrentUserFromSpringSecurity.java b/war/src/main/java/se/su/dsv/scipro/war/CurrentUserFromSpringSecurity.java new file mode 100644 index 0000000000..f10db76cd6 --- /dev/null +++ b/war/src/main/java/se/su/dsv/scipro/war/CurrentUserFromSpringSecurity.java @@ -0,0 +1,98 @@ +package se.su.dsv.scipro.war; + +import jakarta.inject.Inject; +import jakarta.inject.Provider; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +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.security.core.context.SecurityContextHolderStrategy; +import org.springframework.security.web.context.HttpSessionSecurityContextRepository; +import org.springframework.security.web.context.SecurityContextRepository; +import se.su.dsv.scipro.system.User; +import se.su.dsv.scipro.system.AuthenticationContext; +import se.su.dsv.scipro.system.UserService; +import se.su.dsv.scipro.system.Username; + +import java.security.Principal; +import java.util.Collections; + +public class CurrentUserFromSpringSecurity implements AuthenticationContext { + private final UserService userService; + + // injecting providers since this is a singleton and the request and response are not + private final Provider currentRequest; + private final Provider currentResponse; + + // hardcoded since that is what Spring Security does (see SwitchUserFilter) + private final SecurityContextRepository securityContextRepository = new HttpSessionSecurityContextRepository(); + + @Inject + public CurrentUserFromSpringSecurity( + UserService userService, + Provider currentRequest, + Provider currentResponse) + { + this.userService = userService; + this.currentRequest = currentRequest; + this.currentResponse = currentResponse; + } + + @Override + public User get() { + SecurityContext context = SecurityContextHolder.getContext(); + Authentication authentication = context.getAuthentication(); + if (authentication == null) { + return null; + } + String username = authentication.getName(); + return userService.findByUsername(username); + } + + // Implementing switch user manually rather than using the built-in Spring Security switch user feature + // due to compatibility with Wicket. + // Wicket does not supply a form with a username field since it has some JavaScript based auto-complete + // person finder. + // See Spring's SwitchUserFilter for the built-in switch user feature from where most of the code is copied. + @Override + public void set(User user) { + SecurityContextHolderStrategy strategy = SecurityContextHolder.getContextHolderStrategy(); + SecurityContext context = strategy.createEmptyContext(); + WicketControlledPrincipal principal = new WicketControlledPrincipal(user); + UsernamePasswordAuthenticationToken targetUser = UsernamePasswordAuthenticationToken.authenticated( + principal, null, Collections.emptyList()); + context.setAuthentication(targetUser); + strategy.setContext(context); + this.securityContextRepository.saveContext(context, currentRequest.get(), currentResponse.get()); + } + + @Override + public String getPrincipalName() { + SecurityContext context = SecurityContextHolder.getContext(); + Authentication authentication = context.getAuthentication(); + if (authentication == null) { + return null; + } + return authentication.getName(); + } + + private static final class WicketControlledPrincipal implements Principal { + private final String username; + + public WicketControlledPrincipal(User user) { + // extract any username so that we can look it up later + this.username = user.getUsernames() + .stream() + .findAny() + .map(Username::getUsername) + .orElse(""); + } + + @Override + public String getName() { + return username; + } + } +} diff --git a/war/src/main/java/se/su/dsv/scipro/war/Main.java b/war/src/main/java/se/su/dsv/scipro/war/Main.java index 026130dd7d..3f50c122c5 100644 --- a/war/src/main/java/se/su/dsv/scipro/war/Main.java +++ b/war/src/main/java/se/su/dsv/scipro/war/Main.java @@ -18,7 +18,6 @@ import org.springframework.core.task.SimpleAsyncTaskExecutor; import org.springframework.orm.jpa.SharedEntityManagerCreator; import org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter; import se.su.dsv.scipro.CoreConfig; -import se.su.dsv.scipro.CurrentUserFromWicketSession; import se.su.dsv.scipro.FileSystemStore; import se.su.dsv.scipro.RepositoryConfiguration; import se.su.dsv.scipro.file.FileStore; @@ -83,11 +82,6 @@ public class Main extends SpringBootServletInitializer implements ServletContain return currentProfile; } - @Bean - public CurrentUserFromWicketSession currentUserFromWicketSession() { - return new CurrentUserFromWicketSession(); - } - @Bean public FileStore fileStore() { return new FileSystemStore(); diff --git a/war/src/main/java/se/su/dsv/scipro/war/WicketConfiguration.java b/war/src/main/java/se/su/dsv/scipro/war/WicketConfiguration.java index bb255d4915..bd9b3d02f7 100644 --- a/war/src/main/java/se/su/dsv/scipro/war/WicketConfiguration.java +++ b/war/src/main/java/se/su/dsv/scipro/war/WicketConfiguration.java @@ -1,6 +1,9 @@ package se.su.dsv.scipro.war; import com.google.common.eventbus.EventBus; +import jakarta.inject.Provider; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.wicket.protocol.http.WebApplication; import org.apache.wicket.protocol.http.WicketFilter; import org.apache.wicket.spring.injection.annot.SpringComponentInjector; @@ -8,6 +11,10 @@ import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; +import org.springframework.security.config.Customizer; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.web.SecurityFilterChain; import se.su.dsv.scipro.SciProApplication; import se.su.dsv.scipro.crosscutting.ForwardPhase2Feedback; import se.su.dsv.scipro.crosscutting.NotifyFailedReflection; @@ -21,6 +28,7 @@ import se.su.dsv.scipro.notifications.NotificationController; import se.su.dsv.scipro.profiles.CurrentProfile; import se.su.dsv.scipro.reviewing.FinalSeminarApprovalService; import se.su.dsv.scipro.reviewing.RoughDraftApprovalService; +import se.su.dsv.scipro.system.UserService; @Configuration public class WicketConfiguration { @@ -47,6 +55,28 @@ public class WicketConfiguration { return new SciProApplication(currentProfile); } + @Bean + @Order(3) // make sure it's after the API security filters + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http.authorizeHttpRequests((requests) -> requests.anyRequest().authenticated()); + http.jee(Customizer.withDefaults()); // Shibboleth integration + http.oauth2Login(Customizer.withDefaults()); + http.csrf(csrf -> csrf.disable()); // Wicket has its own CSRF protection + http.logout(logout -> logout + .logoutUrl("/logout") + .logoutSuccessUrl("/")); + return http.build(); + } + + @Bean + public CurrentUserFromSpringSecurity currentUserFromSpringSecurity( + UserService userService, + Provider httpServletRequestProvider, + Provider httpServletResponseProvider) + { + return new CurrentUserFromSpringSecurity(userService, httpServletRequestProvider, httpServletResponseProvider); + } + @Bean public ReviewingNotifications reviewingNotifications( EventBus eventBus, diff --git a/war/src/main/resources/application.properties b/war/src/main/resources/application.properties index 753d0e323c..54f5c34fad 100644 --- a/war/src/main/resources/application.properties +++ b/war/src/main/resources/application.properties @@ -18,3 +18,14 @@ springdoc.swagger-ui.persist-authorization=true spring.security.oauth2.resourceserver.opaquetoken.client-id=scipro-api-client spring.security.oauth2.resourceserver.opaquetoken.client-secret=scipro-api-secret spring.security.oauth2.resourceserver.opaquetoken.introspection-uri=http://localhost:59733/introspect + +# Log in via local OAuth 2 authorization server +spring.security.oauth2.client.provider.docker.user-info-uri=http://localhost:59734/verify +spring.security.oauth2.client.provider.docker.user-name-attribute=sub +spring.security.oauth2.client.provider.docker.token-uri=http://localhost:59734/exchange +spring.security.oauth2.client.provider.docker.authorization-uri=http://localhost:59734/authorize +spring.security.oauth2.client.registration.scipro.redirect-uri={baseUrl}/login/oauth2/code/{registrationId} +spring.security.oauth2.client.registration.scipro.provider=docker +spring.security.oauth2.client.registration.scipro.client-id=scipro +spring.security.oauth2.client.registration.scipro.client-secret=s3cr3t +spring.security.oauth2.client.registration.scipro.authorization-grant-type=authorization_code -- 2.39.5 From f6acbd805b5df396ecdd0d2da85e45c8aa8df73f Mon Sep 17 00:00:00 2001 From: Andreas Svanberg Date: Thu, 21 Nov 2024 13:07:25 +0100 Subject: [PATCH 2/3] Remove Shibboleth (SAML) log in Tomcat/Apache integration for SAML will populate the ServletRequest#getRemoteUser with an empty string rather than null when not authenticated. This confuses Spring Security to think the user is authenticated but with an empty string as the principal name. This causes problems further down the line in Spring Security since an empty principal is not accepted. To get around this we simply remove the SAML integration and rely solely on OAuth 2.0 for log in. An alternative would be to apply a servlet filter beforehand that would send null if the string is empty. But that has the downside of having different authentication mechanism for production and development. By using only OAuth 2.0 everywhere it works the same, and it is easier to troubleshoot. --- war/src/main/java/se/su/dsv/scipro/war/WicketConfiguration.java | 1 - 1 file changed, 1 deletion(-) diff --git a/war/src/main/java/se/su/dsv/scipro/war/WicketConfiguration.java b/war/src/main/java/se/su/dsv/scipro/war/WicketConfiguration.java index bd9b3d02f7..6e08db02a8 100644 --- a/war/src/main/java/se/su/dsv/scipro/war/WicketConfiguration.java +++ b/war/src/main/java/se/su/dsv/scipro/war/WicketConfiguration.java @@ -59,7 +59,6 @@ public class WicketConfiguration { @Order(3) // make sure it's after the API security filters public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http.authorizeHttpRequests((requests) -> requests.anyRequest().authenticated()); - http.jee(Customizer.withDefaults()); // Shibboleth integration http.oauth2Login(Customizer.withDefaults()); http.csrf(csrf -> csrf.disable()); // Wicket has its own CSRF protection http.logout(logout -> logout -- 2.39.5 From c3aeb3204597c54d4f86a87346f2e8a2739968d1 Mon Sep 17 00:00:00 2001 From: Andreas Svanberg Date: Tue, 26 Nov 2024 13:07:45 +0100 Subject: [PATCH 3/3] Allow logging in with the default OAuth 2 principal The default principal is "dev@localhost", if you run the system with the development profile (DEV) a user with sys-admin role will be created with that principal as username. --- README.md | 5 +++++ core/src/main/java/se/su/dsv/scipro/DataInitializer.java | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/README.md b/README.md index 50ed89f290..6928e1b262 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,11 @@ The web GUI is protected by OAuth 2 log in. Run the Docker Compose containers with `docker compose up` to start the authorization server to be able to log in. +If you run SciPro in development mode (DEV profile) you will be able to log in +as the "default" OAuth 2 user populated in the upper form. If you have other +data in your database you will have to use the lower form and specify a valid +username in the principal field. + ## Working with the API The API is protected by OAuth 2 acting as a [resource server](https://www.oauth.com/oauth2-servers/the-resource-server/) verifying tokens using [token introspection](https://datatracker.ietf.org/doc/html/rfc7662). diff --git a/core/src/main/java/se/su/dsv/scipro/DataInitializer.java b/core/src/main/java/se/su/dsv/scipro/DataInitializer.java index 762ea0c32a..206f95305d 100644 --- a/core/src/main/java/se/su/dsv/scipro/DataInitializer.java +++ b/core/src/main/java/se/su/dsv/scipro/DataInitializer.java @@ -200,6 +200,10 @@ public class DataInitializer implements Lifecycle { admin.addRole(Roles.SYSADMIN); createBeta(admin); passwordService.updatePassword(admin, "aey7ru8aefei0jaW2wo9eX8EiShi0aan"); + Username defaultOAuth2Principal = new Username(); + defaultOAuth2Principal.setUsername("dev@localhost"); + defaultOAuth2Principal.setUser(admin); + save(defaultOAuth2Principal); } private void createBeta(User user) { -- 2.39.5