Skip to content

Commit

Permalink
Refactor authorization service
Browse files Browse the repository at this point in the history
  • Loading branch information
vpaturet committed Jun 10, 2024
1 parent 4580134 commit ed01913
Show file tree
Hide file tree
Showing 26 changed files with 202 additions and 133 deletions.
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
<parent>
<groupId>org.entur.ror</groupId>
<artifactId>superpom</artifactId>
<version>2.10</version>
<version>2.13</version>
</parent>
<groupId>org.rutebanken</groupId>
<artifactId>tiamat</artifactId>
Expand Down Expand Up @@ -58,7 +58,7 @@
<hazelcast.version>5.2.4</hazelcast.version>
<swagger-jersey2.version>2.2.20</swagger-jersey2.version>
<netex-java-model.version>2.0.14</netex-java-model.version>
<entur.helpers.version>2.11</entur.helpers.version>
<entur.helpers.version>2.26</entur.helpers.version>
<jts-core.version>1.19.0</jts-core.version>
<groovy-all.version>4.0.17</groovy-all.version>
<rest-assured.version>5.4.0</rest-assured.version>
Expand Down
45 changes: 45 additions & 0 deletions src/main/java/org/rutebanken/tiamat/auth/AuthorizationService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package org.rutebanken.tiamat.auth;

import org.rutebanken.helper.organisation.RoleAssignment;
import org.rutebanken.tiamat.model.EntityStructure;
import org.springframework.security.access.AccessDeniedException;

import java.util.Collection;
import java.util.Set;

/**
* Authorize operations for the current user.
*/
public interface AuthorizationService {

/**
* Does the current user have edit right on all the given entities?
*/
boolean canEditEntities(Collection<? extends EntityStructure> entities);

/**
* Verify that the current user has edit right on all the given entities.
* @throws AccessDeniedException if not.
*/
void verifyCanEditEntities(Collection<? extends EntityStructure> entities);

/**
* Verify that the current user has delete right on all the given entities.
* @throws AccessDeniedException if not.
*/
void verifyCanDeleteEntities(Collection<? extends EntityStructure> entities);

/**
* Return the subset of the roles that the current user holds that apply to this entity.
* */
<T extends EntityStructure> Set<String> getRelevantRolesForEntity(T entity);

/**
* Does the role assignment give edit right on the given entity?
* (for unit tests only)
*/
<T extends EntityStructure> boolean canEditEntity(RoleAssignment roleAssignment, T entity);



}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package org.rutebanken.tiamat.auth;

import org.rutebanken.helper.organisation.DataScopedAuthorizationService;
import org.rutebanken.helper.organisation.RoleAssignment;
import org.rutebanken.tiamat.model.EntityStructure;

import java.util.Collection;
import java.util.Set;

import static org.rutebanken.helper.organisation.AuthorizationConstants.ROLE_DELETE_STOPS;
import static org.rutebanken.helper.organisation.AuthorizationConstants.ROLE_EDIT_STOPS;

