Skip to content

Commit

Permalink
Add Auth0 migration service
Browse files Browse the repository at this point in the history
  • Loading branch information
vpaturet committed Aug 14, 2024
1 parent c3a5062 commit da5fb82
Show file tree
Hide file tree
Showing 16 changed files with 754 additions and 256 deletions.
28 changes: 28 additions & 0 deletions src/main/java/no/rutebanken/baba/config/OAuth2Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,47 @@

package no.rutebanken.baba.config;

import org.entur.oauth2.AuthorizedWebClientBuilder;
import org.entur.oauth2.JwtRoleAssignmentExtractor;
import org.entur.oauth2.multiissuer.MultiIssuerAuthenticationManagerResolver;
import org.entur.oauth2.multiissuer.MultiIssuerAuthenticationManagerResolverBuilder;
import org.rutebanken.helper.organisation.RoleAssignmentExtractor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.http.client.reactive.ClientHttpConnector;
import org.springframework.web.reactive.function.client.WebClient;

/**
* Configure Spring Beans for OAuth2 resource server and OAuth2 client security.
*/
@Configuration
public class OAuth2Config {

@Bean("permissionStoreWebClient")
@Profile("!test")
WebClient permissionStoreWebClient(
WebClient.Builder webClientBuilder,
OAuth2ClientProperties properties,
@Value("${baba.permissionstore.oauth2.client.audience}") String audience,
ClientHttpConnector clientHttpConnector,
@Value("${baba.permissionstore.url}") String organisationRegistryUrl
) {
return new AuthorizedWebClientBuilder(webClientBuilder)
.withOAuth2ClientProperties(properties)
.withAudience(audience)
.withClientRegistrationId("permissionstore")
.build()
.mutate()
.clientConnector(clientHttpConnector)
.defaultHeader("Et-Client-Name", "entur-baba")
.baseUrl(organisationRegistryUrl)
.build();
}


@Bean
@Profile("!test")
public MultiIssuerAuthenticationManagerResolver multiIssuerAuthenticationManagerResolver(
Expand Down
46 changes: 46 additions & 0 deletions src/main/java/no/rutebanken/baba/config/PermissionStoreConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Licensed under the EUPL, Version 1.2 or – as soon they will be approved by
* the European Commission - subsequent versions of the EUPL (the "Licence");
* You may not use this work except in compliance with the Licence.
* You may obtain a copy of the Licence at:
*
* https://joinup.ec.europa.eu/software/page/eupl
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the Licence is distributed on an "AS IS" basis,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Licence for the specific language governing permissions and
* limitations under the Licence.
*
*/

package no.rutebanken.baba.config;

import no.rutebanken.baba.permissionstore.service.DefaultPermissionStoreService;
import no.rutebanken.baba.permissionstore.service.PermissionStoreClient;
import no.rutebanken.baba.permissionstore.service.PermissionStoreService;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.web.reactive.function.client.WebClient;

@Configuration
public class PermissionStoreConfig {

@Bean
@Profile("!test")
PermissionStoreClient permissionStoreResource(
@Qualifier("permissionStoreWebClient") WebClient permissionStoreWebClient
) {
return new PermissionStoreClient(permissionStoreWebClient);
}

@Bean
@Profile("!test")
PermissionStoreService permissionStoreService(PermissionStoreClient permissionStoreClient) {
return new DefaultPermissionStoreService(
permissionStoreClient
);
}
}
102 changes: 71 additions & 31 deletions src/main/java/no/rutebanken/baba/organisation/rest/UserResource.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,15 @@

import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.tags.Tags;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.UriInfo;
import no.rutebanken.baba.organisation.email.NewUserEmailSender;
import no.rutebanken.baba.organisation.model.OrganisationException;
import no.rutebanken.baba.organisation.model.responsibility.Role;
import no.rutebanken.baba.organisation.model.user.User;
import no.rutebanken.baba.organisation.repository.RoleRepository;
import no.rutebanken.baba.organisation.repository.UserRepository;
import no.rutebanken.baba.organisation.repository.VersionedEntityRepository;
import no.rutebanken.baba.organisation.rest.dto.user.UserDTO;
Expand All @@ -36,17 +42,6 @@
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.UriInfo;
import java.util.List;

@Component
Expand All @@ -55,22 +50,26 @@
@Transactional
@PreAuthorize("@authorizationService.isOrganisationAdmin()")
@Tags(value = {
@Tag(name = "UserResource", description ="User resource")
@Tag(name = "UserResource", description = "User resource")
})
public class UserResource extends BaseResource<User, UserDTO> {
private static final Logger LOGGER = LoggerFactory.getLogger(UserResource.class);
private final UserRepository repository;

private final RoleRepository roleRepository;
private final UserMapper mapper;
private final UserValidator validator;
private final IamService iamService;

private final NewUserEmailSender newUserEmailSender;

public UserResource(UserRepository repository,
public UserResource(UserRepository repository, RoleRepository roleRepository,
UserMapper mapper,
UserValidator validator,
IamService iamService,
NewUserEmailSender newUserEmailSender) {
this.repository = repository;
this.roleRepository = roleRepository;
this.mapper = mapper;
this.validator = validator;
this.iamService = iamService;
Expand All @@ -85,52 +84,93 @@ public UserDTO get(@PathParam("id") String id, @QueryParam("full") boolean fullO
}

/**
* Create a new user.
* If the user account is not a personal account, it is created only in the local database (baba) and not inserted in the Auth0 tenant.
* Do not wrap method in a single transaction. Need to commit local storage before creating user in IAM, to avoid having users in IAM that does not exist in local storage.
*/
@Transactional(propagation = Propagation.NEVER)
@POST
public Response create(UserDTO dto, @Context UriInfo uriInfo) {
User user = createEntity(dto);
if(user.isPersonalAccount()) {
if (user.isPersonalAccount()) {
boolean created;
try {
iamService.createUser(user);
created = iamService.createOrUpdate(user);
} catch (RuntimeException e) {
LOGGER.warn("Creation of new user in IAM failed. Removing user from local storage. Exception: {}", e.getMessage(), e);
deleteEntity(user.getId());
throw new OrganisationException("Creation of new user in IAM failed", e);
}
newUserEmailSender.sendEmail(user);
if (created) {
newUserEmailSender.sendEmail(user);
}
}

return buildCreatedResponse(uriInfo, user);
}


@PUT
@Path("{id}")
public void update(@PathParam("id") String id, UserDTO dto) {
User user = updateEntity(id, dto);
if(user.isPersonalAccount()) {
iamService.updateUser(user);
if (user.isPersonalAccount()) {
iamService.createOrUpdate(user);
}
}

@DELETE
@Path("{id}")
public void delete(@PathParam("id") String id) {
User user = deleteEntity(id);
if(user.isPersonalAccount()) {
iamService.removeUser(user);
// TODO temporary service to migrate accounts to Auth0
// to be removed after the migration is complete
@POST
@Path("migrate")
public void migrate() throws InterruptedException {
LOGGER.info("Migrating user accounts to Auth0");
for (UserDTO userDTO : listAllEntities()) {
User user = getExisting(userDTO.id);

// Notification accounts do not need to be migrated
if (!user.isPersonalAccount()) {
continue;
}
LOGGER.info("Migrating user {}", user.getUsername());
boolean created = iamService.createOrUpdate(user);
if (created) {
LOGGER.info("Sending email to user {}", user.getUsername());
newUserEmailSender.sendEmail(user);
} else {
LOGGER.info("The user {} was already migrated", user.getUsername());
}
// slow down migration to prevent rate limiting
Thread.sleep(10000);
}
LOGGER.info("Migration to Auth0 complete");
}


// TODO temporary service to migrate accounts to Auth0
// to be removed after the migration is complete
@POST
@Path("{id}/resetPassword")
public void resetPassword(@PathParam("id") String id) {
User user = getExisting(id);
if(user.isPersonalAccount()) {
iamService.resetPassword(user);
newUserEmailSender.sendEmail(user);
@Path("migrate_roles")
public void migrateRoles() throws InterruptedException {
LOGGER.info("Migrating roles to Auth0");
for (Role role : roleRepository.findAll()) {
LOGGER.info("Migrating role {}", role.getName());
// TODO skip when role exists already
iamService.createRole(role);
// slow down migration to prevent rate limiting
Thread.sleep(10000);
}
//TODO retrieve default role
iamService.createRole(new Role("rutebanken", "Default Role"));
LOGGER.info("Migration of roles to Auth0 complete");
}


@DELETE
@Path("{id}")
public void delete(@PathParam("id") String id) {
User user = deleteEntity(id);
if (user.isPersonalAccount()) {
iamService.removeUser(user);
}
}

Expand Down
Loading

0 comments on commit da5fb82

Please sign in to comment.