diff --git a/configuration/launchhudsonslave-java11 b/configuration/launchhudsonslave-java11
new file mode 100755
index 00000000000..0dc430eef52
--- /dev/null
+++ b/configuration/launchhudsonslave-java11
@@ -0,0 +1,25 @@
+#!/bin/bash
+AWS=/root/.local/bin/aws
+REGION=eu-west-1
+HUDSON_SLAVE_AMI_ID=ami-0afdd1680ad31c094
+echo Launching instance...
+instanceid=`$AWS ec2 run-instances --image-id $HUDSON_SLAVE_AMI_ID --count 1 --instance-type c5d.4xlarge --key-name Axel --security-groups "Sailing Analytics App" --region $REGION --placement AvailabilityZone=eu-west-1c --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=Hudson Ubuntu Slave Java 11}]' --instance-initiated-shutdown-behavior terminate | tee /tmp/slavelaunch.out | jq .Instances[0].InstanceId | sed -e 's/"//g'`
+if [ "$instanceid" = "" ]; then
+ echo Error launching instance
+ exit 1
+else
+ echo Instance ID is $instanceid
+ while [ "`$AWS ec2 describe-instances --region $REGION --instance-ids $instanceid | jq .Reservations[0].Instances[0].State.Name`" != "\"running\"" ]; do
+ echo Instance $instanceid not running yet\; trying again...
+ sleep 5
+ done
+ echo Instance $instanceid seems running now
+ private_ip=`$AWS ec2 describe-instances --region $REGION --instance-ids $instanceid | jq .Reservations[0].Instances[0].PrivateIpAddress | sed -e 's/"//g'`
+ echo Probing for SSH on private IP $private_ip
+ while su - hudson -c "ssh -o ConnectTimeout=10,StrictHostKeyChecking=no hudson@$private_ip ls >/dev/null"; do
+ echo SSH daemon not reachable yet. Trying again...
+ done
+ echo SSH daemon reached. State should be ready to connect to now.
+ su - hudson -c "ssh -o StrictHostKeyChecking=no hudson@$private_ip \"/opt/sapjvm_8/bin/java -jar slave.jar; sudo /sbin/shutdown -h now\""
+ $AWS ec2 terminate-instances --instance-ids $instanceid
+fi
diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/RaceDTO.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/RaceDTO.java
old mode 100755
new mode 100644
index 5142ada18e0..09067905a19
--- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/RaceDTO.java
+++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/RaceDTO.java
@@ -2,6 +2,11 @@ package com.sap.sailing.domain.common.dto;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.RegattaNameAndRaceName;
+import com.sap.sse.security.shared.dto.AccessControlListDTO;
+import com.sap.sse.security.shared.dto.OwnershipDTO;
+import com.sap.sse.security.shared.dto.SecuredDTO;
+import com.sap.sse.security.shared.dto.SecurityInformationDTO;
+import com.sap.sse.security.shared.impl.WildcardPermissionEncoder;
/**
* Master data about a single race that is to be transferred to the client.
@@ -9,8 +14,10 @@ import com.sap.sailing.domain.common.RegattaNameAndRaceName;
* @author Axel Uhl (d043530)
*
*/
-public class RaceDTO extends BasicRaceDTO {
+public class RaceDTO extends BasicRaceDTO implements SecuredDTO {
private static final long serialVersionUID = 2613189982608149975L;
+
+ private SecurityInformationDTO securityInformation = new SecurityInformationDTO();
/**
* Tells if this race is currently being tracked, meaning that a {@link RaceTracker} is
@@ -30,10 +37,6 @@ public class RaceDTO extends BasicRaceDTO {
public RaceDTO() {}
- public RaceDTO(RegattaAndRaceIdentifier raceIdentifier) {
- this(raceIdentifier, null, false);
- }
-
public RaceDTO(RegattaAndRaceIdentifier raceIdentifier, TrackedRaceDTO trackedRace, boolean isCurrentlyTracked) {
super(raceIdentifier, trackedRace);
this.regattaName = raceIdentifier.getRegattaName();
@@ -93,4 +96,31 @@ public class RaceDTO extends BasicRaceDTO {
return false;
return true;
}
+
+ @Override
+ public final AccessControlListDTO getAccessControlList() {
+ return securityInformation.getAccessControlList();
+ }
+
+ @Override
+ public final OwnershipDTO getOwnership() {
+ return securityInformation.getOwnership();
+ }
+
+ @Override
+ public final void setAccessControlList(final AccessControlListDTO accessControlList) {
+ this.securityInformation.setAccessControlList(accessControlList);
+ }
+
+ @Override
+ public final void setOwnership(final OwnershipDTO ownership) {
+ this.securityInformation.setOwnership(ownership);
+ }
+
+ public String getTypeRelativeIdentifierAsString() {
+ RegattaAndRaceIdentifier regattaAndRaceId = getRaceIdentifier();
+ WildcardPermissionEncoder wildcardPermissionEncoder = new WildcardPermissionEncoder();
+ return wildcardPermissionEncoder.encodeStringList(regattaAndRaceId.getRegattaName(),
+ regattaAndRaceId.getRaceName());
+ }
}
diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/communication/regatta/GetOpenRegattaByRegattaNameAction.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/communication/regatta/GetOpenRegattaByRegattaNameAction.java
index d60eff330ac..ab904ecc50e 100644
--- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/communication/regatta/GetOpenRegattaByRegattaNameAction.java
+++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/communication/regatta/GetOpenRegattaByRegattaNameAction.java
@@ -38,7 +38,7 @@ public class GetOpenRegattaByRegattaNameAction implements SailingAction();
@@ -40,9 +47,8 @@ public abstract class AbstractEventManagementPanel extends AbstractRegattaPanel
this.availableRegattasListBox.ensureDebugId("AvailableRegattasListBox");
// TrackedEventsComposite should exist in every *ManagementPanel.
- trackedRacesListComposite = new TrackedRacesListComposite(null, null, sailingService, errorReporter,
- regattaRefresher,
- stringMessages, /* multiselection */ true, actionButtonsEnabled);
+ trackedRacesListComposite = new TrackedRacesListComposite(null, null, sailingService, userService,
+ errorReporter, regattaRefresher, stringMessages, /* multiselection */ true, actionButtonsEnabled);
trackedRacesListComposite.ensureDebugId("TrackedRacesListComposite");
}
diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractRaceManagementPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractRaceManagementPanel.java
index cd32c191b96..d4ba6e4ea7a 100644
--- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractRaceManagementPanel.java
+++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractRaceManagementPanel.java
@@ -14,6 +14,7 @@ import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
import com.sap.sse.gwt.client.ErrorReporter;
+import com.sap.sse.security.ui.client.UserService;
public abstract class AbstractRaceManagementPanel extends AbstractEventManagementPanel {
protected RegattaAndRaceIdentifier singleSelectedRace;
@@ -26,10 +27,15 @@ public abstract class AbstractRaceManagementPanel extends AbstractEventManagemen
protected final VerticalPanel selectedRaceContentPanel;
- public AbstractRaceManagementPanel(final SailingServiceAsync sailingService,
- ErrorReporter errorReporter,
+ public AbstractRaceManagementPanel(final SailingServiceAsync sailingService, ErrorReporter errorReporter,
RegattaRefresher regattaRefresher, boolean actionButtonsEnabled, StringMessages stringMessages) {
- super(sailingService, regattaRefresher, errorReporter, actionButtonsEnabled, stringMessages);
+ this(sailingService, null, errorReporter, regattaRefresher, actionButtonsEnabled, stringMessages);
+ }
+
+ public AbstractRaceManagementPanel(final SailingServiceAsync sailingService,
+ UserService userService, ErrorReporter errorReporter,
+ RegattaRefresher regattaRefresher, boolean actionButtonsEnabled, StringMessages stringMessages) {
+ super(sailingService, userService, regattaRefresher, errorReporter, actionButtonsEnabled, stringMessages);
VerticalPanel mainPanel = new VerticalPanel();
this.setWidget(mainPanel);
mainPanel.setWidth("100%");
diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractTrackedRacesListComposite.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractTrackedRacesListComposite.java
index 2276205820d..9bdfc9cf4e1 100644
--- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractTrackedRacesListComposite.java
+++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractTrackedRacesListComposite.java
@@ -1,9 +1,12 @@
package com.sap.sailing.gwt.ui.adminconsole;
+import static com.sap.sse.security.shared.HasPermissions.DefaultActions.CHANGE_OWNERSHIP;
+
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
+import java.util.function.Function;
import com.google.gwt.cell.client.AbstractCell;
import com.google.gwt.dom.client.Document;
@@ -37,6 +40,7 @@ import com.sap.sailing.domain.common.RaceIdentifier;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.TrackedRaceStatusEnum;
import com.sap.sailing.domain.common.dto.RaceDTO;
+import com.sap.sailing.domain.common.security.SecuredDomainType;
import com.sap.sailing.gwt.ui.client.RegattaRefresher;
import com.sap.sailing.gwt.ui.client.RegattasDisplayer;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
@@ -54,6 +58,13 @@ import com.sap.sse.gwt.client.shared.components.AbstractCompositeComponent;
import com.sap.sse.gwt.client.shared.components.Component;
import com.sap.sse.gwt.client.shared.components.SettingsDialogComponent;
import com.sap.sse.gwt.client.shared.settings.ComponentContext;
+import com.sap.sse.security.shared.HasPermissions;
+import com.sap.sse.security.ui.client.UserService;
+import com.sap.sse.security.ui.client.component.AccessControlledActionsColumn;
+import com.sap.sse.security.ui.client.component.DefaultActionsImagesBarCell;
+import com.sap.sse.security.ui.client.component.EditOwnershipDialog;
+import com.sap.sse.security.ui.client.component.EditOwnershipDialog.DialogConfig;
+import com.sap.sse.security.ui.client.component.SecuredDTOOwnerColumn;
public abstract class AbstractTrackedRacesListComposite extends AbstractCompositeComponent implements
RegattasDisplayer {
@@ -85,6 +96,8 @@ public abstract class AbstractTrackedRacesListComposite extends AbstractComposit
private ListBox listBoxRegattas;
+ private final UserService userService;
+
public static class AnchorCell extends AbstractCell {
@Override
public void render(com.google.gwt.cell.client.Cell.Context context, SafeHtml safeHtml, SafeHtmlBuilder sb) {
@@ -95,13 +108,14 @@ public abstract class AbstractTrackedRacesListComposite extends AbstractComposit
public AbstractTrackedRacesListComposite(Component> parent, ComponentContext> context,
final SailingServiceAsync sailingService,
final ErrorReporter errorReporter, final RegattaRefresher regattaRefresher,
- final StringMessages stringMessages, boolean hasMultiSelection) {
+ final StringMessages stringMessages, boolean hasMultiSelection, UserService userService) {
super(parent, context);
this.sailingService = sailingService;
this.errorReporter = errorReporter;
this.regattaRefresher = regattaRefresher;
this.multiSelection = hasMultiSelection;
this.stringMessages = stringMessages;
+ this.userService = userService;
}
public void setRegattaFilterValue(String regattaName) {
@@ -395,6 +409,25 @@ public abstract class AbstractTrackedRacesListComposite extends AbstractComposit
raceTable.addColumn(hasGPSDataColumn, stringMessages.gpsData());
raceTable.addColumn(raceStatusColumn, stringMessages.status());
raceTable.addColumn(raceLiveDelayColumn, stringMessages.delayInSeconds());
+
+ if (userService != null) {
+ final SecuredDTOOwnerColumn groupColumn = SecuredDTOOwnerColumn.getGroupOwnerColumn();
+ groupColumn.setSortable(true);
+ final SecuredDTOOwnerColumn userColumn = SecuredDTOOwnerColumn.getUserOwnerColumn();
+ userColumn.setSortable(true);
+
+ final HasPermissions type = SecuredDomainType.TRACKED_RACE;
+ final Function idFactory = race -> race.getTypeRelativeIdentifierAsString();
+ final AccessControlledActionsColumn actionsColumn = new AccessControlledActionsColumn<>(
+ new DefaultActionsImagesBarCell(stringMessages), userService, type, idFactory);
+ final DialogConfig config = EditOwnershipDialog.create(userService.getUserManagementService(), type,
+ idFactory, race -> regattaRefresher.fillRegattas(), stringMessages);
+ actionsColumn.addAction(EventConfigImagesBarCell.ACTION_CHANGE_OWNERSHIP, CHANGE_OWNERSHIP, config::openDialog);
+
+ raceTable.addColumn(groupColumn, stringMessages.group());
+ raceTable.addColumn(userColumn, stringMessages.user());
+ raceTable.addColumn(actionsColumn, stringMessages.actions());
+ }
}
@Override
diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AdminConsoleEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AdminConsoleEntryPoint.java
index 57fb9fc15c6..e694769cd25 100755
--- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AdminConsoleEntryPoint.java
+++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AdminConsoleEntryPoint.java
@@ -159,8 +159,8 @@ public class AdminConsoleEntryPoint extends AbstractSailingEntryPoint implements
final HorizontalTabLayoutPanel racesTabPanel = panel.addVerticalTab(getStringMessages().trackedRaces(), "RacesPanel");
racesTabPanel.ensureDebugId("RacesPanel");
- final TrackedRacesManagementPanel trackedRacesManagementPanel = new TrackedRacesManagementPanel(getSailingService(), this,
- this, getStringMessages());
+ final TrackedRacesManagementPanel trackedRacesManagementPanel = new TrackedRacesManagementPanel(
+ getSailingService(), getUserService(), this, this, getStringMessages());
trackedRacesManagementPanel.ensureDebugId("TrackedRacesManagement");
panel.addToTabPanel(racesTabPanel, new DefaultRefreshableAdminConsolePanel(trackedRacesManagementPanel) {
@Override
diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/EventListComposite.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/EventListComposite.java
index 3ec6af14f23..49cfd473393 100644
--- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/EventListComposite.java
+++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/EventListComposite.java
@@ -1,5 +1,6 @@
package com.sap.sailing.gwt.ui.adminconsole;
+import static com.sap.sailing.domain.common.security.SecuredDomainType.EVENT;
import static com.sap.sse.security.shared.HasPermissions.DefaultActions.CHANGE_OWNERSHIP;
import static com.sap.sse.security.shared.HasPermissions.DefaultActions.DELETE;
import static com.sap.sse.security.shared.HasPermissions.DefaultActions.UPDATE;
@@ -20,8 +21,6 @@ import com.google.gwt.cell.client.AbstractCell;
import com.google.gwt.cell.client.SafeHtmlCell;
import com.google.gwt.cell.client.ValueUpdater;
import com.google.gwt.core.client.GWT;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.safehtml.client.SafeHtmlTemplates;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
@@ -32,17 +31,15 @@ import com.google.gwt.user.cellview.client.CellTable;
import com.google.gwt.user.cellview.client.Column;
import com.google.gwt.user.cellview.client.ColumnSortEvent.ListHandler;
import com.google.gwt.user.cellview.client.TextColumn;
+import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
-import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.view.client.ListDataProvider;
import com.google.gwt.view.client.SelectionChangeEvent;
-import com.sap.sailing.domain.common.security.SecuredDomainType;
import com.sap.sailing.gwt.ui.adminconsole.LeaderboardGroupDialog.LeaderboardGroupDescriptor;
import com.sap.sailing.gwt.ui.client.EntryPointLinkFactory;
import com.sap.sailing.gwt.ui.client.EventsRefresher;
@@ -71,10 +68,10 @@ import com.sap.sse.gwt.client.celltable.SelectionCheckboxColumn;
import com.sap.sse.gwt.client.dialog.DataEntryDialog;
import com.sap.sse.gwt.client.dialog.DataEntryDialog.DialogCallback;
import com.sap.sse.gwt.client.panels.LabeledAbstractFilterablePanel;
-import com.sap.sse.security.shared.HasPermissions;
import com.sap.sse.security.shared.dto.UserDTO;
import com.sap.sse.security.ui.client.UserService;
import com.sap.sse.security.ui.client.component.AccessControlledActionsColumn;
+import com.sap.sse.security.ui.client.component.AccessControlledButtonPanel;
import com.sap.sse.security.ui.client.component.EditOwnershipDialog;
import com.sap.sse.security.ui.client.component.EditOwnershipDialog.DialogConfig;
import com.sap.sse.security.ui.client.component.SecuredDTOOwnerColumn;
@@ -89,18 +86,13 @@ public class EventListComposite extends Composite implements EventsRefresher, Le
private final UserService userService;
private final ErrorReporter errorReporter;
private final StringMessages stringMessages;
-
- private CellTable eventTable;
+ private final CellTable eventTable;
private final RefreshableMultiSelectionModel refreshableEventSelectionModel;
- private ListDataProvider eventListDataProvider;
- private List allEvents;
- private LabeledAbstractFilterablePanel filterTextbox;
- private Button removeEventsButton;
+ private final ListDataProvider eventListDataProvider;
+ private final List allEvents;
+ private final LabeledAbstractFilterablePanel filterTextbox;
private final Label noEventsLabel;
- private final SimplePanel mainPanel;
- private final VerticalPanel panel;
-
private Iterable availableLeaderboardGroups;
public static class AnchorCell extends AbstractCell {
@@ -132,46 +124,22 @@ public class EventListComposite extends Composite implements EventsRefresher, Le
this.regattaRefresher = regattaRefresher;
this.eventsRefresher = eventsRefresher;
this.handleTabSelectable = handleTabSelectable;
- availableLeaderboardGroups = Collections.emptyList();
- allEvents = new ArrayList();
+ this.availableLeaderboardGroups = Collections.emptyList();
+ this.allEvents = new ArrayList();
- mainPanel = new SimplePanel();
- panel = new VerticalPanel();
- mainPanel.setWidget(panel);
+ final VerticalPanel panel = new VerticalPanel();
+ final AccessControlledButtonPanel buttonPanel = new AccessControlledButtonPanel(userService, EVENT);
+ panel.add(buttonPanel);
- HorizontalPanel eventControlsPanel = new HorizontalPanel();
- eventControlsPanel.setSpacing(5);
- panel.add(eventControlsPanel);
+ final Button refresh = buttonPanel.addUnsecuredAction(stringMessages.refresh(), this::fillEvents);
+ refresh.ensureDebugId("RefreshEventsButton");
- Button refreshButton = new Button(stringMessages.refresh());
- refreshButton.ensureDebugId("RefreshEventsButton");
- refreshButton.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- fillEvents();
- }
- });
- eventControlsPanel.add(refreshButton);
+ final Button create = buttonPanel.addCreateAction(stringMessages.actionAddEvent(), this::openCreateEventDialog);
+ create.ensureDebugId("CreateEventButton");
- Button createEventBtn = new Button(stringMessages.actionAddEvent());
- createEventBtn.ensureDebugId("CreateEventButton");
- createEventBtn.addClickHandler(new ClickHandler() {
+ final Button remove = buttonPanel.addRemoveAction(stringMessages.remove(), new Command() {
@Override
- public void onClick(ClickEvent event) {
- openCreateEventDialog();
- }
- });
- eventControlsPanel.add(createEventBtn);
- if (!userService.hasCurrentUserPermissionToCreateObjectOfType(SecuredDomainType.EVENT)) {
- createEventBtn.setVisible(false);
- }
-
- removeEventsButton = new Button(stringMessages.remove());
- removeEventsButton.ensureDebugId("RemoveEventsButton");
- removeEventsButton.setEnabled(false);
- removeEventsButton.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
+ public void execute() {
if(askUserForConfirmation()){
removeEvents(refreshableEventSelectionModel.getSelectedSet());
}
@@ -179,16 +147,15 @@ public class EventListComposite extends Composite implements EventsRefresher, Le
private boolean askUserForConfirmation() {
if(refreshableEventSelectionModel.itemIsSelectedButNotVisible(eventTable.getVisibleItems())){
- final String eventNames = refreshableEventSelectionModel.getSelectedSet().stream().map(e -> e.getName()).collect(Collectors.joining("\n"));
+ final String eventNames = refreshableEventSelectionModel.getSelectedSet().stream()
+ .map(EventDTO::getName).collect(Collectors.joining("\n"));
return Window.confirm(stringMessages.doYouReallyWantToRemoveNonVisibleEvents(eventNames));
}
return Window.confirm(stringMessages.doYouReallyWantToRemoveEvents());
}
});
- eventControlsPanel.add(removeEventsButton);
- if (!userService.hasCurrentUserPermissionToDeleteAnyObjectOfType(SecuredDomainType.EVENT)) {
- removeEventsButton.setVisible(false);
- }
+ remove.setEnabled(false);
+ remove.ensureDebugId("RemoveEventsButton");
eventListDataProvider = new ListDataProvider();
filterTextbox = new LabeledAbstractFilterablePanel(new Label(stringMessages.filterEventsByName()),
@@ -222,10 +189,10 @@ public class EventListComposite extends Composite implements EventsRefresher, Le
this.refreshableEventSelectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {
@Override
public void onSelectionChange(SelectionChangeEvent event) {
- final boolean somethingSelected = !refreshableEventSelectionModel.getSelectedSet().isEmpty();
- removeEventsButton.setEnabled(somethingSelected);
+ remove.setEnabled(!refreshableEventSelectionModel.getSelectedSet().isEmpty());
final int numberOfItemsSelected = refreshableEventSelectionModel.getSelectedSet().size();
- removeEventsButton.setText(numberOfItemsSelected <= 1 ? stringMessages.remove() : stringMessages.removeNumber(numberOfItemsSelected));
+ remove.setText(numberOfItemsSelected <= 1 ? stringMessages.remove()
+ : stringMessages.removeNumber(numberOfItemsSelected));
}
});
panel.add(filterTextbox);
@@ -235,7 +202,7 @@ public class EventListComposite extends Composite implements EventsRefresher, Le
noEventsLabel.setWordWrap(false);
panel.add(noEventsLabel);
fillEvents();
- initWidget(mainPanel);
+ initWidget(panel);
}
private CellTable createEventTable(UserDTO user) {
@@ -362,17 +329,16 @@ public class EventListComposite extends Composite implements EventsRefresher, Le
final SecuredDTOOwnerColumn groupColumn = SecuredDTOOwnerColumn.getGroupOwnerColumn();
final SecuredDTOOwnerColumn userColumn = SecuredDTOOwnerColumn.getUserOwnerColumn();
- final HasPermissions type = SecuredDomainType.EVENT;
final Function idFactory = event -> event.id.toString();
final AccessControlledActionsColumn actionsColumn = new AccessControlledActionsColumn<>(
- new EventConfigImagesBarCell(stringMessages), userService, type, idFactory);
+ new EventConfigImagesBarCell(stringMessages), userService, EVENT, idFactory);
actionsColumn.addAction(EventConfigImagesBarCell.ACTION_UPDATE, UPDATE, this::openEditEventDialog);
actionsColumn.addAction(EventConfigImagesBarCell.ACTION_DELETE, DELETE, event -> {
if (Window.confirm(stringMessages.doYouReallyWantToRemoveEvent(event.getName()))) {
removeEvent(event);
}
});
- final DialogConfig config = EditOwnershipDialog.create(userService.getUserManagementService(), type,
+ final DialogConfig config = EditOwnershipDialog.create(userService.getUserManagementService(), EVENT,
idFactory, event -> fillEvents(), stringMessages);
actionsColumn.addAction(EventConfigImagesBarCell.ACTION_CHANGE_OWNERSHIP, CHANGE_OWNERSHIP, config::openDialog);
diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardConfigImagesBarCell.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardConfigImagesBarCell.java
index fba05f2afb8..1ef95a2fef6 100644
--- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardConfigImagesBarCell.java
+++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardConfigImagesBarCell.java
@@ -32,7 +32,7 @@ public class LeaderboardConfigImagesBarCell extends DefaultActionsImagesBarCell
resources.competitorsIcon()),
new ImageSpec(ACTION_CONFIGURE_URL, stringMessages.actionConfigureUrl(),
resources.settingsActionIcon()),
- getDeleteImageSpec(), getChangeOwnershipImageSpec(),
+ getDeleteImageSpec(), getChangeOwnershipImageSpec(), getChangeACLImageSpec(),
new ImageSpec(ACTION_EXPORT_XML, stringMessages.actionExportXML(), resources.exportXMLIcon()),
new ImageSpec(ACTION_OPEN_COACH_DASHBOARD, stringMessages.actionOpenDashboard(),
resources.openCoachDashboard()),
diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TrackedRacesListComposite.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TrackedRacesListComposite.java
index 70fd97b0bd2..a36683ed86a 100755
--- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TrackedRacesListComposite.java
+++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TrackedRacesListComposite.java
@@ -26,6 +26,7 @@ import com.sap.sse.gwt.client.async.MarkedAsyncCallback;
import com.sap.sse.gwt.client.shared.components.Component;
import com.sap.sse.gwt.client.shared.components.SettingsDialog;
import com.sap.sse.gwt.client.shared.settings.ComponentContext;
+import com.sap.sse.security.ui.client.UserService;
/**
* Shows the currently tracked events/races in a table. Updated if subscribed as an {@link RegattasDisplayer}, e.g., with
@@ -41,10 +42,18 @@ public class TrackedRacesListComposite extends AbstractTrackedRacesListComposite
private boolean actionButtonsEnabled;
public TrackedRacesListComposite(Component> parent, ComponentContext> context,
- final SailingServiceAsync sailingService,
+ final SailingServiceAsync sailingService, final ErrorReporter errorReporter,
+ final RegattaRefresher regattaRefresher, final StringMessages stringMessages, boolean hasMultiSelection,
+ boolean actionButtonsEnabled) {
+ this(parent, context, sailingService, null, errorReporter, regattaRefresher, stringMessages, hasMultiSelection,
+ actionButtonsEnabled);
+ }
+
+ public TrackedRacesListComposite(Component> parent, ComponentContext> context,
+ final SailingServiceAsync sailingService, UserService userService,
final ErrorReporter errorReporter,
final RegattaRefresher regattaRefresher, final StringMessages stringMessages, boolean hasMultiSelection, boolean actionButtonsEnabled) {
- super(parent, context, sailingService, errorReporter, regattaRefresher, stringMessages, hasMultiSelection);
+ super(parent, context, sailingService, errorReporter, regattaRefresher, stringMessages, hasMultiSelection, userService);
this.raceIsTrackedRaceChangeListener = new HashSet();
this.actionButtonsEnabled = actionButtonsEnabled;
createUI();
diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TrackedRacesManagementPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TrackedRacesManagementPanel.java
index d7c70fc7ed6..48ad38cd935 100644
--- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TrackedRacesManagementPanel.java
+++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TrackedRacesManagementPanel.java
@@ -18,6 +18,7 @@ import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sse.gwt.client.ErrorReporter;
import com.sap.sse.gwt.client.dialog.DataEntryDialog.DialogCallback;
+import com.sap.sse.security.ui.client.UserService;
public class TrackedRacesManagementPanel extends AbstractRaceManagementPanel {
private final DateTimeFormatRenderer dateFormatter = new DateTimeFormatRenderer(
@@ -29,9 +30,10 @@ public class TrackedRacesManagementPanel extends AbstractRaceManagementPanel {
private final Grid raceDataGrid;
- public TrackedRacesManagementPanel(final SailingServiceAsync sailingService, ErrorReporter errorReporter,
- RegattaRefresher regattaRefresher, final StringMessages stringMessages) {
- super(sailingService, errorReporter, regattaRefresher, /* actionButtonsEnabled */ true, stringMessages);
+ public TrackedRacesManagementPanel(final SailingServiceAsync sailingService, UserService userService,
+ ErrorReporter errorReporter, RegattaRefresher regattaRefresher, final StringMessages stringMessages) {
+ super(sailingService, userService, errorReporter, regattaRefresher, /* actionButtonsEnabled */ true,
+ stringMessages);
HorizontalPanel controlsPanel = new HorizontalPanel();
Button setStartTimeButton = new Button(stringMessages.setStartTimeReceived(), new ClickHandler() {
diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/tagging/TagInputPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/tagging/TagInputPanel.java
index e8eba9a498e..fd1f9b53e85 100644
--- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/tagging/TagInputPanel.java
+++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/tagging/TagInputPanel.java
@@ -12,6 +12,7 @@ import com.sap.sailing.domain.common.dto.TagDTO;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.raceboard.tagging.TaggingPanelResources.TagPanelStyle;
+import com.sap.sse.common.Util;
import com.sap.sse.gwt.adminconsole.URLFieldWithFileUpload;
import com.sap.sse.gwt.client.dialog.DataEntryDialog;
import com.sap.sse.gwt.client.dialog.DataEntryDialog.DialogCallback;
@@ -266,8 +267,10 @@ public class TagInputPanel extends FlowPanel {
* false
*/
protected boolean compareFieldsToTag(TagDTO tag) {
- return tag != null && getTag().equals(tag.getTag()) && getComment().equals(tag.getComment())
- && getImageURL().equals(tag.getImageURL()) && isVisibleForPublic() == tag.isVisibleForPublic();
+ return tag != null && getTag().equals(tag.getTag()) && Util.equalsWithNull(getComment(), tag.getComment())
+ && (Util.equalsWithNull(getImageURL(), tag.getImageURL())
+ || (getImageURL() == null && tag.getImageURL() == ""))
+ && isVisibleForPublic() == tag.isVisibleForPublic();
}
/**
@@ -279,8 +282,9 @@ public class TagInputPanel extends FlowPanel {
* false
*/
protected boolean compareFieldsToTagButton(TagButton tagButton) {
- return tagButton != null && getTag().equals(tagButton.getTag()) && getComment().equals(tagButton.getComment())
- && getImageURL().equals(tagButton.getImageURL())
+ return tagButton != null && getTag().equals(tagButton.getTag()) && Util.equalsWithNull(getComment(), tagButton.getComment())
+ && (Util.equalsWithNull(getImageURL(), tagButton.getImageURL())
+ || (getImageURL() == null && tagButton.getImageURL() == ""))
&& isVisibleForPublic() == tagButton.isVisibleForPublic();
}
diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java
index ef130fd60ac..feaed8ace3d 100755
--- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java
+++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java
@@ -1241,6 +1241,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
Map competitorAndBoatDTOs = baseDomainFactory.convertToCompetitorAndBoatDTOs(r.getCompetitorsAndTheirBoats());
RaceWithCompetitorsAndBoatsDTO raceDTO = new RaceWithCompetitorsAndBoatsDTO(raceIdentifier, competitorAndBoatDTOs,
trackedRaceDTO, getService().isRaceBeingTracked(regatta, r));
+ SecurityDTOUtil.addSecurityInformation(getSecurityService(), raceDTO, trackedRace.getIdentifier());
if (trackedRace != null) {
getBaseDomainFactory().updateRaceDTOWithTrackedRaceData(trackedRace, raceDTO);
}
@@ -1257,12 +1258,17 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
private List convertToCompetitorDTOs(Iterable extends Competitor> iterable) {
List result = new ArrayList<>();
for (Competitor c : iterable) {
- CompetitorDTO competitorDTO = baseDomainFactory.convertToCompetitorDTO(c);
- SecurityDTOUtil.addSecurityInformation(getSecurityService(), competitorDTO, c.getIdentifier());
+ CompetitorDTO competitorDTO = convertToCompetitorDTO(c);
result.add(competitorDTO);
}
return result;
}
+
+ private CompetitorDTO convertToCompetitorDTO(Competitor competitor) {
+ CompetitorDTO competitorDTO = baseDomainFactory.convertToCompetitorDTO(competitor);
+ SecurityDTOUtil.addSecurityInformation(getSecurityService(), competitorDTO, competitor.getIdentifier());
+ return competitorDTO;
+ }
/**
* Converts the {@link Competitor} objects passed as {@code iterable} to {@link CompetitorWithBoatDTO} objects with an empty boat.
@@ -1298,10 +1304,17 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
List result = new ArrayList();
for (Boat b : iterable) {
BoatDTO boatDTO = baseDomainFactory.convertToBoatDTO(b);
+ SecurityDTOUtil.addSecurityInformation(getSecurityService(), boatDTO, b.getIdentifier());
result.add(boatDTO);
}
return result;
}
+
+ private BoatDTO convertToBoatDTO(Boat boat) {
+ BoatDTO boatDTO = baseDomainFactory.convertToBoatDTO(boat);
+ SecurityDTOUtil.addSecurityInformation(getSecurityService(), boatDTO, boat.getIdentifier());
+ return boatDTO;
+ }
@Override
public com.sap.sse.common.Util.Pair> listTracTracRacesInEvent(String eventJsonURL, boolean listHiddenRaces) throws MalformedURLException, IOException, ParseException, org.json.simple.parser.ParseException, URISyntaxException {
@@ -5500,17 +5513,25 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
}
@Override
- public Iterable getCompetitors(boolean filterCompetitorsWithBoat, boolean filterCompetitorsWithoutBoat) {
- Iterable result;
+ public Iterable getCompetitors(boolean filterCompetitorsWithBoat,
+ boolean filterCompetitorsWithoutBoat) {
+ final List result = new ArrayList<>();
CompetitorAndBoatStore competitorStore = getService().getBaseDomainFactory().getCompetitorAndBoatStore();
if (filterCompetitorsWithBoat == false && filterCompetitorsWithoutBoat == false) {
- result = convertToCompetitorDTOs(competitorStore.getAllCompetitors());
+ getSecurityService().filterObjectsWithPermissionForCurrentUser(SecuredDomainType.COMPETITOR,
+ SecuredDomainType.CompetitorAndBoatActions.READ_PUBLIC, competitorStore.getAllCompetitors(),
+ boat -> boat.getIdentifier().toString(),
+ filteredObject -> result.add(convertToCompetitorDTO(filteredObject)));
} else if (filterCompetitorsWithBoat == true && filterCompetitorsWithoutBoat == false) {
- result = convertToCompetitorDTOs(competitorStore.getCompetitorsWithoutBoat());
+ getSecurityService().filterObjectsWithPermissionForCurrentUser(SecuredDomainType.COMPETITOR,
+ SecuredDomainType.CompetitorAndBoatActions.READ_PUBLIC, competitorStore.getCompetitorsWithoutBoat(),
+ boat -> boat.getIdentifier().toString(),
+ filteredObject -> result.add(convertToCompetitorDTO(filteredObject)));
} else if (filterCompetitorsWithBoat == false && filterCompetitorsWithoutBoat == true) {
- result = convertToCompetitorDTOs(competitorStore.getCompetitorsWithBoat());
- } else {
- result = Collections.emptyList();
+ getSecurityService().filterObjectsWithPermissionForCurrentUser(SecuredDomainType.COMPETITOR,
+ SecuredDomainType.CompetitorAndBoatActions.READ_PUBLIC, competitorStore.getCompetitorsWithBoat(),
+ boat -> boat.getIdentifier().toString(),
+ filteredObject -> result.add(convertToCompetitorDTO(filteredObject)));
}
return result;
}
@@ -5655,12 +5676,24 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
@Override
public Iterable getAllBoats() {
- return convertToBoatDTOs(getService().getBaseDomainFactory().getCompetitorAndBoatStore().getBoats());
+ List result = new ArrayList<>();
+ getSecurityService().filterObjectsWithPermissionForCurrentUser(SecuredDomainType.BOAT,
+ SecuredDomainType.CompetitorAndBoatActions.READ_PUBLIC,
+ getService().getBaseDomainFactory().getCompetitorAndBoatStore().getBoats(),
+ boat -> boat.getIdentifier().toString(),
+ filteredObject -> result.add(convertToBoatDTO(filteredObject)));
+ return result;
}
@Override
public Iterable getStandaloneBoats() {
- return convertToBoatDTOs(getService().getBaseDomainFactory().getCompetitorAndBoatStore().getStandaloneBoats());
+ List result = new ArrayList<>();
+ getSecurityService().filterObjectsWithPermissionForCurrentUser(SecuredDomainType.BOAT,
+ SecuredDomainType.CompetitorAndBoatActions.READ_PUBLIC,
+ getService().getBaseDomainFactory().getCompetitorAndBoatStore().getStandaloneBoats(),
+ boat -> boat.getIdentifier().toString(),
+ filteredObject -> result.add(convertToBoatDTO(filteredObject)));
+ return result;
}
@Override
diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RaceWithCompetitorsAndBoatsDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RaceWithCompetitorsAndBoatsDTO.java
index fc01a9db971..dd03b609d82 100644
--- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RaceWithCompetitorsAndBoatsDTO.java
+++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RaceWithCompetitorsAndBoatsDTO.java
@@ -24,11 +24,6 @@ public class RaceWithCompetitorsAndBoatsDTO extends RaceDTO {
this.competitorsAndBoats = competitorsAndBoats;
}
- public RaceWithCompetitorsAndBoatsDTO(RegattaAndRaceIdentifier raceIdentifier, Map competitorsAndBoats) {
- super(raceIdentifier);
- this.competitorsAndBoats = competitorsAndBoats;
- }
-
public Map getCompetitorsAndBoats() {
return competitorsAndBoats;
}
diff --git a/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/AbstractJaxRsApiTest.java b/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/AbstractJaxRsApiTest.java
index 8ec82b60347..31dc9191138 100644
--- a/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/AbstractJaxRsApiTest.java
+++ b/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/AbstractJaxRsApiTest.java
@@ -41,7 +41,9 @@ import com.sap.sse.mongodb.MongoDBService;
import com.sap.sse.security.Action;
import com.sap.sse.security.ActionWithResult;
import com.sap.sse.security.SecurityService;
+import com.sap.sse.security.shared.OwnershipAnnotation;
import com.sap.sse.security.shared.WithQualifiedObjectIdentifier;
+import com.sap.sse.security.shared.impl.Ownership;
import com.sap.sse.security.shared.impl.User;
import com.sap.sse.security.shared.impl.UserGroup;
@@ -94,9 +96,8 @@ public abstract class AbstractJaxRsApiTest {
}
return null;
}
- }).when(securityService).setOwnershipCheckPermissionForObjectCreationAndRevertOnError(
- Mockito.any(), Mockito.any(), Mockito.any(),
- Mockito.any(ActionWithResult.class));
+ }).when(securityService).setOwnershipCheckPermissionForObjectCreationAndRevertOnError(Mockito.any(),
+ Mockito.any(), Mockito.any(), Mockito.any(ActionWithResult.class));
Mockito.doReturn(true).when(securityService)
.hasCurrentUserReadPermission(Mockito.any(WithQualifiedObjectIdentifier.class));
@@ -105,6 +106,11 @@ public abstract class AbstractJaxRsApiTest {
Mockito.doReturn(true).when(fakeSubject).isAuthenticated();
+ OwnershipAnnotation mockedOwnership = Mockito.mock(OwnershipAnnotation.class);
+ Ownership ownership = Mockito.mock(Ownership.class);
+ Mockito.doReturn(mockedOwnership).when(securityService).getOwnership(Mockito.any());
+ Mockito.doReturn(ownership).when(mockedOwnership).getAnnotation();
+
eventsResource = spyResource(new DummyEventsRessource());
doReturn(getSecurityService()).when(eventsResource).getSecurityService();
diff --git a/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/RegattasResourceTest.java b/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/RegattasResourceTest.java
index 53c853e14d0..b3bf5c69872 100644
--- a/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/RegattasResourceTest.java
+++ b/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/RegattasResourceTest.java
@@ -42,10 +42,12 @@ import com.sap.sailing.domain.base.impl.SeriesImpl;
import com.sap.sailing.domain.common.CompetitorRegistrationType;
import com.sap.sailing.domain.common.RegattaName;
import com.sap.sailing.domain.common.ScoringSchemeType;
+import com.sap.sailing.domain.common.security.SecuredDomainType;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sse.common.Color;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsTimePoint;
+import com.sap.sse.security.ActionWithResult;
import com.sap.sse.security.SecurityService;
import com.sap.sse.security.UserImpl;
import com.sap.sse.security.shared.Account;
@@ -82,17 +84,34 @@ public class RegattasResourceTest extends AbstractJaxRsApiTest {
Series testSeries = new SeriesImpl("TestSeries", /* isMedal */false, /* isFleetsCanRunInParallel */ true,
fleets, raceColumnNames, /* trackedRegattaRegistry */null);
series.add(testSeries);
- racingEventService.createRegatta(closedRegattaName, boatClassName,
- /* canBoatsOfCompetitorsChangePerRace */ true, CompetitorRegistrationType.CLOSED,
- /* registrationLinkSecret */ null, startDate, endDate, UUID.randomUUID(), series, /* persistent */ true,
- DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null,
- /* buoyZoneRadiusInHullLengths */2.0, /* useStartTimeInference */ true,
- /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new);
- racingEventService.createRegatta(openRegattaName, boatClassName, /* canBoatsOfCompetitorsChangePerRace */ true,
- CompetitorRegistrationType.OPEN_MODERATED, secret, startDate, endDate, UUID.randomUUID(), series,
- /* persistent */ true, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null,
- /* buoyZoneRadiusInHullLengths */2.0, /* useStartTimeInference */ true,
- /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new);
+ final UUID closedRegattaUuid = UUID.randomUUID();
+ getSecurityService().setOwnershipCheckPermissionForObjectCreationAndRevertOnError(SecuredDomainType.REGATTA,
+ closedRegattaUuid.toString(), closedRegattaName, new ActionWithResult() {
+ @Override
+ public Regatta run() throws Exception {
+ return racingEventService.createRegatta(closedRegattaName, boatClassName,
+ /* canBoatsOfCompetitorsChangePerRace */ true, CompetitorRegistrationType.CLOSED,
+ /* registrationLinkSecret */ null, startDate, endDate, closedRegattaUuid, series,
+ /* persistent */ true,
+ DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null,
+ /* buoyZoneRadiusInHullLengths */2.0, /* useStartTimeInference */ true,
+ /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new);
+ }
+ });
+ final UUID openRegattaUuid = UUID.randomUUID();
+ getSecurityService().setOwnershipCheckPermissionForObjectCreationAndRevertOnError(SecuredDomainType.REGATTA,
+ openRegattaUuid.toString(), openRegattaName, new ActionWithResult() {
+ @Override
+ public Regatta run() throws Exception {
+ return racingEventService.createRegatta(openRegattaName, boatClassName,
+ /* canBoatsOfCompetitorsChangePerRace */ true,
+ CompetitorRegistrationType.OPEN_MODERATED, secret, startDate, endDate,
+ UUID.randomUUID(), series, /* persistent */ true,
+ DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null,
+ /* buoyZoneRadiusInHullLengths */2.0, /* useStartTimeInference */ true,
+ /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new);
+ }
+ });
testSeries.addRaceColumn("R1", /* trackedRegattaRegistry */ null);
testSeries.addRaceColumn("R2", /* trackedRegattaRegistry */ null);
diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResource.java
index 988b1718020..0099989c1ae 100755
--- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResource.java
+++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResource.java
@@ -762,32 +762,38 @@ public class LeaderboardsResource extends AbstractLeaderboardsResource {
final Waypoint result;
if (when != null) {
final Iterable> positionsAndCogsAndSogs = getPositionsAndCogsAndSogs(trackedRace, when, extrapolate);
- final Bearing averageCourse = getAverageCourse(positionsAndCogsAndSogs);
- final Bearing fromStartBoatToPinEnd = averageCourse.add(new DegreeBearingImpl(-90));
- final Pair leftmostAndRightmostPositionsAtStart = getPositionsFarthestAheadAndFurthestBack(positionsAndCogsAndSogs,
- averageCourse.add(new DegreeBearingImpl(90)));
- final Position farthestAhead = getPositionsFarthestAheadAndFurthestBack(positionsAndCogsAndSogs, averageCourse).getA();
- if (leftmostAndRightmostPositionsAtStart.getA() != null && leftmostAndRightmostPositionsAtStart.getB() != null &&
- farthestAhead != null) {
- final Position startBoatPosition = leftmostAndRightmostPositionsAtStart.getB().translateGreatCircle(
- fromStartBoatToPinEnd.reverse(), LINE_MARGIN);
- final Position pinEndPosition = leftmostAndRightmostPositionsAtStart.getA().translateGreatCircle(
- fromStartBoatToPinEnd, LINE_MARGIN);
- final Mark startBoat = getService().getBaseDomainFactory().getOrCreateMark(UUID.randomUUID(), "Auto "+waypointName+" Boat");
- RegattaLogDefineMarkEventImpl defineStartBoatEvent = new RegattaLogDefineMarkEventImpl(MillisecondsTimePoint.now(),
- getService().getServerAuthor(), when, UUID.randomUUID(), startBoat);
- regattaLog.add(defineStartBoatEvent);
- final Mark pinEnd = getService().getBaseDomainFactory().getOrCreateMark(UUID.randomUUID(), "Auto "+waypointName+" Pin End");
- RegattaLogDefineMarkEventImpl definePinEndEvent = new RegattaLogDefineMarkEventImpl(MillisecondsTimePoint.now(),
- getService().getServerAuthor(), when, UUID.randomUUID(), pinEnd);
- regattaLog.add(definePinEndEvent);
- getRaceLogTrackingAdapter().pingMark(regattaLog, startBoat, new GPSFixImpl(startBoatPosition, when), getService());
- getRaceLogTrackingAdapter().pingMark(regattaLog, pinEnd, new GPSFixImpl(pinEndPosition, when), getService());
- final ControlPoint startLineControlPoint = getService().getBaseDomainFactory().getOrCreateControlPointWithTwoMarks(
- UUID.randomUUID(), "Auto "+waypointName+" Line", pinEnd, startBoat);
- result = getService().getBaseDomainFactory().createWaypoint(startLineControlPoint, PassingInstruction.Line);
+ final Bearing averageCourse = getAverageCourse(positionsAndCogsAndSogs); // may return null, e.g., if COG/SOG information is missing
+ if (averageCourse != null) {
+ final Bearing fromStartBoatToPinEnd = averageCourse.add(new DegreeBearingImpl(-90));
+ final Pair leftmostAndRightmostPositionsAtStart = getPositionsFarthestAheadAndFurthestBack(positionsAndCogsAndSogs,
+ averageCourse.add(new DegreeBearingImpl(90)));
+ final Position farthestAhead = getPositionsFarthestAheadAndFurthestBack(positionsAndCogsAndSogs, averageCourse).getA();
+ if (leftmostAndRightmostPositionsAtStart.getA() != null && leftmostAndRightmostPositionsAtStart.getB() != null &&
+ farthestAhead != null) {
+ final Position startBoatPosition = leftmostAndRightmostPositionsAtStart.getB().translateGreatCircle(
+ fromStartBoatToPinEnd.reverse(), LINE_MARGIN);
+ final Position pinEndPosition = leftmostAndRightmostPositionsAtStart.getA().translateGreatCircle(
+ fromStartBoatToPinEnd, LINE_MARGIN);
+ final Mark startBoat = getService().getBaseDomainFactory().getOrCreateMark(UUID.randomUUID(), "Auto "+waypointName+" Boat");
+ RegattaLogDefineMarkEventImpl defineStartBoatEvent = new RegattaLogDefineMarkEventImpl(MillisecondsTimePoint.now(),
+ getService().getServerAuthor(), when, UUID.randomUUID(), startBoat);
+ regattaLog.add(defineStartBoatEvent);
+ final Mark pinEnd = getService().getBaseDomainFactory().getOrCreateMark(UUID.randomUUID(), "Auto "+waypointName+" Pin End");
+ RegattaLogDefineMarkEventImpl definePinEndEvent = new RegattaLogDefineMarkEventImpl(MillisecondsTimePoint.now(),
+ getService().getServerAuthor(), when, UUID.randomUUID(), pinEnd);
+ regattaLog.add(definePinEndEvent);
+ getRaceLogTrackingAdapter().pingMark(regattaLog, startBoat, new GPSFixImpl(startBoatPosition, when), getService());
+ getRaceLogTrackingAdapter().pingMark(regattaLog, pinEnd, new GPSFixImpl(pinEndPosition, when), getService());
+ final ControlPoint startLineControlPoint = getService().getBaseDomainFactory().getOrCreateControlPointWithTwoMarks(
+ UUID.randomUUID(), "Auto "+waypointName+" Line", pinEnd, startBoat);
+ result = getService().getBaseDomainFactory().createWaypoint(startLineControlPoint, PassingInstruction.Line);
+ } else {
+ result = null;
+ }
} else {
result = null;
+ logger.warning(()->"COG/SOG information for race "+trackedRace.getRace().getName()+" at "+when+
+ " missing. Cannot construct a line for waypoint "+waypointName+" enclosing tracks with unknown course.");
}
} else {
result = null;
diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java
index 0c7deb35938..63a3815227f 100644
--- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java
+++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java
@@ -139,6 +139,7 @@ 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.shared.HasPermissions.DefaultActions;
+import com.sap.sse.security.shared.OwnershipAnnotation;
import com.sap.sse.security.shared.impl.User;
@Path("/v1/regattas")
@@ -370,7 +371,8 @@ public class RegattasResource extends AbstractSailingServerResource {
final User user = getSecurityService().getCurrentUser();
Response response;
final Regatta regatta = findRegattaByName(regattaName);
- if (regatta == null) {
+ OwnershipAnnotation regattaOwnerShipAnnotation = getSecurityService().getOwnership(regatta.getIdentifier());
+ if (regatta == null || regattaOwnerShipAnnotation == null) {
return getBadRegattaErrorResponse(regattaName);
}
boolean registerCompetitor = false;
@@ -478,20 +480,37 @@ public class RegattasResource extends AbstractSailingServerResource {
final String name = eCompetitorName;
final String shortName = eCompetitorShortName;
final String email = eCompetitorEmail;
- final CompetitorWithBoat competitor = getSecurityService()
- .setOwnershipCheckPermissionForObjectCreationAndRevertOnError(SecuredDomainType.COMPETITOR,
- competitorUuid.toString(), name, new ActionWithResult() {
+ final CompetitorWithBoat competitor;
+ if (subject.isAuthenticated()) {
+ competitor = getSecurityService().setOwnershipCheckPermissionForObjectCreationAndRevertOnError(
+ SecuredDomainType.COMPETITOR, competitorUuid.toString(), name,
+ new ActionWithResult() {
@Override
public CompetitorWithBoat run() throws Exception {
return getService().getCompetitorAndBoatStore().getOrCreateCompetitorWithBoat(
- competitorUuid, name, shortName, color,
- email, flagImageURI, team, timeOnTimeFactor,
+ competitorUuid, name, shortName, color, email, flagImageURI, team,
+ timeOnTimeFactor,
timeOnDistanceAllowancePerNauticalMileAsMillis == null ? null
: new MillisecondsDurationImpl(
timeOnDistanceAllowancePerNauticalMileAsMillis),
searchTag, boat);
}
});
+ } else {
+ competitor = getService().getCompetitorAndBoatStore().getOrCreateCompetitorWithBoat(competitorUuid,
+ name, shortName, color, email, flagImageURI, team, timeOnTimeFactor,
+ timeOnDistanceAllowancePerNauticalMileAsMillis == null ? null
+ : new MillisecondsDurationImpl(timeOnDistanceAllowancePerNauticalMileAsMillis),
+ searchTag, boat);
+ getSecurityService().setOwnership(competitor.getIdentifier(),
+ (User) regattaOwnerShipAnnotation.getAnnotation().getUserOwner(),
+ regattaOwnerShipAnnotation.getAnnotation().getTenantOwner(), name);
+ if (getSecurityService().getOwnership(boat.getIdentifier()) == null) {
+ getSecurityService().setOwnership(boat.getIdentifier(),
+ (User) regattaOwnerShipAnnotation.getAnnotation().getUserOwner(),
+ regattaOwnerShipAnnotation.getAnnotation().getTenantOwner(), name);
+ }
+ }
regatta.registerCompetitor(competitor);
response = Response.ok(CompetitorJsonSerializer.create().serialize(competitor).toJSONString())
.header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
@@ -538,7 +557,9 @@ public class RegattasResource extends AbstractSailingServerResource {
private DynamicBoat createBoat(String name, String boatClassName, String sailId) {
final UUID boatUUID = UUID.randomUUID();
- DynamicBoat boat = getSecurityService().setOwnershipCheckPermissionForObjectCreationAndRevertOnError(
+ final DynamicBoat boat;
+ if (SecurityUtils.getSubject().isAuthenticated()) {
+ boat = getSecurityService().setOwnershipCheckPermissionForObjectCreationAndRevertOnError(
SecuredDomainType.BOAT, boatUUID.toString(), name, new ActionWithResult() {
@Override
@@ -547,6 +568,10 @@ public class RegattasResource extends AbstractSailingServerResource {
.getOrCreateBoatClass(boatClassName, /* typicallyStartsUpwind */ true), sailId);
}
});
+ } else {
+ boat = new BoatImpl(boatUUID, name, getService().getBaseDomainFactory().getOrCreateBoatClass(boatClassName,
+ /* typicallyStartsUpwind */ true), sailId);
+ }
return boat;
}
diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java
index c32ea3e7779..0a3a8e29d4c 100755
--- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java
+++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java
@@ -995,6 +995,12 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes
}
securityService.assumeOwnershipMigrated(SecuredDomainType.MEDIA_TRACK.getName(),
SecuredDomainType.getAllInstances());
+ for (Competitor competitor : getCompetitorAndBoatStore().getAllCompetitors()) {
+ securityService.migrateOwnership(competitor, SecuredDomainType.getAllInstances());
+ }
+ for (Boat boat : getCompetitorAndBoatStore().getBoats()) {
+ securityService.migrateOwnership(boat, SecuredDomainType.getAllInstances());
+ }
}
private void loadRaceIDToRegattaAssociations() {
diff --git a/java/com.sap.sse.gwt/resources/com/sap/sse/gwt/client/images/change-acl.png b/java/com.sap.sse.gwt/resources/com/sap/sse/gwt/client/images/change-acl.png
new file mode 100644
index 00000000000..c938b1c13ad
Binary files /dev/null and b/java/com.sap.sse.gwt/resources/com/sap/sse/gwt/client/images/change-acl.png differ
diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/IconResources.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/IconResources.java
index 36441c5caca..65907efc86b 100755
--- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/IconResources.java
+++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/IconResources.java
@@ -8,6 +8,9 @@ public interface IconResources extends ClientBundle {
public static final IconResources INSTANCE = GWT.create(IconResources.class);
+ @Source("images/change-acl.png")
+ ImageResource changeACLIcon();
+
@Source("images/change-ownership.png")
ImageResource changeOwnershipIcon();
diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/UserManagementService.java b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/UserManagementService.java
index fed261c60cc..11aefe2b2bb 100755
--- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/UserManagementService.java
+++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/UserManagementService.java
@@ -149,4 +149,10 @@ public interface UserManagementService extends RemoteService {
public Pair verifySocialUser(CredentialDTO credential) throws OAuthException;
OwnershipAnnotationDTO getOwnership(QualifiedObjectIdentifier idOfOwnedObject);
+
+ AccessControlListDTO overrideAccessControlList(QualifiedObjectIdentifier idOfAccessControlledObject,
+ AccessControlListDTO acl) throws UnauthorizedException;
+
+ AccessControlListDTO getAccessControlListWithoutPruning(QualifiedObjectIdentifier idOfAccessControlledObject)
+ throws UnauthorizedException;
}
diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/UserManagementServiceAsync.java b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/UserManagementServiceAsync.java
index 9f80051f08b..84ba92ab358 100755
--- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/UserManagementServiceAsync.java
+++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/UserManagementServiceAsync.java
@@ -35,6 +35,12 @@ public interface UserManagementServiceAsync {
void updateAccessControlList(QualifiedObjectIdentifier idOfAccessControlledObject,
Map> permissionStrings, AsyncCallback callback);
+ void getAccessControlListWithoutPruning(QualifiedObjectIdentifier idOfAccessControlledObject,
+ AsyncCallback updateAclAsyncCallback);
+
+ void overrideAccessControlList(QualifiedObjectIdentifier idOfAccessControlledObject, AccessControlListDTO acl,
+ AsyncCallback updateAclAsyncCallback);
+
void addToAccessControlList(QualifiedObjectIdentifier idOfAccessControlledObject, String permission, String name,
AsyncCallback callback);
diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/component/AccessControlledButtonPanel.java b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/component/AccessControlledButtonPanel.java
new file mode 100644
index 00000000000..6c549088bce
--- /dev/null
+++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/component/AccessControlledButtonPanel.java
@@ -0,0 +1,103 @@
+package com.sap.sse.security.ui.client.component;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.BiConsumer;
+import java.util.function.Supplier;
+
+import com.google.gwt.dom.client.Style.Unit;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.user.client.Command;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.HorizontalPanel;
+import com.sap.sse.security.shared.HasPermissions;
+import com.sap.sse.security.ui.client.UserService;
+
+/**
+ * Panel where several buttons can be added which are either {@link #addUnsecuredAction(String, Command) unsecured} or
+ * restricted for users with {@link #addCreateAction(String, Command) create} and /or
+ * {@link #addRemoveAction(String, Command) remove} permissions. The visibility of secured buttons depend on the
+ * permissions of the currently logged-in user and changes dynamically.
+ */
+public class AccessControlledButtonPanel extends Composite {
+
+ private final HorizontalPanel panel = new HorizontalPanel();
+ private final Map