public class DefaultAuthorizationService implements AuthorizationService {
private final DataScopedAuthorizationService dataScopedAuthorizationService;

public DefaultAuthorizationService(DataScopedAuthorizationService dataScopedAuthorizationService) {
this.dataScopedAuthorizationService = dataScopedAuthorizationService;
}

@Override
public boolean canEditEntities(Collection<? extends EntityStructure> entities) {
return dataScopedAuthorizationService.isAuthorized(ROLE_EDIT_STOPS, entities);
}

@Override
public <T extends EntityStructure> boolean canEditEntity(RoleAssignment roleAssignment, T entity) {
return dataScopedAuthorizationService.authorized(roleAssignment, entity, ROLE_EDIT_STOPS);
}

@Override
public void verifyCanEditEntities(Collection<? extends EntityStructure> entities) {
dataScopedAuthorizationService.assertAuthorized(ROLE_EDIT_STOPS, entities);
}

@Override
public void verifyCanDeleteEntities(Collection<? extends EntityStructure> entities) {
dataScopedAuthorizationService.assertAuthorized(ROLE_DELETE_STOPS, entities);

}

@Override
public <T extends EntityStructure> Set<String> getRelevantRolesForEntity(T entity) {
return dataScopedAuthorizationService.getRelevantRolesForEntity(entity);
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@

package org.rutebanken.tiamat.auth;

import org.rutebanken.helper.organisation.ReflectionAuthorizationService;
import org.rutebanken.tiamat.diff.TiamatObjectDiffer;
import org.rutebanken.tiamat.diff.generic.Difference;
import org.rutebanken.tiamat.model.StopPlace;
Expand All @@ -32,23 +31,21 @@
import java.util.Set;
import java.util.stream.Collectors;

import static org.rutebanken.helper.organisation.AuthorizationConstants.ROLE_EDIT_STOPS;

/**
* This authorization service is implemented mainly for handling multi modal stops.
* Generic authorization logic should be implemented in and handled by {@link ReflectionAuthorizationService}.
* Generic authorization logic should be implemented in and handled by {@link AuthorizationService}.
*/
@Service
public class StopPlaceAuthorizationService {

private static final Logger logger = LoggerFactory.getLogger(StopPlaceAuthorizationService.class);

private final ReflectionAuthorizationService authorizationService;
private final AuthorizationService authorizationService;

private final TiamatObjectDiffer tiamatObjectDiffer;

@Autowired
public StopPlaceAuthorizationService(ReflectionAuthorizationService authorizationService, TiamatObjectDiffer tiamatObjectDiffer) {
public StopPlaceAuthorizationService(AuthorizationService authorizationService, TiamatObjectDiffer tiamatObjectDiffer) {
this.authorizationService = authorizationService;
this.tiamatObjectDiffer = tiamatObjectDiffer;
}
Expand All @@ -65,7 +62,7 @@ public void assertAuthorizedToEdit(StopPlace existingVersion, StopPlace newVersi
* In this situation, the newVersion of the stop must only be populated with the children that are relevant to edit.
* If the newVersion of the stop place contains all current children, and the user does not have authorization to edit those stop places, authorization is not granted.
* <p>
* If the stop place is a normal mono modal stop place, the {@link ReflectionAuthorizationService} will be called directly.
* If the stop place is a normal mono modal stop place, the {@link AuthorizationService} will be called directly.
* @param existingVersion the current version of the stop place, persisted
* @param newVersion the new version of the same stop place, containing the changed state. If type is parent stop place, only child stops that the user would be authorized to change and edit, should be present.
* @param childStopsUpdated
Expand All @@ -76,7 +73,7 @@ public void assertAuthorizedToEdit(StopPlace existingVersion, StopPlace newVersi
// Only child stops that the user has access to should be provided with the new version
// If the stop place already contains children the user does not have access to, the user does not have access to terminate the stop place.

boolean accessToAllChildren = authorizationService.isAuthorized(ROLE_EDIT_STOPS, existingVersion.getChildren());
boolean accessToAllChildren = authorizationService.canEditEntities(existingVersion.getChildren());
if (!accessToAllChildren) {
// This user does not have access to all children.
// Could the user still be allowed to edit a child?
Expand All @@ -91,7 +88,7 @@ public void assertAuthorizedToEdit(StopPlace existingVersion, StopPlace newVersi
}

logger.debug("Must be authorized to edit these children: {}", mustBeAuthorizedToEditTheseChildren.stream().map(child -> child.getNetexId()).collect(Collectors.toList()));
authorizationService.assertAuthorized(ROLE_EDIT_STOPS, mustBeAuthorizedToEditTheseChildren);
authorizationService.verifyCanEditEntities( mustBeAuthorizedToEditTheseChildren);

Set<String> existingChildrenIds = existingVersion.getChildren().stream().map(s -> s.getNetexId()).collect(Collectors.toSet());

Expand All @@ -103,7 +100,7 @@ public void assertAuthorizedToEdit(StopPlace existingVersion, StopPlace newVersi
existingChildrenIds, newVersion.getNetexId(), mustBeAuthorizedToEditTheseChildren);
}
} else {
authorizationService.assertAuthorized(ROLE_EDIT_STOPS, Arrays.asList(newVersion));
authorizationService.verifyCanEditEntities( Arrays.asList(newVersion));
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@

package org.rutebanken.tiamat.config;

import org.rutebanken.helper.organisation.DataScopedAuthorizationService;
import org.rutebanken.helper.organisation.ReflectionAuthorizationService;
import org.rutebanken.helper.organisation.RoleAssignmentExtractor;
import org.rutebanken.tiamat.auth.TiamatEntityResolver;
import org.rutebanken.tiamat.auth.check.TiamatOriganisationChecker;
import org.rutebanken.tiamat.auth.check.TopographicPlaceChecker;
import org.rutebanken.tiamat.auth.AuthorizationService;
import org.rutebanken.tiamat.auth.DefaultAuthorizationService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
Expand All @@ -36,11 +39,16 @@ public class AuthorizationServiceConfig {


@Bean
public ReflectionAuthorizationService getAuthorizationService(RoleAssignmentExtractor roleAssignmentExtractor,
@Value("${authorization.enabled:true}") boolean authorizationEnabled,
TiamatOriganisationChecker tiamatOriganisationChecker,
TopographicPlaceChecker topographicPlaceChecker,
TiamatEntityResolver tiamatEntityResolver) {
public AuthorizationService authorizationService(DataScopedAuthorizationService dataScopedAuthorizationService) {
return new DefaultAuthorizationService(dataScopedAuthorizationService);
}

@Bean
public DataScopedAuthorizationService dataScopedAuthorizationService(RoleAssignmentExtractor roleAssignmentExtractor,
@Value("${authorization.enabled:true}") boolean authorizationEnabled,
TiamatOriganisationChecker tiamatOriganisationChecker,
TopographicPlaceChecker topographicPlaceChecker,
TiamatEntityResolver tiamatEntityResolver) {

// Should be made configurable
Map<String, List<String>> fieldMappings = new HashMap<>();
Expand All @@ -49,5 +57,4 @@ public ReflectionAuthorizationService getAuthorizationService(RoleAssignmentExtr

return new ReflectionAuthorizationService(roleAssignmentExtractor, authorizationEnabled, tiamatOriganisationChecker, topographicPlaceChecker, tiamatEntityResolver, fieldMappings);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

package org.rutebanken.tiamat.importer;

import org.rutebanken.helper.organisation.ReflectionAuthorizationService;
import org.rutebanken.tiamat.auth.AuthorizationService;
import org.rutebanken.tiamat.model.AddressablePlace;
import org.rutebanken.tiamat.model.AddressablePlaceRefStructure;
import org.rutebanken.tiamat.model.PathLink;
Expand All @@ -38,7 +38,6 @@
import java.util.concurrent.atomic.AtomicInteger;

import static java.util.stream.Collectors.toList;
import static org.rutebanken.helper.organisation.AuthorizationConstants.ROLE_EDIT_STOPS;

@Service
@Transactional
Expand All @@ -56,10 +55,10 @@ public class PathLinksImporter {

private final VersionIncrementor versionIncrementor;

private final ReflectionAuthorizationService authorizationService;
private final AuthorizationService authorizationService;

@Autowired
public PathLinksImporter(PathLinkRepository pathLinkRepository, ReferenceResolver referenceResolver, NetexMapper netexMapper, KeyValueListAppender keyValueListAppender, VersionIncrementor versionIncrementor, ReflectionAuthorizationService authorizationService) {
public PathLinksImporter(PathLinkRepository pathLinkRepository, ReferenceResolver referenceResolver, NetexMapper netexMapper, KeyValueListAppender keyValueListAppender, VersionIncrementor versionIncrementor, AuthorizationService authorizationService) {
this.pathLinkRepository = pathLinkRepository;
this.referenceResolver = referenceResolver;
this.netexMapper = netexMapper;
Expand All @@ -78,7 +77,7 @@ public List<org.rutebanken.netex.model.PathLink> importPathLinks(List<PathLink>
if(optionalPathLink.isPresent()) {
PathLink existing = optionalPathLink.get();
// Role edit stops. There could be a role for path links. But pathlinks are used in relation to stop place.
authorizationService.assertAuthorized(ROLE_EDIT_STOPS, Arrays.asList(existing, pathLink));
authorizationService.verifyCanEditEntities( Arrays.asList(existing, pathLink));
boolean changed = keyValueListAppender.appendToOriginalId(NetexIdMapper.ORIGINAL_ID_KEY, pathLink, existing);
if(changed) {
existing.setChanged(Instant.now());
Expand All @@ -87,7 +86,7 @@ public List<org.rutebanken.netex.model.PathLink> importPathLinks(List<PathLink>
versionIncrementor.incrementVersion(existing);
return existing;
} else {
authorizationService.assertAuthorized(ROLE_EDIT_STOPS, Arrays.asList(pathLink));
authorizationService.verifyCanEditEntities( Arrays.asList(pathLink));
logger.debug("No existing path link. Using incoming {}", pathLink);
pathLink.setCreated(Instant.now());
pathLink.setVersion(VersionIncrementor.INITIAL_VERSION);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

import graphql.schema.DataFetcher;
import graphql.schema.DataFetchingEnvironment;
import org.rutebanken.helper.organisation.ReflectionAuthorizationService;
import org.rutebanken.tiamat.auth.AuthorizationService;
import org.rutebanken.tiamat.model.EntityInVersionStructure;
import org.rutebanken.tiamat.model.authorization.AuthorizationResponse;
import org.rutebanken.tiamat.netex.id.TypeFromIdResolver;
Expand All @@ -43,7 +43,7 @@ public class AuthorizationCheckDataFetcher implements DataFetcher {
private TypeFromIdResolver typeFromIdResolver;

@Autowired
private ReflectionAuthorizationService authorizationService;
private AuthorizationService authorizationService;

@Override
public Object get(DataFetchingEnvironment dataFetchingEnvironment) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import graphql.schema.DataFetcher;
import graphql.schema.DataFetchingEnvironment;
import org.locationtech.jts.geom.Point;
import org.rutebanken.helper.organisation.ReflectionAuthorizationService;
import org.rutebanken.tiamat.auth.AuthorizationService;
import org.rutebanken.tiamat.model.EmbeddableMultilingualString;
import org.rutebanken.tiamat.model.Parking;
import org.rutebanken.tiamat.model.ParkingArea;
Expand Down Expand Up @@ -53,7 +53,6 @@
import java.util.Objects;
import java.util.stream.Collectors;

import static org.rutebanken.helper.organisation.AuthorizationConstants.ROLE_EDIT_STOPS;
import static org.rutebanken.tiamat.rest.graphql.GraphQLNames.BOOKING_URL;
import static org.rutebanken.tiamat.rest.graphql.GraphQLNames.FREE_PARKING_OUT_OF_HOURS;
import static org.rutebanken.tiamat.rest.graphql.GraphQLNames.GEOMETRY;
Expand Down Expand Up @@ -100,7 +99,7 @@ class ParkingUpdater implements DataFetcher {
private GeometryMapper geometryMapper;

@Autowired
private ReflectionAuthorizationService authorizationService;
private AuthorizationService authorizationService;

@Autowired
private ValidBetweenMapper validBetweenMapper;
Expand Down Expand Up @@ -139,7 +138,7 @@ private Parking createOrUpdateParking(Map input) {
boolean isUpdated = populateParking(input, updatedParking);

if (isUpdated) {
authorizationService.assertAuthorized(ROLE_EDIT_STOPS, Arrays.asList(existingVersion, updatedParking));
authorizationService.verifyCanEditEntities( Arrays.asList(existingVersion, updatedParking));

logger.info("Saving new version of parking {}", updatedParking);
updatedParking = parkingVersionedSaverService.saveNewVersion(updatedParking);
Expand Down
8 changes: 3 additions & 5 deletions src/main/java/org/rutebanken/tiamat/service/TagCreator.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import org.rutebanken.helper.organisation.ReflectionAuthorizationService;
import org.rutebanken.tiamat.auth.AuthorizationService;
import org.rutebanken.tiamat.auth.UsernameFetcher;
import org.rutebanken.tiamat.model.EntityInVersionStructure;
import org.rutebanken.tiamat.model.StopPlace;
Expand All @@ -34,8 +34,6 @@
import java.util.List;
import java.util.regex.Pattern;

import static org.rutebanken.helper.organisation.AuthorizationConstants.ROLE_EDIT_STOPS;

@Service
public class TagCreator {

Expand All @@ -55,7 +53,7 @@ public class TagCreator {
private ReferenceResolver referenceResolver;

@Autowired
private ReflectionAuthorizationService authorizationService;
private AuthorizationService authorizationService;

public Tag createTag(String tagName, String idReference, String comment) {

Expand All @@ -80,7 +78,7 @@ public Tag createTag(String tagName, String idReference, String comment) {
throw new IllegalArgumentException("The type " + entityInVersionStructure.getClass().getSimpleName() + " is not taggable. Supported types: " + SUPPORTED_TAGGABLE_TYPES);
}

authorizationService.assertAuthorized(ROLE_EDIT_STOPS, Sets.newHashSet(entityInVersionStructure));
authorizationService.verifyCanEditEntities( Sets.newHashSet(entityInVersionStructure));


logger.info("Found entity from reference: {}", entityInVersionStructure);
Expand Down
8 changes: 3 additions & 5 deletions src/main/java/org/rutebanken/tiamat/service/TagRemover.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
package org.rutebanken.tiamat.service;

import com.google.common.collect.Sets;
import org.rutebanken.helper.organisation.ReflectionAuthorizationService;
import org.rutebanken.tiamat.auth.AuthorizationService;
import org.rutebanken.tiamat.auth.UsernameFetcher;
import org.rutebanken.tiamat.model.DataManagedObjectStructure;
import org.rutebanken.tiamat.model.VersionOfObjectRefStructure;
Expand All @@ -31,8 +31,6 @@

import java.time.Instant;

import static org.rutebanken.helper.organisation.AuthorizationConstants.ROLE_EDIT_STOPS;

@Service
@Transactional
public class TagRemover {
Expand All @@ -49,7 +47,7 @@ public class TagRemover {
private ReferenceResolver referenceResolver;

@Autowired
private ReflectionAuthorizationService authorizationService;
private AuthorizationService authorizationService;

public Tag removeTag(String tagName, String idReference, String comment) {
tagName = tagName.toLowerCase().trim();
Expand All @@ -60,7 +58,7 @@ public Tag removeTag(String tagName, String idReference, String comment) {
}

DataManagedObjectStructure dataManagedObjectStructure = referenceResolver.resolve(new VersionOfObjectRefStructure(idReference));
authorizationService.assertAuthorized(ROLE_EDIT_STOPS, Sets.newHashSet(dataManagedObjectStructure));
authorizationService.verifyCanEditEntities( Sets.newHashSet(dataManagedObjectStructure));

tag.setComment(comment);
tag.setRemoved(Instant.now());
Expand Down
Loading

0 comments on commit ed01913

Please sign in to comment.