mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-11-15 06:49:56 +00:00
Merge branch 'permission-vertical' into permission-vertical-split
Conflicts: java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/EventListComposite.java java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/AbstractJaxRsApiTest.java java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/UserManagementService.java java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/UserManagementServiceAsync.java java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/usermanagement/UserTableWrapper.java
This commit is contained in:
commit
6859918ea7
38 files changed
+1106
-163
No files matched your search
Executable
+25
@@ -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
|
||||
Executable → Regular
+35
-5
@@ -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.<p>
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -38,7 +38,7 @@ public class GetOpenRegattaByRegattaNameAction implements SailingAction<SimpleRe
|
||||
Regatta regatta = ctx.getRacingEventService().getRegattaByName(regattaName);
|
||||
if (regatta == null || !regatta.getCompetitorRegistrationType().isOpen()
|
||||
|| secret.equals(regatta.getRegistrationLinkSecret())) {
|
||||
// throw new DispatchException("Regatta not found or not allowed for this action");
|
||||
throw new DispatchException("Regatta \"" + regattaName + "\" not found or not allowed for this action");
|
||||
}
|
||||
return new SimpleRegattaDTO(regatta.getName());
|
||||
}
|
||||
|
||||
-4
@@ -163,10 +163,6 @@ public class QRCodePresenter {
|
||||
+ BranchIOConstants.SAILINSIGHT_APP_BRANCHIO_PATH + "="
|
||||
+ QRCodePresenter.this.checkInUrl;
|
||||
break;
|
||||
case PUBLIC_INVITE:
|
||||
branchIoUrl = BranchIOConstants.OPEN_REGATTA_APP_BRANCHIO + "?"
|
||||
+ BranchIOConstants.OPEN_REGATTA_APP_BRANCHIO_PATH + "="
|
||||
+ QRCodePresenter.this.checkInUrl;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
+11
-5
@@ -24,6 +24,7 @@ import com.sap.sse.common.Util;
|
||||
import com.sap.sse.gwt.client.ErrorReporter;
|
||||
import com.sap.sse.gwt.client.Notification;
|
||||
import com.sap.sse.gwt.client.Notification.NotificationType;
|
||||
import com.sap.sse.security.ui.client.UserService;
|
||||
|
||||
public abstract class AbstractEventManagementPanel extends AbstractRegattaPanel {
|
||||
protected final TrackedRacesListComposite trackedRacesListComposite;
|
||||
@@ -31,8 +32,14 @@ public abstract class AbstractEventManagementPanel extends AbstractRegattaPanel
|
||||
private final ListBox availableRegattasListBox;
|
||||
|
||||
public AbstractEventManagementPanel(SailingServiceAsync sailingService,
|
||||
RegattaRefresher regattaRefresher,
|
||||
ErrorReporter errorReporter, boolean actionButtonsEnabled, StringMessages stringMessages) {
|
||||
RegattaRefresher regattaRefresher, ErrorReporter errorReporter, boolean actionButtonsEnabled,
|
||||
StringMessages stringMessages) {
|
||||
this(sailingService, null, regattaRefresher, errorReporter, actionButtonsEnabled, stringMessages);
|
||||
}
|
||||
|
||||
public AbstractEventManagementPanel(SailingServiceAsync sailingService, UserService userService,
|
||||
RegattaRefresher regattaRefresher, ErrorReporter errorReporter, boolean actionButtonsEnabled,
|
||||
StringMessages stringMessages) {
|
||||
super(sailingService, regattaRefresher, errorReporter, stringMessages);
|
||||
this.availableRegattas = new ArrayList<RegattaDTO>();
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
|
||||
+9
-3
@@ -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%");
|
||||
|
||||
+34
-1
@@ -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<TrackedRacesSettings> 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<SafeHtml> {
|
||||
@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<RaceDTO> groupColumn = SecuredDTOOwnerColumn.getGroupOwnerColumn();
|
||||
groupColumn.setSortable(true);
|
||||
final SecuredDTOOwnerColumn<RaceDTO> userColumn = SecuredDTOOwnerColumn.getUserOwnerColumn();
|
||||
userColumn.setSortable(true);
|
||||
|
||||
final HasPermissions type = SecuredDomainType.TRACKED_RACE;
|
||||
final Function<RaceDTO, String> idFactory = race -> race.getTypeRelativeIdentifierAsString();
|
||||
final AccessControlledActionsColumn<RaceDTO, DefaultActionsImagesBarCell> actionsColumn = new AccessControlledActionsColumn<>(
|
||||
new DefaultActionsImagesBarCell(stringMessages), userService, type, idFactory);
|
||||
final DialogConfig<RaceDTO> 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
|
||||
|
||||
+2
-2
@@ -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>(trackedRacesManagementPanel) {
|
||||
@Override
|
||||
|
||||
+28
-62
@@ -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<EventDTO> eventTable;
|
||||
private final CellTable<EventDTO> eventTable;
|
||||
private final RefreshableMultiSelectionModel<EventDTO> refreshableEventSelectionModel;
|
||||
private ListDataProvider<EventDTO> eventListDataProvider;
|
||||
private List<EventDTO> allEvents;
|
||||
private LabeledAbstractFilterablePanel<EventDTO> filterTextbox;
|
||||
private Button removeEventsButton;
|
||||
private final ListDataProvider<EventDTO> eventListDataProvider;
|
||||
private final List<EventDTO> allEvents;
|
||||
private final LabeledAbstractFilterablePanel<EventDTO> filterTextbox;
|
||||
private final Label noEventsLabel;
|
||||
|
||||
private final SimplePanel mainPanel;
|
||||
private final VerticalPanel panel;
|
||||
|
||||
private Iterable<LeaderboardGroupDTO> availableLeaderboardGroups;
|
||||
|
||||
public static class AnchorCell extends AbstractCell<SafeHtml> {
|
||||
@@ -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<EventDTO>();
|
||||
this.availableLeaderboardGroups = Collections.emptyList();
|
||||
this.allEvents = new ArrayList<EventDTO>();
|
||||
|
||||
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<EventDTO>();
|
||||
filterTextbox = new LabeledAbstractFilterablePanel<EventDTO>(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<EventDTO> createEventTable(UserDTO user) {
|
||||
@@ -362,17 +329,16 @@ public class EventListComposite extends Composite implements EventsRefresher, Le
|
||||
final SecuredDTOOwnerColumn<EventDTO> groupColumn = SecuredDTOOwnerColumn.getGroupOwnerColumn();
|
||||
final SecuredDTOOwnerColumn<EventDTO> userColumn = SecuredDTOOwnerColumn.getUserOwnerColumn();
|
||||
|
||||
final HasPermissions type = SecuredDomainType.EVENT;
|
||||
final Function<EventDTO, String> idFactory = event -> event.id.toString();
|
||||
final AccessControlledActionsColumn<EventDTO, EventConfigImagesBarCell> 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<EventDTO> config = EditOwnershipDialog.create(userService.getUserManagementService(), type,
|
||||
final DialogConfig<EventDTO> config = EditOwnershipDialog.create(userService.getUserManagementService(), EVENT,
|
||||
idFactory, event -> fillEvents(), stringMessages);
|
||||
actionsColumn.addAction(EventConfigImagesBarCell.ACTION_CHANGE_OWNERSHIP, CHANGE_OWNERSHIP, config::openDialog);
|
||||
|
||||
|
||||
+1
-1
@@ -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()),
|
||||
|
||||
+11
-2
@@ -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<TrackedRaceChangedListener>();
|
||||
this.actionButtonsEnabled = actionButtonsEnabled;
|
||||
createUI();
|
||||
|
||||
+5
-3
@@ -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() {
|
||||
|
||||
+8
-4
@@ -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 {
|
||||
* <code>false</code>
|
||||
*/
|
||||
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 {
|
||||
* <code>false</code>
|
||||
*/
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
+44
-11
@@ -1241,6 +1241,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
|
||||
Map<CompetitorDTO, BoatDTO> 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<CompetitorDTO> convertToCompetitorDTOs(Iterable<? extends Competitor> iterable) {
|
||||
List<CompetitorDTO> 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<BoatDTO> result = new ArrayList<BoatDTO>();
|
||||
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<String, List<TracTracRaceRecordDTO>> 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<CompetitorDTO> getCompetitors(boolean filterCompetitorsWithBoat, boolean filterCompetitorsWithoutBoat) {
|
||||
Iterable<CompetitorDTO> result;
|
||||
public Iterable<CompetitorDTO> getCompetitors(boolean filterCompetitorsWithBoat,
|
||||
boolean filterCompetitorsWithoutBoat) {
|
||||
final List<CompetitorDTO> 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<BoatDTO> getAllBoats() {
|
||||
return convertToBoatDTOs(getService().getBaseDomainFactory().getCompetitorAndBoatStore().getBoats());
|
||||
List<BoatDTO> 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<BoatDTO> getStandaloneBoats() {
|
||||
return convertToBoatDTOs(getService().getBaseDomainFactory().getCompetitorAndBoatStore().getStandaloneBoats());
|
||||
List<BoatDTO> 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
|
||||
|
||||
-5
@@ -24,11 +24,6 @@ public class RaceWithCompetitorsAndBoatsDTO extends RaceDTO {
|
||||
this.competitorsAndBoats = competitorsAndBoats;
|
||||
}
|
||||
|
||||
public RaceWithCompetitorsAndBoatsDTO(RegattaAndRaceIdentifier raceIdentifier, Map<CompetitorDTO, BoatDTO> competitorsAndBoats) {
|
||||
super(raceIdentifier);
|
||||
this.competitorsAndBoats = competitorsAndBoats;
|
||||
}
|
||||
|
||||
public Map<CompetitorDTO, BoatDTO> getCompetitorsAndBoats() {
|
||||
return competitorsAndBoats;
|
||||
}
|
||||
|
||||
+9
-3
@@ -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();
|
||||
|
||||
|
||||
+30
-11
@@ -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<Regatta>() {
|
||||
@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<Regatta>() {
|
||||
@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);
|
||||
|
||||
|
||||
+30
-24
@@ -762,32 +762,38 @@ public class LeaderboardsResource extends AbstractLeaderboardsResource {
|
||||
final Waypoint result;
|
||||
if (when != null) {
|
||||
final Iterable<Pair<Position, SpeedWithBearing>> positionsAndCogsAndSogs = getPositionsAndCogsAndSogs(trackedRace, when, extrapolate);
|
||||
final Bearing averageCourse = getAverageCourse(positionsAndCogsAndSogs);
|
||||
final Bearing fromStartBoatToPinEnd = averageCourse.add(new DegreeBearingImpl(-90));
|
||||
final Pair<Position, Position> 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<Position, Position> 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;
|
||||
|
||||
+32
-7
@@ -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<CompetitorWithBoat>() {
|
||||
final CompetitorWithBoat competitor;
|
||||
if (subject.isAuthenticated()) {
|
||||
competitor = getSecurityService().setOwnershipCheckPermissionForObjectCreationAndRevertOnError(
|
||||
SecuredDomainType.COMPETITOR, competitorUuid.toString(), name,
|
||||
new ActionWithResult<CompetitorWithBoat>() {
|
||||
@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<DynamicBoat>() {
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
|
||||
+6
@@ -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() {
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
@@ -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();
|
||||
|
||||
|
||||
+6
@@ -149,4 +149,10 @@ public interface UserManagementService extends RemoteService {
|
||||
public Pair<UserDTO, UserDTO> verifySocialUser(CredentialDTO credential) throws OAuthException;
|
||||
|
||||
OwnershipAnnotationDTO getOwnership(QualifiedObjectIdentifier idOfOwnedObject);
|
||||
|
||||
AccessControlListDTO overrideAccessControlList(QualifiedObjectIdentifier idOfAccessControlledObject,
|
||||
AccessControlListDTO acl) throws UnauthorizedException;
|
||||
|
||||
AccessControlListDTO getAccessControlListWithoutPruning(QualifiedObjectIdentifier idOfAccessControlledObject)
|
||||
throws UnauthorizedException;
|
||||
}
|
||||
+6
@@ -35,6 +35,12 @@ public interface UserManagementServiceAsync {
|
||||
void updateAccessControlList(QualifiedObjectIdentifier idOfAccessControlledObject,
|
||||
Map<String, Set<String>> permissionStrings, AsyncCallback<AccessControlListDTO> callback);
|
||||
|
||||
void getAccessControlListWithoutPruning(QualifiedObjectIdentifier idOfAccessControlledObject,
|
||||
AsyncCallback<AccessControlListDTO> updateAclAsyncCallback);
|
||||
|
||||
void overrideAccessControlList(QualifiedObjectIdentifier idOfAccessControlledObject, AccessControlListDTO acl,
|
||||
AsyncCallback<AccessControlListDTO> updateAclAsyncCallback);
|
||||
|
||||
void addToAccessControlList(QualifiedObjectIdentifier idOfAccessControlledObject, String permission, String name,
|
||||
AsyncCallback<AccessControlListDTO> callback);
|
||||
|
||||
|
||||
+103
@@ -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<Button, Supplier<Boolean>> buttonToPermissions = new HashMap<>();
|
||||
|
||||
private final Supplier<Boolean> createPermissionCheck, removePermissionCheck;
|
||||
private final BiConsumer<Button, Supplier<Boolean>> visibilityUpdater = (btn, check) -> btn.setVisible(check.get());
|
||||
|
||||
/**
|
||||
* Creates an {@link AccessControlledButtonPanel} instance for the given {@link HasPermissions type} using the
|
||||
* provided {@link UserService} to check permissions and register for user status changes.
|
||||
*
|
||||
* @param userService
|
||||
* the {@link UserService} to check permissions and register for user status changes
|
||||
* @param type
|
||||
* the {@link HasPermissions} representing the type of objects to be secured by this panel
|
||||
*/
|
||||
public AccessControlledButtonPanel(final UserService userService, final HasPermissions type) {
|
||||
this.createPermissionCheck = () -> userService.hasCurrentUserPermissionToCreateObjectOfType(type);
|
||||
this.removePermissionCheck = () -> userService.hasCurrentUserPermissionToDeleteAnyObjectOfType(type);
|
||||
userService.addUserStatusEventHandler((user, preAuth) -> buttonToPermissions.forEach(visibilityUpdater), true);
|
||||
initWidget(panel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an unsecured action button, which's visibility is independent from the current user's permissions.
|
||||
*
|
||||
* @param text
|
||||
* the {@link String text} to show on the button
|
||||
* @param callback
|
||||
* the {@link Command callback} to execute on button click
|
||||
* @return the created {@link Button} instance
|
||||
*/
|
||||
public Button addUnsecuredAction(final String text, final Command callback) {
|
||||
return addAction(text, () -> true, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an secured action button, which is only visible if the current user has the
|
||||
* {@link UserService#hasCurrentUserPermissionToCreateObjectOfType(HasPermissions) create permission} for the
|
||||
* {@link HasPermissions type} provided in this {@link AccessControlledButtonPanel}'s constructor.
|
||||
*
|
||||
* @param text
|
||||
* the {@link String text} to show on the button
|
||||
* @param callback
|
||||
* the {@link Command callback} to execute on button click, if permission is granted
|
||||
* @return the created {@link Button} instance
|
||||
*/
|
||||
public Button addCreateAction(final String text, final Command callback) {
|
||||
return addAction(text, createPermissionCheck, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an secured action button, which is only visible if the current user has any
|
||||
* {@link UserService#hasCurrentUserPermissionToDeleteAnyObjectOfType(HasPermissions) delete permission} for the
|
||||
* {@link HasPermissions type} provided in this {@link AccessControlledButtonPanel}'s constructor.
|
||||
*
|
||||
* @param text
|
||||
* the {@link String text} to show on the button
|
||||
* @param callback
|
||||
* the {@link Command callback} to execute on button click, if permission is granted
|
||||
* @return the created {@link Button} instance
|
||||
*/
|
||||
public Button addRemoveAction(final String text, final Command callback) {
|
||||
return addAction(text, removePermissionCheck, callback);
|
||||
}
|
||||
|
||||
private Button addAction(final String text, final Supplier<Boolean> permissionCheck, final Command callback) {
|
||||
final Button button = new Button(text, (ClickHandler) event -> {
|
||||
if (permissionCheck.get()) {
|
||||
callback.execute();
|
||||
}
|
||||
});
|
||||
this.buttonToPermissions.put(button, permissionCheck);
|
||||
button.getElement().getStyle().setMarginRight(5, Unit.PX);
|
||||
this.panel.add(button);
|
||||
this.visibilityUpdater.accept(button, permissionCheck);
|
||||
return button;
|
||||
}
|
||||
|
||||
}
|
||||
+4
-3
@@ -23,7 +23,8 @@ public class DefaultActionsImagesBarCell extends ImagesBarCell {
|
||||
|
||||
@Override
|
||||
protected Iterable<ImageSpec> getImageSpecs() {
|
||||
return Arrays.asList(getUpdateImageSpec(), getDeleteImageSpec(), getChangeOwnershipImageSpec());
|
||||
return Arrays.asList(getUpdateImageSpec(), getDeleteImageSpec(), getChangeOwnershipImageSpec(),
|
||||
getChangeACLImageSpec());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -60,8 +61,8 @@ public class DefaultActionsImagesBarCell extends ImagesBarCell {
|
||||
* @return {@link ImageSpec} for {@link DefaultActions#CHANGE_ACL change acl} action
|
||||
*/
|
||||
protected ImageSpec getChangeACLImageSpec() {
|
||||
return new ImageSpec(ACTION_CHANGE_OWNERSHIP, stringMessages.actionChangeOwnership(),
|
||||
IconResources.INSTANCE.changeOwnershipIcon());
|
||||
return new ImageSpec(ACTION_CHANGE_ACL, stringMessages.actionChangeACL(),
|
||||
IconResources.INSTANCE.changeACLIcon());
|
||||
}
|
||||
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
.allowedActionsTable {
|
||||
margin-right: 5px;
|
||||
margin-left: 5px;
|
||||
margin-top: 10px;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.deniedActionsTable {
|
||||
margin-right: 5px;
|
||||
margin-left: 5px;
|
||||
margin-top: 10px;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.additionalWidget {
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.userGroupTextBox {
|
||||
min-width: 200px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package com.sap.sse.security.ui.client.component.editacl;
|
||||
|
||||
import com.google.gwt.core.client.GWT;
|
||||
import com.google.gwt.resources.client.ClientBundle;
|
||||
import com.google.gwt.resources.client.CssResource;
|
||||
|
||||
/**
|
||||
* Resources used by the authentication framework.
|
||||
*/
|
||||
public interface AclDialogResources extends ClientBundle {
|
||||
|
||||
public static final AclDialogResources INSTANCE = GWT.create(AclDialogResources.class);
|
||||
|
||||
@Source("AclDialogResources.gss")
|
||||
LocalCss css();
|
||||
|
||||
public interface LocalCss extends CssResource {
|
||||
String allowedActionsTable();
|
||||
|
||||
String deniedActionsTable();
|
||||
|
||||
String additionalWidget();
|
||||
|
||||
String userGroupTextBox();
|
||||
}
|
||||
}
|
||||
+346
@@ -0,0 +1,346 @@
|
||||
package com.sap.sse.security.ui.client.component.editacl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.google.gwt.cell.client.AbstractCell;
|
||||
import com.google.gwt.core.client.GWT;
|
||||
import com.google.gwt.event.dom.client.ClickEvent;
|
||||
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
|
||||
import com.google.gwt.uibinder.client.UiBinder;
|
||||
import com.google.gwt.uibinder.client.UiField;
|
||||
import com.google.gwt.user.cellview.client.CellList;
|
||||
import com.google.gwt.user.cellview.client.SimplePager;
|
||||
import com.google.gwt.user.cellview.client.SimplePager.TextLocation;
|
||||
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.CaptionPanel;
|
||||
import com.google.gwt.user.client.ui.Composite;
|
||||
import com.google.gwt.user.client.ui.FlowPanel;
|
||||
import com.google.gwt.user.client.ui.MultiWordSuggestOracle;
|
||||
import com.google.gwt.user.client.ui.ScrollPanel;
|
||||
import com.google.gwt.user.client.ui.SuggestBox;
|
||||
import com.google.gwt.user.client.ui.TextBox;
|
||||
import com.google.gwt.user.client.ui.VerticalPanel;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.google.gwt.view.client.SingleSelectionModel;
|
||||
import com.sap.sse.gwt.client.Notification;
|
||||
import com.sap.sse.gwt.client.Notification.NotificationType;
|
||||
import com.sap.sse.gwt.client.controls.listedit.StringListEditorComposite;
|
||||
import com.sap.sse.security.shared.HasPermissions.Action;
|
||||
import com.sap.sse.security.shared.dto.AccessControlListDTO;
|
||||
import com.sap.sse.security.shared.dto.UserGroupDTO;
|
||||
import com.sap.sse.security.shared.impl.AccessControlList;
|
||||
import com.sap.sse.security.shared.impl.UserGroup;
|
||||
import com.sap.sse.security.ui.client.UserManagementServiceAsync;
|
||||
import com.sap.sse.security.ui.client.i18n.StringMessages;
|
||||
|
||||
public class AclEditPanel extends Composite {
|
||||
|
||||
private static AclEditPanelUiBinder uiBinder = GWT.create(AclEditPanelUiBinder.class);
|
||||
|
||||
interface AclEditPanelUiBinder extends UiBinder<Widget, AclEditPanel> {
|
||||
}
|
||||
|
||||
@UiField
|
||||
FlowPanel userGroupCellListPanelUi;
|
||||
@UiField
|
||||
FlowPanel permissionsCellListPanelUi;
|
||||
|
||||
private final Button removeUserGroupButtonUi;
|
||||
private final Button addUserGroupButtonUi;
|
||||
private final SuggestBox suggestUserGroupUi;
|
||||
|
||||
private final StringMessages stringMessages;
|
||||
|
||||
private final StringListEditorComposite allowedActionsEditor;
|
||||
private final StringListEditorComposite deniedActionsEditor;
|
||||
|
||||
private final SingleSelectionModel<UserGroupDTO> userGroupSingleSelectionModel = new SingleSelectionModel<>();
|
||||
private CellList<UserGroupDTO> userGroupList;
|
||||
|
||||
// denied actions start with '!'
|
||||
private Map<UserGroupDTO, Set<String>> userGroupsWithAllowedActions = new HashMap<>();
|
||||
private Map<UserGroupDTO, Set<String>> userGroupsWithDeniedActions = new HashMap<>();
|
||||
private UserManagementServiceAsync userManagementService;
|
||||
|
||||
public AclEditPanel(UserManagementServiceAsync userManagementService, Action[] availableActions,
|
||||
StringMessages stringMessages) {
|
||||
this.stringMessages = stringMessages;
|
||||
this.userManagementService = userManagementService;
|
||||
AclDialogResources.INSTANCE.css().ensureInjected();
|
||||
|
||||
suggestUserGroupUi = createUserGroupSuggest(userManagementService);
|
||||
addUserGroupButtonUi = new Button(stringMessages.add());
|
||||
removeUserGroupButtonUi = new Button(stringMessages.remove());
|
||||
addUserGroupButtonUi.addClickHandler(e -> onUserGroupAdd(e));
|
||||
removeUserGroupButtonUi.addClickHandler(e -> onUserGroupRemove(e));
|
||||
|
||||
initWidget(uiBinder.createAndBindUi(this));
|
||||
userGroupList = createUserGroupCellList();
|
||||
userGroupCellListPanelUi.add(wrapIntoCaptionPanel(userGroupList, stringMessages.userGroups(),
|
||||
suggestUserGroupUi, addUserGroupButtonUi, removeUserGroupButtonUi));
|
||||
|
||||
userGroupSingleSelectionModel.addSelectionChangeHandler(h -> {
|
||||
onUserGroupsChange();
|
||||
});
|
||||
|
||||
// retrieve set of available action names
|
||||
final Collection<String> actionNames = new ArrayList<>();
|
||||
for (Action a : availableActions) {
|
||||
actionNames.add(a.name());
|
||||
}
|
||||
|
||||
// create action editor for allowed actions
|
||||
allowedActionsEditor = new StringListEditorComposite(new ArrayList<>(), stringMessages,
|
||||
com.sap.sse.gwt.client.IconResources.INSTANCE.removeIcon(), actionNames,
|
||||
stringMessages.allowedActionName());
|
||||
allowedActionsEditor.addValueChangeHandler(e -> userGroupsWithAllowedActions
|
||||
.put(userGroupSingleSelectionModel.getSelectedObject(), toSet(e.getValue())));
|
||||
|
||||
CaptionPanel allowedActionsPanel = new CaptionPanel(stringMessages.allowedActions());
|
||||
allowedActionsPanel.add(allowedActionsEditor);
|
||||
allowedActionsPanel.addStyleName(AclDialogResources.INSTANCE.css().allowedActionsTable());
|
||||
permissionsCellListPanelUi.add(allowedActionsPanel);
|
||||
|
||||
// create action editor for denied actions
|
||||
deniedActionsEditor = new StringListEditorComposite(new ArrayList<>(), stringMessages,
|
||||
com.sap.sse.gwt.client.IconResources.INSTANCE.removeIcon(), actionNames,
|
||||
stringMessages.deniedActionName());
|
||||
deniedActionsEditor.addValueChangeHandler(e -> userGroupsWithDeniedActions
|
||||
.put(userGroupSingleSelectionModel.getSelectedObject(), toDeniedActionSet(e.getValue())));
|
||||
|
||||
CaptionPanel deniedActionsPanel = new CaptionPanel(stringMessages.deniedActions());
|
||||
deniedActionsPanel.add(deniedActionsEditor);
|
||||
deniedActionsPanel.addStyleName(AclDialogResources.INSTANCE.css().deniedActionsTable());
|
||||
permissionsCellListPanelUi.add(deniedActionsPanel);
|
||||
}
|
||||
|
||||
/** Called when the selected {@link UserGroup} changes. */
|
||||
private void onUserGroupsChange() {
|
||||
removeUserGroupButtonUi.setEnabled(userGroupSingleSelectionModel.getSelectedObject() != null);
|
||||
}
|
||||
|
||||
/** @return UI element for selection of {@link UserGroup} elements. */
|
||||
private SuggestBox createUserGroupSuggest(UserManagementServiceAsync userManagementService) {
|
||||
final MultiWordSuggestOracle userGroupOracle = new MultiWordSuggestOracle();
|
||||
userManagementService.getUserGroups(new AsyncCallback<Collection<UserGroupDTO>>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
Window.alert(caught.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Collection<UserGroupDTO> result) {
|
||||
final List<String> suggestionList = result.stream().map(UserGroupDTO::getName)
|
||||
.collect(Collectors.toList());
|
||||
userGroupOracle.clear();
|
||||
userGroupOracle.addAll(suggestionList);
|
||||
userGroupOracle.setDefaultSuggestionsFromText(suggestionList);
|
||||
}
|
||||
});
|
||||
TextBox textBox = new TextBox();
|
||||
textBox.getElement().setPropertyString("placeholder", stringMessages.enterUserGroupName());
|
||||
|
||||
SuggestBox suggestBox = new SuggestBox(userGroupOracle, textBox);
|
||||
suggestBox.addStyleName(AclDialogResources.INSTANCE.css().userGroupTextBox());
|
||||
return suggestBox;
|
||||
}
|
||||
|
||||
/** @return the UI element for visualizing {@link UserGroup} elements. */
|
||||
private CellList<UserGroupDTO> createUserGroupCellList() {
|
||||
final CellList<UserGroupDTO> userGroupCellList = new CellList<>(new AbstractCell<UserGroupDTO>() {
|
||||
@Override
|
||||
public void render(Context context, UserGroupDTO value, SafeHtmlBuilder sb) {
|
||||
if (value != null) {
|
||||
sb.appendEscaped(value.getName());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
userGroupCellList.setSelectionModel(userGroupSingleSelectionModel);
|
||||
userGroupSingleSelectionModel
|
||||
.addSelectionChangeHandler(e -> updateActionEditors(userGroupSingleSelectionModel.getSelectedObject()));
|
||||
return userGroupCellList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the {@link #allowedActionsEditor} and {@link #deniedActionsEditor} when the selected UserGroup changed.
|
||||
*/
|
||||
private void updateActionEditors(UserGroupDTO selectedUserGroup) {
|
||||
onUserGroupsChange();
|
||||
allowedActionsEditor.setValue(userGroupsWithAllowedActions.get(selectedUserGroup), false);
|
||||
deniedActionsEditor
|
||||
.setValue(removeExclamationMarkFromStrings(userGroupsWithDeniedActions.get(selectedUserGroup)), false);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the CellList together with a title and additional widgets (e.g. add/remove buttons) into a
|
||||
* {@link CaptionPanel}.
|
||||
*/
|
||||
private CaptionPanel wrapIntoCaptionPanel(CellList<?> cellList, String title, Widget... additionalWidgets) {
|
||||
cellList.setPageSize(10);
|
||||
final SimplePager tenantPager = new SimplePager(TextLocation.CENTER, false, /* fast forward step size */ 50,
|
||||
true);
|
||||
tenantPager.setDisplay(cellList);
|
||||
final ScrollPanel tenantPanel = new ScrollPanel(cellList);
|
||||
final VerticalPanel tenantListWrapper = new VerticalPanel();
|
||||
|
||||
// add additional widgets
|
||||
final FlowPanel additionalWidgetsPanel = new FlowPanel();
|
||||
for (Widget additionalWidget : additionalWidgets) {
|
||||
additionalWidgetsPanel.add(additionalWidget);
|
||||
additionalWidget.addStyleName(AclDialogResources.INSTANCE.css().additionalWidget());
|
||||
}
|
||||
tenantListWrapper.add(additionalWidgetsPanel);
|
||||
tenantListWrapper.add(tenantPanel);
|
||||
tenantListWrapper.add(tenantPager);
|
||||
|
||||
final CaptionPanel tenantListCaption = new CaptionPanel(title);
|
||||
tenantListCaption.add(tenantListWrapper);
|
||||
return tenantListCaption;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the ACL edited in the EditAclDialog. Splits combinedActions retrieved by
|
||||
* {@link AccessControlList#getActionsByUserGroup()} into {@link #allowedActionsEditor} and
|
||||
* {@link #deniedActionsEditor}.
|
||||
*/
|
||||
public void updateAcl(AccessControlListDTO acl) {
|
||||
final Map<UserGroupDTO, Set<String>> combinedActions = (acl != null)
|
||||
? acl.getActionsByUserGroup() != null ? new HashMap<>(acl.getActionsByUserGroup()) : new HashMap<>()
|
||||
: new HashMap<>();
|
||||
|
||||
for (Map.Entry<UserGroupDTO, Set<String>> combinedAction : combinedActions.entrySet()) {
|
||||
final Set<String> allowedActions = new HashSet<>();
|
||||
final Set<String> deniedActions = new HashSet<>();
|
||||
for (String action : combinedAction.getValue()) {
|
||||
if (action.startsWith("!")) {
|
||||
deniedActions.add(action);
|
||||
} else {
|
||||
allowedActions.add(action);
|
||||
}
|
||||
}
|
||||
userGroupsWithAllowedActions.put(combinedAction.getKey(), allowedActions);
|
||||
userGroupsWithDeniedActions.put(combinedAction.getKey(), deniedActions);
|
||||
}
|
||||
|
||||
refreshUi();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the UI by refreshing the {@link #userGroupList} and selects an element to trigger the update of
|
||||
* {@link #allowedActionsEditor} and {@link #deniedActionsEditor}.
|
||||
*/
|
||||
private void refreshUi() {
|
||||
final Set<UserGroupDTO> combinedKeySet = new HashSet<>();
|
||||
combinedKeySet.addAll(userGroupsWithAllowedActions.keySet());
|
||||
combinedKeySet.addAll(userGroupsWithDeniedActions.keySet());
|
||||
userGroupList.setRowCount(combinedKeySet.size(), true);
|
||||
userGroupList.setRowData(0, new ArrayList<>(combinedKeySet));
|
||||
|
||||
// select an element
|
||||
if (combinedKeySet.size() > 0) {
|
||||
userGroupSingleSelectionModel.setSelected(combinedKeySet.iterator().next(), true);
|
||||
}
|
||||
}
|
||||
|
||||
/** Called when the user clicks on the 'Add' button */
|
||||
private void onUserGroupAdd(ClickEvent e) {
|
||||
final String userGroupName = suggestUserGroupUi.getValue();
|
||||
|
||||
// get UserGroup object corresponding to user group name
|
||||
userManagementService.getUserGroupByName(userGroupName, new AsyncCallback<UserGroupDTO>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
Notification.notify(stringMessages.errorMessageUserGroupNameNotFound(userGroupName),
|
||||
NotificationType.ERROR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(UserGroupDTO result) {
|
||||
if (result == null) {
|
||||
Notification.notify(stringMessages.errorMessageUserGroupNameNotFound(userGroupName),
|
||||
NotificationType.ERROR);
|
||||
} else {
|
||||
Notification.notify(stringMessages.successMessageAddedUserGroup(userGroupName),
|
||||
NotificationType.SUCCESS);
|
||||
userGroupsWithAllowedActions.put(result, new HashSet<>());
|
||||
userGroupsWithDeniedActions.put(result, new HashSet<>());
|
||||
refreshUi();
|
||||
suggestUserGroupUi.setText("");
|
||||
userGroupSingleSelectionModel.setSelected(result, true);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** Called when the user clicks on the 'Remove' button */
|
||||
private void onUserGroupRemove(ClickEvent e) {
|
||||
UserGroupDTO selectedObject = userGroupSingleSelectionModel.getSelectedObject();
|
||||
if (selectedObject != null) {
|
||||
userGroupsWithAllowedActions.remove(selectedObject);
|
||||
userGroupsWithDeniedActions.remove(selectedObject);
|
||||
Notification.notify(stringMessages.successMessageRemovedUserGroup(selectedObject.getName()),
|
||||
NotificationType.SUCCESS);
|
||||
refreshUi();
|
||||
}
|
||||
}
|
||||
|
||||
/** Merges {@link #userGroupsWithAllowedActions} and {@link #userGroupsWithDeniedActions}. */
|
||||
public Map<UserGroupDTO, Set<String>> getUserGroupsWithCombinedActions() {
|
||||
final Map<UserGroupDTO, Set<String>> combinedActions = new HashMap<>(userGroupsWithAllowedActions);
|
||||
for (Map.Entry<UserGroupDTO, Set<String>> actionEntry : userGroupsWithDeniedActions.entrySet()) {
|
||||
if (combinedActions.containsKey(actionEntry.getKey())) {
|
||||
final Set<String> set = combinedActions.get(actionEntry.getKey());
|
||||
set.addAll(actionEntry.getValue());
|
||||
} else {
|
||||
combinedActions.put(actionEntry.getKey(), actionEntry.getValue());
|
||||
}
|
||||
}
|
||||
return combinedActions;
|
||||
}
|
||||
|
||||
/** Converts an {@link Iterable} into a {@link Set}. */
|
||||
private <T> Set<T> toSet(Iterable<T> iter) {
|
||||
Set<T> resultSet;
|
||||
if (iter instanceof Set) {
|
||||
resultSet = (Set<T>) iter;
|
||||
} else {
|
||||
resultSet = new HashSet<>();
|
||||
for (T t : iter) {
|
||||
resultSet.add(t);
|
||||
}
|
||||
}
|
||||
return resultSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an {@link Iterable} into a {@link Set} and adds a '!' in front of each String to mark the action as
|
||||
* denied.
|
||||
*/
|
||||
private Set<String> toDeniedActionSet(Iterable<String> iter) {
|
||||
final Set<String> set = new HashSet<>();
|
||||
for (String s : iter) {
|
||||
set.add(s.startsWith("!") ? s : "!" + s);
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
private Set<String> removeExclamationMarkFromStrings(Iterable<String> iter) {
|
||||
final Set<String> set = new HashSet<>();
|
||||
for (String s : iter) {
|
||||
set.add(s.startsWith("!") ? s.substring(1) : s);
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
|
||||
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder" xmlns:g="urn:import:com.google.gwt.user.client.ui">
|
||||
<g:HTMLPanel>
|
||||
<g:FlowPanel ui:field="userGroupCellListPanelUi" />
|
||||
<g:FlowPanel ui:field="permissionsCellListPanelUi" />
|
||||
</g:HTMLPanel>
|
||||
</ui:UiBinder>
|
||||
+162
@@ -0,0 +1,162 @@
|
||||
package com.sap.sse.security.ui.client.component.editacl;
|
||||
|
||||
import static com.sap.sse.gwt.client.Notification.NotificationType.ERROR;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.sap.sailing.domain.common.security.SecuredDomainType;
|
||||
import com.sap.sse.common.Named;
|
||||
import com.sap.sse.gwt.client.Notification;
|
||||
import com.sap.sse.gwt.client.Notification.NotificationType;
|
||||
import com.sap.sse.gwt.client.dialog.DataEntryDialog;
|
||||
import com.sap.sse.security.shared.HasPermissions;
|
||||
import com.sap.sse.security.shared.HasPermissions.Action;
|
||||
import com.sap.sse.security.shared.QualifiedObjectIdentifier;
|
||||
import com.sap.sse.security.shared.dto.AccessControlListDTO;
|
||||
import com.sap.sse.security.shared.dto.SecuredDTO;
|
||||
import com.sap.sse.security.ui.client.UserManagementServiceAsync;
|
||||
import com.sap.sse.security.ui.client.component.editacl.EditACLDialog.AclDialogResult;
|
||||
import com.sap.sse.security.ui.client.i18n.StringMessages;
|
||||
|
||||
public class EditACLDialog extends DataEntryDialog<AclDialogResult> {
|
||||
|
||||
private final AclEditPanel aclEditPanel;
|
||||
|
||||
static class AclDialogResult {
|
||||
private final AccessControlListDTO acl;
|
||||
|
||||
private AclDialogResult(final AccessControlListDTO acl) {
|
||||
this.acl = acl;
|
||||
}
|
||||
|
||||
public AccessControlListDTO getAcl() {
|
||||
return acl;
|
||||
}
|
||||
}
|
||||
|
||||
private static class Validator implements DataEntryDialog.Validator<AclDialogResult> {
|
||||
@Override
|
||||
public String getErrorMessage(AclDialogResult valueToValidate) {
|
||||
// nothing to validate since user input is already validated in aclPanel
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private EditACLDialog(UserManagementServiceAsync userManagementService,
|
||||
QualifiedObjectIdentifier qualifiedObjectIdentifier, Action[] availableActions,
|
||||
StringMessages stringMessages, DialogCallback<AclDialogResult> callback) {
|
||||
super(stringMessages.acl(), stringMessages.editACLForObject(qualifiedObjectIdentifier.toString()),
|
||||
stringMessages.ok(), stringMessages.cancel(), new Validator(), callback);
|
||||
aclEditPanel = new AclEditPanel(userManagementService, availableActions, stringMessages);
|
||||
userManagementService.getAccessControlListWithoutPruning(qualifiedObjectIdentifier,
|
||||
new AsyncCallback<AccessControlListDTO>() {
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
Notification.notify(caught.getMessage(), NotificationType.ERROR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(AccessControlListDTO result) {
|
||||
aclEditPanel.updateAcl(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Widget getAdditionalWidget() {
|
||||
return aclEditPanel;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AclDialogResult getResult() {
|
||||
return new AclDialogResult(new AccessControlListDTO(aclEditPanel.getUserGroupsWithCombinedActions()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link DialogConfig dialog configuration} instance which can be (re-)used to
|
||||
* {@link DialogConfig#openDialog(Named) open} a {@link EditACLDialog dialog}.
|
||||
*
|
||||
* @param userManagementService
|
||||
* {@link UserManagementServiceAsync} to use to set the secured object's ownership
|
||||
* @param type
|
||||
* {@link SecuredDomainType} specifying the type of required permissions to modify the secured object
|
||||
* @param typeRelativeIdFactory
|
||||
* {@link Function factory} to get a {@link String type relative identifier} for the secured object
|
||||
* @param updateCallback
|
||||
* {@link Consumer callback} to execute when the dialog is confirmed and ownership update succeeded
|
||||
* @param errorCallback
|
||||
* {@link Consumer callback} to execute when the dialog is confirmed and ownership update fails
|
||||
*/
|
||||
public static <T extends Named & SecuredDTO> DialogConfig<T> create(
|
||||
final UserManagementServiceAsync userManagementService, final HasPermissions type,
|
||||
final Function<T, String> typeRelativeIdFactory, final Consumer<T> updateCallback,
|
||||
final StringMessages stringMessages) {
|
||||
return new DialogConfig<>(userManagementService, type, typeRelativeIdFactory, updateCallback, stringMessages);
|
||||
}
|
||||
|
||||
public static class DialogConfig<T extends Named & SecuredDTO> {
|
||||
private final UserManagementServiceAsync userManagementService;
|
||||
private final Consumer<T> updateCallback;
|
||||
private final Function<T, QualifiedObjectIdentifier> identifierFactory;
|
||||
private final Function<T, Action[]> availableActionsFactory;
|
||||
private final StringMessages stringMessages;
|
||||
|
||||
private DialogConfig(final UserManagementServiceAsync userManagementService, final HasPermissions type,
|
||||
final Function<T, String> idFactory, final Consumer<T> updateCallback,
|
||||
final StringMessages stringMessages) {
|
||||
this.userManagementService = userManagementService;
|
||||
this.identifierFactory = idFactory.andThen(type::getQualifiedObjectIdentifier);
|
||||
this.availableActionsFactory = idFactory.andThen(t -> type.getAvailableActions());
|
||||
this.updateCallback = updateCallback;
|
||||
this.stringMessages = stringMessages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a {@link EditACLDialog dialog} to edit ownerships for the provided secured object instance.
|
||||
*
|
||||
* @param securedObject
|
||||
* {@link Named} {@link SecuredObject} instance to edit ownerships for
|
||||
*/
|
||||
public void openDialog(final T securedObject) {
|
||||
new EditACLDialog(userManagementService, identifierFactory.apply(securedObject),
|
||||
availableActionsFactory.apply(securedObject), StringMessages.INSTANCE,
|
||||
new EditAclDialogCallback(securedObject)).show();
|
||||
}
|
||||
|
||||
private class EditAclDialogCallback implements DialogCallback<AclDialogResult> {
|
||||
|
||||
private final T securedObject;
|
||||
|
||||
private EditAclDialogCallback(T securedObject) {
|
||||
this.securedObject = securedObject;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ok(AclDialogResult aclResult) {
|
||||
final QualifiedObjectIdentifier objectIdentifier = identifierFactory.apply(securedObject);
|
||||
userManagementService.overrideAccessControlList(objectIdentifier, aclResult.getAcl(),
|
||||
new AsyncCallback<AccessControlListDTO>() {
|
||||
@Override
|
||||
public void onSuccess(AccessControlListDTO result) {
|
||||
securedObject.setAccessControlList(result);
|
||||
updateCallback.accept(securedObject);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void onFailure(Throwable caught) {
|
||||
Notification.notify(stringMessages.errorUpdatingAcl(securedObject.getName()), ERROR);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void cancel() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+12
@@ -2,6 +2,7 @@ package com.sap.sse.security.ui.client.i18n;
|
||||
|
||||
import com.google.gwt.core.client.GWT;
|
||||
import com.google.gwt.i18n.client.LocalizableResource.DefaultLocale;
|
||||
import com.google.gwt.safehtml.shared.SafeHtml;
|
||||
|
||||
@DefaultLocale("en")
|
||||
public interface StringMessages extends com.sap.sse.gwt.client.StringMessages {
|
||||
@@ -152,4 +153,15 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages {
|
||||
String pleaseWaitUntilUserGroupNameIsResolved();
|
||||
String userNotFound(String username);
|
||||
String usergroupNotFound(String userGroupName);
|
||||
String actionChangeACL();
|
||||
String editACLForObject(String objectName);
|
||||
String acl();
|
||||
String errorUpdatingAcl(String name);
|
||||
String allowedActionName();
|
||||
String deniedActionName();
|
||||
String errorMessageUserGroupNameNotFound(String name);
|
||||
String successMessageAddedUserGroup(String name);
|
||||
String successMessageRemovedUserGroup(String name);
|
||||
SafeHtml allowedActions();
|
||||
SafeHtml deniedActions();
|
||||
}
|
||||
+12
-1
@@ -159,4 +159,15 @@ errorObtainingUserGroup=Error obtaining user group: {0}
|
||||
pleaseWaitUntilUsernameIsResolved=Please wait until user name is resolved
|
||||
pleaseWaitUntilUserGroupNameIsResolved=Please wait until user group name is resolved
|
||||
userNotFound=User {0} not found
|
||||
usergroupNotFound=User group {0} not found
|
||||
usergroupNotFound=User group {0} not found
|
||||
actionChangeACL=Change ACL
|
||||
editACLForObject=Edit ACL for ''{0}''
|
||||
acl=ACL
|
||||
errorUpdatingAcl=Error updating ACL: {0}.
|
||||
allowedActionName=Allowed action name
|
||||
deniedActionName=Denied action name
|
||||
errorMessageUserGroupNameNotFound=Did not find user group by name {0}.
|
||||
successMessageAddedUserGroup=Added user group {0}.
|
||||
successMessageRemovedUserGroup=Removed user group {0}.
|
||||
allowedActions=Allowed Actions
|
||||
deniedActions=Denied Actions
|
||||
+12
-1
@@ -158,4 +158,15 @@ errorObtainingUserGroup=Fehler beim Nachschlagen der Benutzergruppe: {0}
|
||||
pleaseWaitUntilUsernameIsResolved=Bitte warten, bis der Benutzername ausgelöst wurde
|
||||
pleaseWaitUntilUserGroupNameIsResolved=Bitte warten, bis der Benutzergruppen-Name aufgelöst wurde
|
||||
userNotFound=Benutzer {0} nicht gefunden
|
||||
usergroupNotFound=Benutzergruppe {0} nicht gefunden
|
||||
usergroupNotFound=Benutzergruppe {0} nicht gefunden
|
||||
actionChangeACL=ACL ändern
|
||||
editACLForObject=ACL für ''{0}'' ändern
|
||||
acl=ACL
|
||||
errorUpdatingAcl=Fehler beim Aktualisieren der ACL: {0}.
|
||||
allowedActionName=Erlaubter Aktionsname
|
||||
deniedActionName=Verweigerter Aktionsname
|
||||
errorMessageUserGroupNameNotFound=Benutzergruppe nicht gefunden: {0}.
|
||||
successMessageAddedUserGroup=Benutzergruppe hinzugefügt: {0}.
|
||||
successMessageRemovedUserGroup=Benutzergruppe entfernt: {0}.
|
||||
allowedActions=Erlabute Aktionen
|
||||
deniedActions=Verweigerte Aktionen
|
||||
+12
-4
@@ -40,6 +40,7 @@ import com.sap.sse.gwt.client.celltable.TableWrapper;
|
||||
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.HasPermissions.DefaultActions;
|
||||
import com.sap.sse.security.shared.WildcardPermission;
|
||||
import com.sap.sse.security.shared.dto.RoleDTO;
|
||||
import com.sap.sse.security.shared.dto.UserDTO;
|
||||
@@ -50,8 +51,8 @@ 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;
|
||||
import com.sap.sse.security.ui.client.component.editacl.EditACLDialog;
|
||||
import com.sap.sse.security.ui.client.i18n.StringMessages;
|
||||
import com.sap.sse.security.ui.shared.SuccessInfo;
|
||||
|
||||
@@ -186,9 +187,16 @@ extends TableWrapper<UserDTO, S, StringMessages, TR> {
|
||||
});
|
||||
}
|
||||
});
|
||||
final DialogConfig<UserDTO> config = EditOwnershipDialog.create(userService.getUserManagementService(), type,
|
||||
final EditOwnershipDialog.DialogConfig<UserDTO> configOwnership = EditOwnershipDialog.create(
|
||||
userService.getUserManagementService(), type,
|
||||
idFactory, user -> refreshUserList((Callback<Iterable<UserDTO>, Throwable>) null), stringMessages);
|
||||
userActionColumn.addAction(ACTION_CHANGE_OWNERSHIP, CHANGE_OWNERSHIP, config::openDialog);
|
||||
|
||||
final EditACLDialog.DialogConfig<UserDTO> configACL = EditACLDialog.create(
|
||||
userService.getUserManagementService(), type,
|
||||
idFactory, user -> user.getAccessControlList(), stringMessages);
|
||||
userActionColumn.addAction(ACTION_CHANGE_OWNERSHIP, CHANGE_OWNERSHIP, configOwnership::openDialog);
|
||||
userActionColumn.addAction(DefaultActionsImagesBarCell.ACTION_CHANGE_ACL, DefaultActions.CHANGE_ACL,
|
||||
u -> configACL.openDialog(u));
|
||||
|
||||
filterField = new LabeledAbstractFilterablePanel<UserDTO>(new Label(stringMessages.filterUsers()),
|
||||
new ArrayList<UserDTO>(), dataProvider) {
|
||||
@@ -275,7 +283,7 @@ extends TableWrapper<UserDTO, S, StringMessages, TR> {
|
||||
users.add(user);
|
||||
getUserManagementService().updateUserProperties(user.getName(), user.getFullName(),
|
||||
user.getCompany(), user.getLocale(),
|
||||
user.getDefaultTenant() != null ? user.getDefaultTenant().getName() : null,
|
||||
user.getDefaultTenant() != null ? user.getDefaultTenant().getId().toString() : null,
|
||||
new AsyncCallback<UserDTO>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
|
||||
+41
@@ -800,4 +800,45 @@ public class UserManagementServiceImpl extends RemoteServiceServlet implements U
|
||||
public String getOrCreateAccessToken(String username) {
|
||||
return getSecurityService().getOrCreateAccessToken(username);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessControlListDTO overrideAccessControlList(QualifiedObjectIdentifier idOfAccessControlledObject,
|
||||
AccessControlListDTO acl) throws UnauthorizedException {
|
||||
if (SecurityUtils.getSubject()
|
||||
.isPermitted(idOfAccessControlledObject.getStringPermission(DefaultActions.CHANGE_ACL))) {
|
||||
|
||||
Map<UserGroup,Set<String>> aclActionsByGroup = new HashMap<>();
|
||||
for (Map.Entry<UserGroupDTO, Set<String>> entry : acl.getActionsByUserGroup().entrySet()) {
|
||||
final UserGroupDTO groupDTO = entry.getKey();
|
||||
final UserGroup userGroup;
|
||||
if (groupDTO == null) {
|
||||
userGroup = null;
|
||||
} else {
|
||||
userGroup = getSecurityService().getUserGroup(groupDTO.getId());
|
||||
}
|
||||
aclActionsByGroup.put(userGroup, entry.getValue());
|
||||
}
|
||||
|
||||
return securityDTOFactory.createAccessControlListDTO(getSecurityService()
|
||||
.overrideAccessControlList(idOfAccessControlledObject, aclActionsByGroup));
|
||||
} else {
|
||||
throw new UnauthorizedException("Not permitted to update the ACL for a user");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessControlListDTO getAccessControlListWithoutPruning(QualifiedObjectIdentifier idOfAccessControlledObject) throws UnauthorizedException {
|
||||
if (SecurityUtils.getSubject()
|
||||
.isPermitted(idOfAccessControlledObject.getStringPermission(DefaultActions.CHANGE_ACL))) {
|
||||
AccessControlListAnnotation accessControlList = getSecurityService().getAccessControlList(idOfAccessControlledObject);
|
||||
if (accessControlList == null) {
|
||||
return null;
|
||||
}
|
||||
return securityDTOFactory.createAccessControlListDTO(
|
||||
accessControlList.getAnnotation());
|
||||
} else {
|
||||
throw new UnauthorizedException("Not permitted to get the unpruned ACL for a user");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -76,6 +76,9 @@ public interface SecurityService extends ReplicableWithObjectInputStream<Replica
|
||||
|
||||
AccessControlList updateAccessControlList(QualifiedObjectIdentifier idOfAccessControlledObject, Map<UserGroup, Set<String>> permissionMap);
|
||||
|
||||
AccessControlList overrideAccessControlList(QualifiedObjectIdentifier idOfAccessControlledObject,
|
||||
Map<UserGroup, Set<String>> permissionMap);
|
||||
|
||||
/**
|
||||
* @param name The name of the user group to add
|
||||
*/
|
||||
|
||||
@@ -423,6 +423,13 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessControlList overrideAccessControlList(QualifiedObjectIdentifier idOfAccessControlledObject,
|
||||
Map<UserGroup, Set<String>> permissionMap) {
|
||||
accessControlStore.removeAccessControlList(idOfAccessControlledObject);
|
||||
return updateAccessControlList(idOfAccessControlledObject, permissionMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessControlList updateAccessControlList(QualifiedObjectIdentifier idOfAccessControlledObject, Map<UserGroup, Set<String>> permissionMap) {
|
||||
if (getAccessControlList(idOfAccessControlledObject) == null) {
|
||||
|
||||
Reference in new issue
Block a user