Merge branch 'permission-vertical' of ssh://trac@sapsailing.com/home/trac/git into permission-vertical

This commit is contained in:
Kai Börnert committed 2018-11-21 17:27:04 +01:00
commit 7b41c741ce
18 files changed
+449 -51

No files matched your search

+1 -7
View File
@@ -97,12 +97,6 @@ extra=''
parallelexecution=0
p2local=0
export max_user_watches=`cat /proc/sys/fs/inotify/max_user_watches`
echo "max User Watches: $max_user_watches"
echo "trying to set max User Watches to 16384"
sysctl fs.inotify.max_user_watches=16384
sysctl -p
if [ $# -eq 0 ]; then
echo "buildAndUpdateProduct [-b -u -g -t -a -r -o -c -p -v -m <config> -n <package> -l <port> -x <gwt-workers> -j <test-package>] [build|install|all|hot-deploy|remote-deploy|local-deploy|release]"
echo ""
@@ -197,7 +191,7 @@ echo TMP will be used for java.io.tmpdir and is $TMP
if [ "$TMP" = "" ]; then
export TMP=/tmp
fi
extra="${extra} -Dgwt.workers=${GWT_WORKERS} -Djava.io.tmpdir=$TMP -Dgwt.watchFileChanges=false"
extra="${extra} -Dgwt.workers=${GWT_WORKERS} -Djava.io.tmpdir=$TMP"
shift $((OPTIND-1))
@@ -5,11 +5,12 @@ import java.io.Serializable;
import com.sap.sailing.domain.common.racelog.tracking.MappableToDevice;
import com.sap.sse.common.Color;
import com.sap.sse.common.Duration;
import com.sap.sse.security.shared.SecuredObject;
/**
* Equality and hash code are based on the {@link #getIdAsString() ID} and all contained attributes like name, shortName, email, etc.
*/
public interface CompetitorDTO extends Serializable, MappableToDevice {
public interface CompetitorDTO extends Serializable, MappableToDevice, SecuredObject {
String getTwoLetterIsoCountryCode();
@@ -4,7 +4,7 @@ import java.io.Serializable;
import com.sap.sse.common.Color;
import com.sap.sse.common.Duration;
import com.sap.sse.security.shared.NamedDTO;
import com.sap.sse.security.shared.NamedSecuredObjectDTO;
/**
* Equality and hash code are based on the {@link #getIdAsString() ID}, the {@link #getSailID() sail number}, the
@@ -15,7 +15,7 @@ import com.sap.sse.security.shared.NamedDTO;
* @author Axel Uhl (d043530)
*
*/
public class CompetitorDTOImpl extends NamedDTO implements CompetitorDTO, Serializable {
public class CompetitorDTOImpl extends NamedSecuredObjectDTO implements CompetitorDTO, Serializable {
private static final long serialVersionUID = 3019126418065082622L;
private String countryName;
private String twoLetterIsoCountryCode;
@@ -2,6 +2,7 @@ package com.sap.sailing.domain.common.dto;
import com.sap.sse.common.Color;
import com.sap.sse.common.Duration;
import com.sap.sse.security.shared.NamedSecuredObjectDTO;
/**
* Equality and hash code are based on the index pointing into a previous leaderboard's competitors list.
@@ -9,7 +10,7 @@ import com.sap.sse.common.Duration;
* @author Axel Uhl (d043530)
*
*/
public class PreviousCompetitorDTOImpl implements CompetitorDTO {
public class PreviousCompetitorDTOImpl extends NamedSecuredObjectDTO implements CompetitorDTO {
private static final long serialVersionUID = 8820028699103040805L;
private int indexInPreviousCompetitorList;
@@ -66,15 +66,23 @@ public class SecuredDomainType extends HasPermissionsImpl {
*/
public static final HasPermissions TRACKED_RACE = new SecuredDomainType("TRACKED_RACE");
public static enum CompetitorAndBoatActions implements Action {
READ_PUBLIC, READ_PRIVATE, LIST;
private static final Action[] ALL_ACTIONS = new Action[] { READ_PUBLIC, READ_PRIVATE, LIST,
DefaultActions.CREATE, DefaultActions.UPDATE, DefaultActions.CHANGE_OWNERSHIP,
DefaultActions.CHANGE_ACL };
};
/**
* type relative identifier is the competitor ID's string representation
*/
public static final HasPermissions COMPETITOR = new SecuredDomainType("COMPETITOR");
public static final HasPermissions COMPETITOR = new SecuredDomainType("COMPETITOR", CompetitorAndBoatActions.ALL_ACTIONS);
/**
* type relative identifier is the boat ID's string representation
*/
public static final HasPermissions BOAT = new SecuredDomainType("BOAT");
public static final HasPermissions BOAT = new SecuredDomainType("BOAT", CompetitorAndBoatActions.ALL_ACTIONS);
/**
* type-relative identifier is the media track's "DB ID"
-1
View File
@@ -163,7 +163,6 @@
<warSourceDirectory>${basedir}/src/main/resources</warSourceDirectory>
<!-- Uncomment the following to obtain details split-point reports
<compileReport>true</compileReport> <detailedSoyc>true</detailedSoyc> -->
<extraJvmArgs>-Dgwt.watchFileChanges=false -Xmx8192m</extraJvmArgs>
</configuration>
<goals>
<goal>compile</goal>
@@ -950,4 +950,10 @@ public interface SailingService extends RemoteService, FileStorageManagementGwtS
void setDefaultTenantForCurrentServer(String tennant);
List<String> getPossibleTennants();
void updateGroupOwnerForEventHierarchy(UUID eventId, UUID newGroupOwnerId, String newGroupNameToCreate,
boolean updateCompetitors, boolean updateBoats);
void updateGroupOwnerForLeaderboardGroupHierarchy(UUID leaderboardGroupId, UUID newGroupOwnerId,
String newGroupNameToCreate, boolean updateCompetitors, boolean updateBoats);
}
@@ -1049,4 +1049,10 @@ public interface SailingServiceAsync extends ServerInfoRetriever, FileStorageMan
* gets a (possibly imcomplete) list of available tennants to choose from.
*/
void getPossibleTennants(AsyncCallback<List<String>> asyncCallback);
void updateGroupOwnerForEventHierarchy(UUID eventId, UUID newGroupOwnerId, String newGroupNameToCreate,
boolean updateCompetitors, boolean updateBoats, AsyncCallback<Void> callback);
void updateGroupOwnerForLeaderboardGroupHierarchy(UUID leaderboardGroupId, UUID newGroupOwnerId,
String newGroupNameToCreate, boolean updateCompetitors, boolean updateBoats, AsyncCallback<Void> callback);
}
@@ -469,6 +469,8 @@ import com.sap.sailing.manage2sail.RegattaResultDescriptor;
import com.sap.sailing.resultimport.ResultUrlProvider;
import com.sap.sailing.resultimport.ResultUrlRegistry;
import com.sap.sailing.server.RacingEventService;
import com.sap.sailing.server.hierarchy.SailingHierarchyOwnershipUpdater;
import com.sap.sailing.server.hierarchy.SailingHierarchyOwnershipUpdater.GroupOwnerUpdateStrategy;
import com.sap.sailing.server.masterdata.MasterDataImporter;
import com.sap.sailing.server.operationaltransformation.AddColumnToLeaderboard;
import com.sap.sailing.server.operationaltransformation.AddColumnToSeries;
@@ -591,10 +593,13 @@ import com.sap.sse.security.ActionWithResult;
import com.sap.sse.security.SecurityService;
import com.sap.sse.security.SessionUtils;
import com.sap.sse.security.shared.HasPermissions.DefaultActions;
import com.sap.sse.security.shared.OwnershipAnnotation;
import com.sap.sse.security.shared.QualifiedObjectIdentifier;
import com.sap.sse.security.shared.RoleDefinition;
import com.sap.sse.security.shared.RoleImpl;
import com.sap.sse.security.shared.User;
import com.sap.sse.security.shared.UserGroup;
import com.sap.sse.security.shared.UserGroupManagementException;
import com.sap.sse.security.shared.WildcardPermission;
import com.sap.sse.security.shared.impl.SecuredSecurityTypes;
import com.sap.sse.security.shared.impl.SecuredSecurityTypes.ServerActions;
@@ -1245,6 +1250,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
List<CompetitorDTO> result = new ArrayList<>();
for (Competitor c : iterable) {
CompetitorDTO competitorDTO = baseDomainFactory.convertToCompetitorDTO(c);
SecurityDTOUtil.addSecurityInformation(getSecurityService(), competitorDTO, c.getIdentifier());
result.add(competitorDTO);
}
return result;
@@ -5479,7 +5485,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
}
private CompetitorWithBoat addOrUpdateCompetitorWithBoatInternal(CompetitorWithBoatDTO competitor) throws URISyntaxException {
CompetitorWithBoat result;
final CompetitorWithBoat result;
CompetitorWithBoat existingCompetitor = getService().getCompetitorAndBoatStore().getExistingCompetitorWithBoatByIdAsString(competitor.getIdAsString());
Nationality nationality = (competitor.getThreeLetterIocCountryCode() == null || competitor.getThreeLetterIocCountryCode().isEmpty()) ? null :
getBaseDomainFactory().getOrCreateNationality(competitor.getThreeLetterIocCountryCode());
@@ -5490,13 +5496,23 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
DynamicTeam team = new TeamImpl(competitor.getName() + " team", Collections.singleton(sailor), null);
// new boat
DynamicBoat boat = (DynamicBoat) addOrUpdateBoatInternal(competitor.getBoat());
result = getBaseDomainFactory().getOrCreateCompetitorWithBoat(competitorUUID, competitor.getName(), competitor.getShortName(),
competitor.getColor(), competitor.getEmail(),
competitor.getFlagImageURL() == null ? null : new URI(competitor.getFlagImageURL()), team,
competitor.getTimeOnTimeFactor(),
competitor.getTimeOnDistanceAllowancePerNauticalMile(), competitor.getSearchTag(), boat);
final UUID competitorUuid = UUID.randomUUID();
result = getSecurityService().setOwnershipCheckPermissionForObjectCreationAndRevertOnError(
SecuredDomainType.COMPETITOR, competitorUuid.toString(), competitor.getName(),
new ActionWithResult<CompetitorWithBoat>() {
@Override
public CompetitorWithBoat run() throws Exception {
return getBaseDomainFactory().getCompetitorAndBoatStore().getOrCreateCompetitorWithBoat(
competitorUUID, competitor.getName(), competitor.getShortName(),
competitor.getColor(), competitor.getEmail(),
competitor.getFlagImageURL() == null ? null : new URI(competitor.getFlagImageURL()),
team, competitor.getTimeOnTimeFactor(),
competitor.getTimeOnDistanceAllowancePerNauticalMile(), competitor.getSearchTag(),
boat);
}
});
} else {
SecurityUtils.getSubject().checkPermission(SecuredDomainType.COMPETITOR.getStringPermissionForObjects(DefaultActions.UPDATE, competitor.getIdAsString()));
Competitor updatedCompetitor = getService().apply(
new UpdateCompetitor(competitor.getIdAsString(), competitor.getName(), competitor.getShortName(),
competitor.getColor(), competitor.getEmail(), nationality,
@@ -5513,29 +5529,39 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
private Competitor addOrUpdateCompetitorWithoutBoatInternal(CompetitorDTO competitor) throws URISyntaxException {
Competitor result;
Competitor existingCompetitor = getService().getCompetitorAndBoatStore().getExistingCompetitorByIdAsString(competitor.getIdAsString());
Nationality nationality = (competitor.getThreeLetterIocCountryCode() == null || competitor.getThreeLetterIocCountryCode().isEmpty()) ? null :
getBaseDomainFactory().getOrCreateNationality(competitor.getThreeLetterIocCountryCode());
final Competitor result;
Competitor existingCompetitor = getService().getCompetitorAndBoatStore()
.getExistingCompetitorByIdAsString(competitor.getIdAsString());
Nationality nationality = (competitor.getThreeLetterIocCountryCode() == null
|| competitor.getThreeLetterIocCountryCode().isEmpty()) ? null
: getBaseDomainFactory().getOrCreateNationality(competitor.getThreeLetterIocCountryCode());
if (competitor.getIdAsString() == null || competitor.getIdAsString().isEmpty() || existingCompetitor == null) {
// new competitor
UUID competitorUUID = UUID.randomUUID();
DynamicPerson sailor = new PersonImpl(competitor.getName(), nationality, null, null);
DynamicTeam team = new TeamImpl(competitor.getName() + " team", Collections.singleton(sailor), null);
result = getBaseDomainFactory().getOrCreateCompetitor(competitorUUID, competitor.getName(), competitor.getShortName(),
competitor.getColor(), competitor.getEmail(),
competitor.getFlagImageURL() == null ? null : new URI(competitor.getFlagImageURL()), team,
competitor.getTimeOnTimeFactor(),
competitor.getTimeOnDistanceAllowancePerNauticalMile(), competitor.getSearchTag());
} else {
result = getService().apply(
new UpdateCompetitor(competitor.getIdAsString(), competitor.getName(), competitor.getShortName(),
competitor.getColor(), competitor.getEmail(), nationality,
competitor.getImageURL() == null ? null : new URI(competitor.getImageURL()),
result = getSecurityService().setOwnershipCheckPermissionForObjectCreationAndRevertOnError(
SecuredDomainType.COMPETITOR, competitorUUID.toString(), competitor.getName(),
new ActionWithResult<Competitor>() {
@Override
public Competitor run() throws Exception {
return getBaseDomainFactory().getOrCreateCompetitor(competitorUUID, competitor.getName(),
competitor.getShortName(), competitor.getColor(), competitor.getEmail(),
competitor.getFlagImageURL() == null ? null : new URI(competitor.getFlagImageURL()),
competitor.getTimeOnTimeFactor(),
competitor.getTimeOnDistanceAllowancePerNauticalMile(),
competitor.getSearchTag()));
team, competitor.getTimeOnTimeFactor(),
competitor.getTimeOnDistanceAllowancePerNauticalMile(), competitor.getSearchTag());
}
});
} else {
SecurityUtils.getSubject().checkPermission(SecuredDomainType.COMPETITOR
.getStringPermissionForObjects(DefaultActions.UPDATE, competitor.getIdAsString()));
result = getService().apply(new UpdateCompetitor(competitor.getIdAsString(), competitor.getName(),
competitor.getShortName(), competitor.getColor(), competitor.getEmail(), nationality,
competitor.getImageURL() == null ? null : new URI(competitor.getImageURL()),
competitor.getFlagImageURL() == null ? null : new URI(competitor.getFlagImageURL()),
competitor.getTimeOnTimeFactor(), competitor.getTimeOnDistanceAllowancePerNauticalMile(),
competitor.getSearchTag()));
}
return result;
}
@@ -5593,10 +5619,20 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
// new boat
UUID boatUUID = UUID.randomUUID();
BoatClass boatClass = getBaseDomainFactory().getOrCreateBoatClass(boat.getBoatClass().getName());
result = getBaseDomainFactory().getOrCreateBoat(boatUUID, boat.getName(), boatClass, boat.getSailId(), boat.getColor());
result = getSecurityService().setOwnershipCheckPermissionForObjectCreationAndRevertOnError(
SecuredDomainType.BOAT, boatUUID.toString(), boat.getName(), new ActionWithResult<Boat>() {
@Override
public Boat run() throws Exception {
return getBaseDomainFactory().getOrCreateBoat(boatUUID, boat.getName(), boatClass,
boat.getSailId(), boat.getColor());
}
});
} else {
result = getService().apply(
new UpdateBoat(boat.getIdAsString(), boat.getName(), boat.getColor(), boat.getSailId()));
SecurityUtils.getSubject().checkPermission(
SecuredDomainType.BOAT.getStringPermissionForObjects(DefaultActions.UPDATE, boat.getIdAsString()));
result = getService()
.apply(new UpdateBoat(boat.getIdAsString(), boat.getName(), boat.getColor(), boat.getSailId()));
}
return result;
}
@@ -8519,4 +8555,73 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
}
return result;
}
@Override
public void updateGroupOwnerForEventHierarchy(UUID eventId, UUID newGroupOwnerId, String newGroupNameToCreate, boolean updateCompetitors,
boolean updateBoats) {
Event event = getService().getEvent(eventId);
UserGroup groupOwnerToSet = getSecurityService().getUserGroup(newGroupOwnerId);
if (event != null && groupOwnerToSet != null) {
createOwnershipUpdater(newGroupOwnerId, newGroupNameToCreate, updateCompetitors,
updateBoats).updateGroupOwnershipForEventHierarchy(event);
}
}
@Override
public void updateGroupOwnerForLeaderboardGroupHierarchy(UUID leaderboardGroupId, UUID newGroupOwnerId, String newGroupNameToCreate,
boolean updateCompetitors, boolean updateBoats) {
LeaderboardGroup leaderboardGroup = getService().getLeaderboardGroupByID(leaderboardGroupId);
UserGroup groupOwnerToSet = getSecurityService().getUserGroup(newGroupOwnerId);
if (leaderboardGroup != null && groupOwnerToSet != null) {
createOwnershipUpdater(newGroupOwnerId, newGroupNameToCreate, updateCompetitors,
updateBoats).updateGroupOwnershipForLeaderboardGroupHierarchy(leaderboardGroup);
}
}
private SailingHierarchyOwnershipUpdater createOwnershipUpdater( UUID newGroupOwnerId, String newGroupNameToCreate, boolean updateCompetitors,
boolean updateBoats) {
final GroupOwnerUpdateStrategy updateStrategy;
if (newGroupOwnerId != null) {
final UserGroup groupOwnerToSet = getSecurityService().getUserGroup(newGroupOwnerId);
if (groupOwnerToSet == null) {
throw new RuntimeException("User group does not exist");
}
updateStrategy = new GroupOwnerUpdateStrategy() {
@Override
public boolean needsUpdate(QualifiedObjectIdentifier identifier, OwnershipAnnotation currentOwnership) {
return currentOwnership == null || !groupOwnerToSet.equals(currentOwnership.getAnnotation().getTenantOwner());
}
@Override
public UserGroup getNewGroupOwner() {
return groupOwnerToSet;
}
};
} else if (newGroupNameToCreate != null && ! newGroupNameToCreate.isEmpty()) {
updateStrategy = new GroupOwnerUpdateStrategy() {
private UserGroup groupOwnerToSet;
@Override
public boolean needsUpdate(QualifiedObjectIdentifier identifier, OwnershipAnnotation currentOwnership) {
return true;
}
@Override
public UserGroup getNewGroupOwner() {
if (groupOwnerToSet != null) {
try {
groupOwnerToSet = getSecurityService().createUserGroup(UUID.randomUUID(), newGroupNameToCreate);
} catch (UserGroupManagementException e) {
throw new RuntimeException("Could not create user group");
}
}
return groupOwnerToSet;
}
};
} else {
throw new RuntimeException("No user group given");
}
return new SailingHierarchyOwnershipUpdater(getService(), getSecurityService(), updateStrategy, updateCompetitors,
updateBoats);
}
}
@@ -124,6 +124,7 @@ import com.sap.sse.common.impl.MillisecondsTimePoint;
import com.sap.sse.common.impl.RGBColor;
import com.sap.sse.common.util.RoundingUtil;
import com.sap.sse.datamining.shared.impl.PredefinedQueryIdentifier;
import com.sap.sse.security.ActionWithResult;
import com.sap.sse.security.SecurityService;
import com.sap.sse.security.shared.HasPermissions.DefaultActions;
import com.sap.sse.security.shared.User;
@@ -357,12 +358,22 @@ public class RegattasResource extends AbstractSailingServerResource {
getService().getBaseDomainFactory().getOrCreateNationality(nationalityThreeLetterIOCCode),
/* dateOfBirth */ null, /* description */ null)),
/* coach */ null);
final CompetitorWithBoat competitor = getService().getCompetitorAndBoatStore()
.getOrCreateCompetitorWithBoat(UUID.randomUUID(), name, /* shortName */ null,
/* displayColor */ null, email, /* flagImageURI */ null, team, timeOnTimeFactor,
timeOnDistanceAllowancePerNauticalMileAsMillis == null ? null
: new MillisecondsDurationImpl(timeOnDistanceAllowancePerNauticalMileAsMillis),
searchTag, boat);
final UUID competitorUuid = UUID.randomUUID();
final CompetitorWithBoat competitor = getSecurityService()
.setOwnershipCheckPermissionForObjectCreationAndRevertOnError(SecuredDomainType.COMPETITOR,
competitorUuid.toString(), name, new ActionWithResult<CompetitorWithBoat>() {
@Override
public CompetitorWithBoat run() throws Exception {
return getService().getCompetitorAndBoatStore().getOrCreateCompetitorWithBoat(
UUID.randomUUID(), name, /* shortName */ null, /* displayColor */ null,
email, /* flagImageURI */ null, team, timeOnTimeFactor,
timeOnDistanceAllowancePerNauticalMileAsMillis == null ? null
: new MillisecondsDurationImpl(
timeOnDistanceAllowancePerNauticalMileAsMillis),
searchTag, boat);
}
});
regatta.registerCompetitor(competitor);
response = Response.ok(CompetitorJsonSerializer.create().serialize(competitor).toJSONString())
.header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
@@ -386,12 +397,25 @@ public class RegattasResource extends AbstractSailingServerResource {
} else {
response = createAndAddCompetitor(regattaName, nationalityThreeLetterIOCCode, timeOnTimeFactor,
timeOnDistanceAllowancePerNauticalMileAsMillis, searchTag, competitorName, competitorEmail,
shortName -> new BoatImpl(UUID.randomUUID(), shortName, getService().getBaseDomainFactory()
.getOrCreateBoatClass(boatClassName, /* typicallyStartsUpwind */ true), sailId));
shortName -> createBoat(shortName, boatClassName, sailId));
}
return response;
}
private DynamicBoat createBoat(String name, String boatClassName, String sailId) {
final UUID boatUUID = UUID.randomUUID();
DynamicBoat boat = getSecurityService().setOwnershipCheckPermissionForObjectCreationAndRevertOnError(
SecuredDomainType.BOAT, boatUUID.toString(), name, new ActionWithResult<DynamicBoat>() {
@Override
public DynamicBoat run() throws Exception {
return new BoatImpl(boatUUID, name, getService().getBaseDomainFactory()
.getOrCreateBoatClass(boatClassName, /* typicallyStartsUpwind */ true), sailId);
}
});
return boat;
}
@POST
@Produces("application/json;charset=UTF-8")
@Path("{regattaname}/competitors/createandaddwithboat")
@@ -43,6 +43,7 @@ Require-Bundle: com.sap.sailing.domain,
com.sap.sse.security.common
Bundle-ClassPath: .
Export-Package: com.sap.sailing.server,
com.sap.sailing.server.hierarchy,
com.sap.sailing.server.impl;
x-friends:="com.sap.sailing.mongodb.test,
com.sap.sailing.server.test,
@@ -0,0 +1,13 @@
package com.sap.sailing.server.hierarchy;
import java.util.Set;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
public interface EventHierarchyVisitor {
void visit(LeaderboardGroup leaderboardGroup);
void visit(Leaderboard leaderboard, Set<LeaderboardGroup> leaderboardGroups);
}
@@ -0,0 +1,11 @@
package com.sap.sailing.server.hierarchy;
import com.sap.sailing.domain.base.Event;
import com.sap.sailing.domain.leaderboard.Leaderboard;
public interface LeaderboardGroupHierarchyVisitor {
void visit(Event event);
void visit(Leaderboard leaderboard);
}
@@ -0,0 +1,15 @@
package com.sap.sailing.server.hierarchy;
import com.sap.sailing.domain.base.Boat;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.tracking.TrackedRace;
public interface LeaderboardHierarchyVisitor {
void visit(TrackedRace race);
void visit(Competitor competitor);
void visit(Boat boat);
}
@@ -0,0 +1,138 @@
package com.sap.sailing.server.hierarchy;
import java.util.HashSet;
import java.util.Set;
import org.apache.shiro.SecurityUtils;
import com.sap.sailing.domain.base.Boat;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Event;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.server.RacingEventService;
import com.sap.sse.security.SecurityService;
import com.sap.sse.security.shared.HasPermissions.DefaultActions;
import com.sap.sse.security.shared.OwnershipAnnotation;
import com.sap.sse.security.shared.QualifiedObjectIdentifier;
import com.sap.sse.security.shared.User;
import com.sap.sse.security.shared.UserGroup;
import com.sap.sse.security.shared.WildcardPermission;
/**
* Encapsulates the logic to ensure consistency of group owners in the sailing domain object hierarchy.
*/
public class SailingHierarchyOwnershipUpdater {
public interface GroupOwnerUpdateStrategy {
boolean needsUpdate(QualifiedObjectIdentifier identifier, OwnershipAnnotation currentOwnership);
UserGroup getNewGroupOwner();
}
private final RacingEventService service;
private final SecurityService securityService;
private final GroupOwnerUpdateStrategy updateStrategy;
private final boolean updateCompetitors;
private final boolean updateBoats;
private final Set<QualifiedObjectIdentifier> objectsToUpdateOwnershipsFor;
public SailingHierarchyOwnershipUpdater(final RacingEventService service, SecurityService securityService,
final GroupOwnerUpdateStrategy updateStrategy, final boolean updateCompetitors, final boolean updateBoats) {
this.service = service;
this.securityService = securityService;
this.updateStrategy = updateStrategy;
this.updateCompetitors = updateCompetitors;
this.updateBoats = updateBoats;
objectsToUpdateOwnershipsFor = new HashSet<>();
}
public void updateGroupOwnershipForEventHierarchy(Event event) {
updateGroupOwnershipForEventHierarchyInternal(event);
commitChanges();
}
private void updateGroupOwnershipForEventHierarchyInternal(Event event) {
updateGroupOwner(event.getIdentifier());
SailingHierarchyWalker.walkFromEvent(event, false, new EventHierarchyVisitor() {
@Override
public void visit(Leaderboard leaderboard, Set<LeaderboardGroup> leaderboardGroups) {
updateGroupOwnershipForLeaderboardHierarchy(leaderboard);
}
@Override
public void visit(LeaderboardGroup leaderboardGroup) {
// No LeaderboardGroups with overall leaderboard are visited -> no infinite recursion occurs
updateGroupOwnershipForLeaderboardGroupHierarchyInternal(leaderboardGroup);
}
});
}
public void updateGroupOwnershipForLeaderboardGroupHierarchy(LeaderboardGroup leaderboardGroup) {
updateGroupOwnershipForLeaderboardGroupHierarchyInternal(leaderboardGroup);
commitChanges();
}
public void updateGroupOwnershipForLeaderboardGroupHierarchyInternal(LeaderboardGroup leaderboardGroup) {
updateGroupOwner(leaderboardGroup.getIdentifier());
SailingHierarchyWalker.walkFromLeaderboardGroup(service, leaderboardGroup, true,
new LeaderboardGroupHierarchyVisitor() {
@Override
public void visit(Leaderboard leaderboard) {
updateGroupOwnershipForLeaderboardHierarchy(leaderboard);
}
@Override
public void visit(Event event) {
// Only events of LeaderboardGroups with overall leaderboard are visited -> no infinite
// recursion occurs
updateGroupOwnershipForEventHierarchyInternal(event);
}
});
}
public void updateGroupOwnershipForLeaderboardHierarchy(Leaderboard leaderboard) {
updateGroupOwner(leaderboard.getIdentifier());
SailingHierarchyWalker.walkFromLeaderboard(leaderboard, new LeaderboardHierarchyVisitor() {
@Override
public void visit(TrackedRace race) {
updateGroupOwner(race.getIdentifier());
}
@Override
public void visit(Boat boat) {
if (updateBoats) {
updateGroupOwner(boat.getIdentifier());
}
}
@Override
public void visit(Competitor competitor) {
if (updateCompetitors) {
updateGroupOwner(competitor.getIdentifier());
}
}
});
}
private void updateGroupOwner(QualifiedObjectIdentifier id) {
final OwnershipAnnotation ownership = securityService.getOwnership(id);
if (updateStrategy.needsUpdate(id, ownership)) {
String permissionToCheck = id.getTypeIdentifier() + WildcardPermission.PART_DIVIDER_TOKEN
+ DefaultActions.CHANGE_OWNERSHIP.name() + WildcardPermission.PART_DIVIDER_TOKEN
+ id.getTypeRelativeObjectIdentifier();
SecurityUtils.getSubject().checkPermission(permissionToCheck);
objectsToUpdateOwnershipsFor.add(id);
}
}
private void commitChanges() {
final UserGroup groupOwnerToSet = updateStrategy.getNewGroupOwner();
for (QualifiedObjectIdentifier id : objectsToUpdateOwnershipsFor) {
final OwnershipAnnotation ownership = securityService.getOwnership(id);
securityService.setOwnership(id, ownership == null ? null : (User) ownership.getAnnotation().getUserOwner(),
groupOwnerToSet);
}
}
}
@@ -0,0 +1,74 @@
package com.sap.sailing.server.hierarchy;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import com.sap.sailing.domain.base.Boat;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Event;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.server.RacingEventService;
import com.sap.sse.common.Util;
/**
* Util to walk the sailing domain object hierarchy using visitors. This util encapsulates specific hierarchy logic
* necessary because the hierarchy is not always obvious and consistent. For event series, the {@link LeaderboardGroup}
* is meant to be the top level domain object and defines a hierarchy containing several {@link Event Events} and
* {@link Leaderboard Leaderboards}. For other event types, the {@link LeaderboardGroup} is only a related object of the
* {@link Event} defining the top level point of a hierarchy.
*/
public final class SailingHierarchyWalker {
private SailingHierarchyWalker() {
}
public static void walkFromEvent(final Event event, final boolean includeLeaderboardGroupsWithOverallLeaderboard,
final EventHierarchyVisitor visitor) {
final Map<Leaderboard, Set<LeaderboardGroup>> leaderboardsToLeaderboardGroups = new HashMap<>();
for (LeaderboardGroup lg : event.getLeaderboardGroups()) {
if (includeLeaderboardGroupsWithOverallLeaderboard || !lg.hasOverallLeaderboard()) {
visitor.visit(lg);
}
for (Leaderboard lb : lg.getLeaderboards()) {
Util.add(leaderboardsToLeaderboardGroups, lb, lg);
}
}
for (Map.Entry<Leaderboard, Set<LeaderboardGroup>> entry : leaderboardsToLeaderboardGroups.entrySet()) {
visitor.visit(entry.getKey(), entry.getValue());
}
}
public static void walkFromLeaderboardGroup(RacingEventService service, final LeaderboardGroup leaderboardGroup,
final boolean includeEventsIfLeaderboardGroupHasOverallLeaderboard,
final LeaderboardGroupHierarchyVisitor visitor) {
boolean visitEvents = includeEventsIfLeaderboardGroupHasOverallLeaderboard
&& leaderboardGroup.hasOverallLeaderboard();
if (visitEvents) {
for (Event event : service.getAllEvents()) {
if (Util.contains(event.getLeaderboardGroups(), leaderboardGroup)) {
visitor.visit(event);
}
}
}
for (Leaderboard lb : leaderboardGroup.getLeaderboards()) {
visitor.visit(lb);
}
}
public static void walkFromLeaderboard(final Leaderboard leaderboard, final LeaderboardHierarchyVisitor visitor) {
for (TrackedRace race : leaderboard.getTrackedRaces()) {
visitor.visit(race);
}
for (Competitor comp : leaderboard.getCompetitors()) {
visitor.visit(comp);
}
for (Boat boat : leaderboard.getAllBoats()) {
visitor.visit(boat);
}
}
}
@@ -1,6 +1,7 @@
package com.sap.sailing.server.security;
import com.sap.sailing.domain.common.security.SecuredDomainType;
import com.sap.sailing.domain.common.security.SecuredDomainType.CompetitorAndBoatActions;
import com.sap.sse.security.shared.HasPermissions.DefaultActions;
import com.sap.sse.security.shared.RolePrototype;
import com.sap.sse.security.shared.WildcardPermission;
@@ -18,7 +19,8 @@ public class SailingViewerRole extends RolePrototype {
super("sailing_viewer", "c42948df-517b-45cb-9fa9-d1e79f18e115",
WildcardPermission.builder().withTypes(SecuredDomainType.EVENT, SecuredDomainType.LEADERBOARD_GROUP,
SecuredDomainType.LEADERBOARD, SecuredDomainType.REGATTA, SecuredDomainType.TRACKED_RACE)
.withActions(DefaultActions.READ).build());
.withActions(DefaultActions.READ).build(), WildcardPermission.builder().withTypes(SecuredDomainType.COMPETITOR, SecuredDomainType.BOAT)
.withActions(CompetitorAndBoatActions.READ_PUBLIC).build());
}
public static SailingViewerRole getInstance() {
+1 -1
View File
@@ -505,7 +505,7 @@
<!-- GWT version detected from dependencyManagement -->
<execution>
<configuration>
<extraJvmArgs>-Dgwt.persistentunitcache=false -Xmx2560m -Djava.io.tmpdir=${env.TMP}</extraJvmArgs>
<extraJvmArgs>-Dgwt.persistentunitcache=false -Xmx2560m -Djava.io.tmpdir=${env.TMP} -Dgwt.watchFileChanges=false</extraJvmArgs>
<fragmentCount>4</fragmentCount>
<!--
<userAgents>chrome</userAgents>