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 Mar 11, 2024
1 parent 6748a5c commit 86794b7
Show file tree
Hide file tree
Showing 12 changed files with 459 additions and 184 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,10 +16,16 @@

package no.rutebanken.baba.config;

import org.entur.oauth2.AuthorizedWebClientBuilder;
import org.entur.oauth2.JwtRoleAssignmentExtractor;
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.
Expand All @@ -37,6 +43,28 @@ public RoleAssignmentExtractor roleAssignmentExtractor() {
}


@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();
}


}


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.PermissionStoreResource;
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")
PermissionStoreResource permissionStoreResource(
@Qualifier("permissionStoreWebClient") WebClient orgRegisterClient
) {
return new PermissionStoreResource(orgRegisterClient);
}

@Bean
@Profile("!test")
PermissionStoreService permissionStoreService(PermissionStoreResource permissionStoreResource) {
return new DefaultPermissionStoreService(
permissionStoreResource
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@

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.user.User;
Expand All @@ -29,25 +33,13 @@
import no.rutebanken.baba.organisation.rest.validation.DTOValidator;
import no.rutebanken.baba.organisation.rest.validation.UserValidator;
import no.rutebanken.baba.organisation.service.IamService;
import no.rutebanken.baba.organisation.service.OAuth2UserNotFoundException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Component;
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;

import static org.rutebanken.helper.organisation.AuthorizationConstants.ROLE_ORGANISATION_EDIT;
Expand All @@ -58,14 +50,15 @@
@Transactional
@PreAuthorize("hasRole('" + ROLE_ORGANISATION_EDIT + "')")
@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 UserMapper mapper;
private final UserValidator validator;
private final IamService iamService;

private final NewUserEmailSender newUserEmailSender;

public UserResource(UserRepository repository,
Expand All @@ -88,47 +81,47 @@ public UserDTO get(@PathParam("id") String id, @QueryParam("full") boolean fullO
}

/**
* Create a new user.
* <br>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.
* <br>Otherwise, if the user account is federated:
* - if the user account exists already (that is: the user has logged in at least once in the tenant), its metadata and roles are updated.
* - if the user account does not exist yet, its metadata and roles are updated in the pre-provisioning database
* (and the metadata and roles will be copied over from the pre-provisioning database when the user logs in for the first time).
* <br>Otherwise:
* - if the user account exists already in the tenant, its metadata and roles are updated.
* - if the user account does not exist yet in the tenant, the account is created, its metadata and roles are updated.
* <br>
* 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()) {
try {
iamService.createUser(user);
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);
}

return buildCreatedResponse(uriInfo, user);
}


@PUT
@Path("{id}")
public void update(@PathParam("id") String id, UserDTO dto) {
User user = updateEntity(id, dto);
if (user.isPersonalAccount()) {
try {
iamService.updateUser(user);
} catch (OAuth2UserNotFoundException e) {
// TODO temporarily allow creation of user when missing in Entur Partner Auth0 to facilitate migration from RoR Auth0
// to be removed after the migration is complete
LOGGER.info("User {} not found in Auth0 tenant. Creating a new user", user.getUsername());
iamService.createUser(user);
newUserEmailSender.sendEmail(user);
}
iamService.createOrUpdate(user);
}


}

// TODO temporary service to migrate accounts to Auth0
// to be removed after the migration is complete
// to be removed after the migration is complete
@POST
@Path("migrate")
public void migrate() throws InterruptedException {
Expand All @@ -141,42 +134,25 @@ public void migrate() throws InterruptedException {
continue;
}
LOGGER.info("Migrating user {}", user.getUsername());
try {
iamService.updateUser(user);
LOGGER.info("The user {} was already migrated", user.getUsername());
} catch (OAuth2UserNotFoundException e) {
LOGGER.info("User {} not found in Auth0 tenant. Creating a new user", user.getUsername());
iamService.createUser(user);
newUserEmailSender.sendEmail(user);
}
iamService.createOrUpdate(user);
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");
}



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


@POST
@Path("{id}/resetPassword")
public void resetPassword(@PathParam("id") String id) {
User user = getExisting(id);
if(user.isPersonalAccount()) {
iamService.resetPassword(user);
newUserEmailSender.sendEmail(user);
}
}

@GET
public List<UserDTO> listAll(@QueryParam("full") boolean fullObject) {
return super.listAllEntities(fullObject);
Expand Down
Loading

0 comments on commit 86794b7

Please sign in to comment.