mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-09-23 22:19:13 +00:00
Merge branch 'permission-vertical' into bug4709_pv
This commit is contained in:
+8
-7
@@ -47,15 +47,12 @@ import com.sap.sse.common.Color;
|
||||
import com.sap.sse.common.Duration;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
import com.sap.sse.security.AccessControlStore;
|
||||
import com.sap.sse.security.SecurityService;
|
||||
import com.sap.sse.security.UserImpl;
|
||||
import com.sap.sse.security.UserStore;
|
||||
import com.sap.sse.security.impl.SecurityServiceImpl;
|
||||
import com.sap.sse.security.shared.User;
|
||||
import com.sap.sse.security.shared.UserGroup;
|
||||
import com.sap.sse.security.shared.UserGroupManagementException;
|
||||
import com.sap.sse.security.shared.UserManagementException;
|
||||
import com.sap.sse.security.userstore.mongodb.AccessControlStoreImpl;
|
||||
import com.sap.sse.security.userstore.mongodb.UserStoreImpl;
|
||||
|
||||
public class OfflineSerializationTest extends AbstractSerializationTest {
|
||||
@@ -112,9 +109,13 @@ public class OfflineSerializationTest extends AbstractSerializationTest {
|
||||
DomainFactory receiverDomainFactory = new DomainFactoryImpl((srlid)->null);
|
||||
UserStore userStore = new UserStoreImpl("defaultTenant");
|
||||
userStore.clear();
|
||||
AccessControlStore aclStore = new AccessControlStoreImpl(userStore);
|
||||
SecurityService securityService = new SecurityServiceImpl(userStore, aclStore);
|
||||
assertNotNull(securityService);
|
||||
UserGroup defaultTenant = userStore.createUserGroup(UUID.randomUUID(), "admin-tenant");
|
||||
UserImpl user = userStore.createUser("admin", "", defaultTenant);
|
||||
defaultTenant.add(user);
|
||||
userStore.updateUserGroup(defaultTenant);
|
||||
user.getDefaultTenantMap().put("testserver", defaultTenant);
|
||||
userStore.updateUser(user);
|
||||
|
||||
{
|
||||
User admin = userStore.getUserByName("admin");
|
||||
UserGroup adminTenant = admin.getDefaultTenant("testserver");
|
||||
|
||||
+6
-9
@@ -2,21 +2,18 @@
|
||||
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
|
||||
xmlns:g="urn:import:com.google.gwt.user.client.ui" xmlns:s="urn:import:com.sap.sailing.gwt.home.client.shared"
|
||||
xmlns:sse="urn:import:com.sap.sse.gwt.client.controls">
|
||||
<ui:with field="i18n"
|
||||
type="com.sap.sailing.gwt.ui.client.StringMessages" />
|
||||
<ui:with field="res"
|
||||
type="com.sap.sailing.gwt.common.client.SharedResources" />
|
||||
<ui:with field="local_res"
|
||||
type="com.sap.sailing.gwt.home.mobile.partials.stage.StageResources" />
|
||||
<ui:with field="i18n" type="com.sap.sailing.gwt.ui.client.StringMessages" />
|
||||
<ui:with field="res" type="com.sap.sailing.gwt.common.client.SharedResources" />
|
||||
<ui:with field="local_res" type="com.sap.sailing.gwt.home.mobile.partials.stage.StageResources" />
|
||||
<g:HTMLPanel>
|
||||
<!-- The main stage -->
|
||||
<div class="{local_res.css.stage}">
|
||||
<div class="{local_res.css.stage_teasers} {local_res.css.swipercontainer}" ui:field="stageContainer">
|
||||
<sse:carousel.WidgetCarousel ui:field="widgetCarousel" />
|
||||
</div>
|
||||
<div class="{local_res.css.stage_progress}">
|
||||
<div class="{local_res.css.stage_progress_bar}"></div>
|
||||
</div>
|
||||
<!-- <div class="{local_res.css.stage_progress}"> -->
|
||||
<!-- <div class="{local_res.css.stage_progress_bar}"></div> -->
|
||||
<!-- </div> -->
|
||||
</div>
|
||||
</g:HTMLPanel>
|
||||
</ui:UiBinder>
|
||||
|
||||
+2
-5
@@ -18,7 +18,6 @@ import com.sap.sailing.server.statistics.TrackedRaceStatisticsCache;
|
||||
import com.sap.sse.gwt.dispatch.shared.exceptions.DispatchException;
|
||||
import com.sap.sse.gwt.dispatch.shared.exceptions.ServerDispatchException;
|
||||
import com.sap.sse.security.SecurityService;
|
||||
import com.sap.sse.security.UserStore;
|
||||
import com.sap.sse.security.shared.SecurityUser;
|
||||
|
||||
@GwtIncompatible
|
||||
@@ -30,20 +29,18 @@ public class SailingDispatchContextImpl implements SailingDispatchContext {
|
||||
private String clientLocaleName;
|
||||
private final HttpServletRequest request;
|
||||
private final SecurityService securityService;
|
||||
private final UserStore userStore;
|
||||
private final TrackedRaceStatisticsCache trackedRaceStatisticsCache;
|
||||
private final WindFinderTrackerFactory windFinderTrackerFactory;
|
||||
|
||||
public SailingDispatchContextImpl(Date currentClientTime, RacingEventService racingEventService,
|
||||
WindFinderTrackerFactory windFinderTrackerFactory, EventNewsService eventNewsService,
|
||||
SecurityService securityService, UserStore userStore, TrackedRaceStatisticsCache trackedRaceStatisticsCache,
|
||||
SecurityService securityService, TrackedRaceStatisticsCache trackedRaceStatisticsCache,
|
||||
String clientLocaleName, HttpServletRequest request) {
|
||||
this.currentClientTime = currentClientTime;
|
||||
this.racingEventService = racingEventService;
|
||||
this.windFinderTrackerFactory = windFinderTrackerFactory;
|
||||
this.eventNewsService = eventNewsService;
|
||||
this.securityService = securityService;
|
||||
this.userStore = userStore;
|
||||
this.trackedRaceStatisticsCache = trackedRaceStatisticsCache;
|
||||
this.clientLocaleName = clientLocaleName;
|
||||
this.request = request;
|
||||
@@ -101,7 +98,7 @@ public class SailingDispatchContextImpl implements SailingDispatchContext {
|
||||
public <T> T getPreferenceForCurrentUser(String preferenceKey) {
|
||||
SecurityUser currentUser = securityService.getCurrentUser();
|
||||
if (currentUser != null) {
|
||||
return userStore.getPreferenceObject(currentUser.getName(), preferenceKey);
|
||||
return securityService.getPreferenceObject(currentUser.getName(), preferenceKey);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
+1
-4
@@ -24,7 +24,6 @@ import com.sap.sse.gwt.dispatch.servlets.AbstractDispatchServlet;
|
||||
import com.sap.sse.gwt.dispatch.shared.commands.Action;
|
||||
import com.sap.sse.gwt.dispatch.shared.commands.Result;
|
||||
import com.sap.sse.security.SecurityService;
|
||||
import com.sap.sse.security.UserStore;
|
||||
import com.sap.sse.util.ServiceTrackerFactory;
|
||||
|
||||
public class SailingDispatchServlet extends AbstractDispatchServlet<SailingDispatchContext> {
|
||||
@@ -34,7 +33,6 @@ public class SailingDispatchServlet extends AbstractDispatchServlet<SailingDispa
|
||||
private final ServiceTracker<WindFinderTrackerFactory, WindFinderTrackerFactory> windFinderTrackerFactory;
|
||||
private final ServiceTracker<EventNewsService, EventNewsService> eventNewsServiceTracker;
|
||||
private final ServiceTracker<SecurityService, SecurityService> securityServiceTracker;
|
||||
private final ServiceTracker<UserStore, UserStore> userStoreTracker;
|
||||
private final ServiceTracker<TrackedRaceStatisticsCache, TrackedRaceStatisticsCache> trackedRaceStatisticsCacheTracker;
|
||||
|
||||
public SailingDispatchServlet() {
|
||||
@@ -43,7 +41,6 @@ public class SailingDispatchServlet extends AbstractDispatchServlet<SailingDispa
|
||||
windFinderTrackerFactory = ServiceTrackerFactory.createAndOpen(context, WindFinderTrackerFactory.class);
|
||||
eventNewsServiceTracker = ServiceTrackerFactory.createAndOpen(context, EventNewsService.class);
|
||||
securityServiceTracker = ServiceTrackerFactory.createAndOpen(context, SecurityService.class);
|
||||
userStoreTracker = ServiceTrackerFactory.createAndOpen(context, UserStore.class);
|
||||
trackedRaceStatisticsCacheTracker = ServiceTrackerFactory.createAndOpen(context, TrackedRaceStatisticsCache.class);
|
||||
}
|
||||
|
||||
@@ -53,7 +50,7 @@ public class SailingDispatchServlet extends AbstractDispatchServlet<SailingDispa
|
||||
return new SailingDispatchContextImpl(request.getCurrentClientTime(), racingEventServiceTracker.getService(),
|
||||
windFinderTrackerFactory.getService(),
|
||||
eventNewsServiceTracker.getService(), securityServiceTracker.getService(),
|
||||
userStoreTracker.getService(), trackedRaceStatisticsCacheTracker.getService(),
|
||||
trackedRaceStatisticsCacheTracker.getService(),
|
||||
request.getClientLocaleName(), getThreadLocalRequest());
|
||||
}
|
||||
|
||||
|
||||
+12
-18
@@ -2,11 +2,11 @@
|
||||
left: 50% !important;
|
||||
top: 50% !important;
|
||||
position: absolute !important;
|
||||
background-color: #f2f2f2;
|
||||
background-color: #ffffff;
|
||||
padding: 1em;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 3px;
|
||||
width: 25%;
|
||||
width: 30em;
|
||||
-webkit-transform: translate(-50%, -50%);
|
||||
-moz-transform: translate(-50%, -50%);
|
||||
-ms-transform: translate(-50%, -50%);
|
||||
@@ -15,8 +15,7 @@
|
||||
}
|
||||
|
||||
.buttonAdjustments {
|
||||
margin-left: .5em;
|
||||
margin-top: .5em;
|
||||
margin: .5em .25em 0;
|
||||
min-width: 11em;
|
||||
}
|
||||
|
||||
@@ -39,6 +38,7 @@
|
||||
|
||||
.buttonPanel {
|
||||
text-align: center;
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
.backgroundPanel {
|
||||
@@ -48,6 +48,7 @@
|
||||
top: 0;
|
||||
left: 0;
|
||||
background-color: rgba(0, 0, 0, .33333333);
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.title {
|
||||
@@ -57,20 +58,13 @@
|
||||
margin-bottom: .75em;
|
||||
}
|
||||
|
||||
@media ( max-width: 400px) {
|
||||
@media (max-width: 425px) {
|
||||
.dialog {
|
||||
width: 90%;
|
||||
width: calc(100% - 1em);
|
||||
}
|
||||
|
||||
.buttonAdjustments {
|
||||
min-width: 100%;
|
||||
margin: .5em 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media ( min-width: 400px) {
|
||||
.dialog {
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
@media ( min-width: 600px) {
|
||||
.dialog {
|
||||
width: 40%;
|
||||
}
|
||||
}
|
||||
+5
-10
@@ -36,29 +36,24 @@ public class TwoOptionsDialogPanel extends Composite {
|
||||
DivElement titleField;
|
||||
|
||||
private final DialogCallback<Void> callback;
|
||||
|
||||
private final PopupPanel parent;
|
||||
|
||||
public TwoOptionsDialogPanel() {
|
||||
this("", "", null, null);
|
||||
}
|
||||
|
||||
public TwoOptionsDialogPanel(String message, String title, DialogCallback<Void> callback, PopupPanel parent) {
|
||||
TwoOptionsDialogPanel(String message, String title, DialogCallback<Void> callback, PopupPanel parent) {
|
||||
initWidget(uiBinder.createAndBindUi(this));
|
||||
this.callback = callback;
|
||||
this.parent = parent;
|
||||
messageField.setInnerText(message);
|
||||
titleField.setInnerText(title);
|
||||
this.messageField.setInnerText(message);
|
||||
this.titleField.setInnerText(title);
|
||||
}
|
||||
|
||||
/** Sets the labels of the {@link #firstButton} and {@link #secondButton}. */
|
||||
public void setButtonLabels(String firstButtonText, String secondButtonText) {
|
||||
void setButtonLabels(String firstButtonText, String secondButtonText) {
|
||||
firstButton.setText(firstButtonText);
|
||||
secondButton.setText(secondButtonText);
|
||||
}
|
||||
|
||||
/** Changes the color of the {@link #firstButton} to red. */
|
||||
public void setFirstButtonDestructive() {
|
||||
void setFirstButtonDestructive() {
|
||||
firstButton.addStyleName(DialogResources.INSTANCE.css().destructiveButton());
|
||||
}
|
||||
|
||||
|
||||
+7
-2
@@ -24,6 +24,8 @@ public final class WhatsNewDialogFactory {
|
||||
private static final long THRESHOLD_WHATS_NEW = 10;
|
||||
private static final Logger LOG = Logger.getLogger(WhatsNewDialogFactory.class.getName());
|
||||
|
||||
private static boolean isUserNotified = false;
|
||||
|
||||
private WhatsNewDialogFactory() {
|
||||
}
|
||||
|
||||
@@ -38,10 +40,10 @@ public final class WhatsNewDialogFactory {
|
||||
|
||||
/** Shows a What's New Dialog. */
|
||||
private static void showWhatsNewDialog(PlaceController placeController, DialogCallback<Void> dialogCallback) {
|
||||
|
||||
PopupPanel dialog = DialogFactory.createDialog(StringMessages.INSTANCE.whatsNewDialogMessage(),
|
||||
final PopupPanel dialog = DialogFactory.createDialog(StringMessages.INSTANCE.whatsNewDialogMessage(),
|
||||
StringMessages.INSTANCE.whatsNewDialogTitle(), false, StringMessages.INSTANCE.showChangelog(),
|
||||
StringMessages.INSTANCE.cancel(), dialogCallback);
|
||||
isUserNotified = true;
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
@@ -51,6 +53,9 @@ public final class WhatsNewDialogFactory {
|
||||
*/
|
||||
private static void showWhatsNewDialogIfNecessaryAndUpdatePreference(UserService userService,
|
||||
PlaceController placeController) {
|
||||
if (isUserNotified) {
|
||||
return;
|
||||
}
|
||||
final long charactersInWhatsChangedDocument = WhatsNewResources.INSTANCE.getSailingAnalyticsNotesHtml()
|
||||
.getText().length();
|
||||
userService.getPreference(WhatsNewSettings.PREF_NAME, new AsyncCallback<String>() {
|
||||
|
||||
+54
-4
@@ -13,6 +13,8 @@ import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sailing.gwt.ui.shared.ServerConfigurationDTO;
|
||||
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.gwt.client.ServerInfoDTO;
|
||||
|
||||
public class LocalServerManagementPanel extends SimplePanel {
|
||||
@@ -25,6 +27,8 @@ public class LocalServerManagementPanel extends SimplePanel {
|
||||
private CheckBox isStandaloneServerCheckbox;
|
||||
private Label serverNameLabel;
|
||||
private Label buildVersionLabel;
|
||||
private CheckBox isPublicServerCheckbox;
|
||||
private CheckBox isSelfServiceServerCheckbox;
|
||||
|
||||
public LocalServerManagementPanel(SailingServiceAsync sailingService, ErrorReporter errorReporter,
|
||||
StringMessages stringMessages) {
|
||||
@@ -74,26 +78,59 @@ public class LocalServerManagementPanel extends SimplePanel {
|
||||
serverConfigurationChanged();
|
||||
}
|
||||
});
|
||||
|
||||
isPublicServerCheckbox = new CheckBox();
|
||||
isPublicServerCheckbox.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
|
||||
@Override
|
||||
public void onValueChange(ValueChangeEvent<Boolean> event) {
|
||||
serverConfigurationChanged();
|
||||
}
|
||||
});
|
||||
isPublicServerCheckbox.setEnabled(false);
|
||||
|
||||
isSelfServiceServerCheckbox = new CheckBox();
|
||||
isSelfServiceServerCheckbox.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
|
||||
@Override
|
||||
public void onValueChange(ValueChangeEvent<Boolean> event) {
|
||||
serverConfigurationChanged();
|
||||
}
|
||||
});
|
||||
isSelfServiceServerCheckbox.setEnabled(false);
|
||||
|
||||
Grid grid = new Grid(2, 2);
|
||||
Grid grid = new Grid(3, 2);
|
||||
grid.setWidget(0, 0, new Label(stringMessages.standaloneServer() + ":"));
|
||||
grid.setWidget(0, 1, isStandaloneServerCheckbox);
|
||||
|
||||
grid.setWidget(1, 0, new Label(stringMessages.publicServer() + ":"));
|
||||
grid.setWidget(1, 1, isPublicServerCheckbox);
|
||||
|
||||
grid.setWidget(2, 0, new Label(stringMessages.selfServiceServer() + ":"));
|
||||
grid.setWidget(2, 1, isSelfServiceServerCheckbox);
|
||||
|
||||
serverConfigurationContentPanel.add(grid);
|
||||
|
||||
refreshServerConfiguration();
|
||||
}
|
||||
|
||||
private void serverConfigurationChanged() {
|
||||
ServerConfigurationDTO serverConfig = new ServerConfigurationDTO(isStandaloneServerCheckbox.getValue());
|
||||
|
||||
Boolean publicServer = isPublicServerCheckbox.isEnabled() ? isPublicServerCheckbox.getValue() : null;
|
||||
// FIXME self service not yet supported
|
||||
Boolean selfServiceServer = isSelfServiceServerCheckbox.isEnabled() ? isSelfServiceServerCheckbox.getValue()
|
||||
: null;
|
||||
ServerConfigurationDTO serverConfig = new ServerConfigurationDTO(isStandaloneServerCheckbox.getValue(),
|
||||
publicServer, selfServiceServer);
|
||||
|
||||
sailingService.updateServerConfiguration(serverConfig, new AsyncCallback<Void>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
Notification.notify(stringMessages.updatedServerSetupError(), NotificationType.ERROR);
|
||||
errorReporter.reportError(caught.getMessage());
|
||||
refreshServerConfiguration();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Void result) {
|
||||
Notification.notify(stringMessages.updatedServerSetup(), NotificationType.SUCCESS);
|
||||
refreshServerConfiguration();
|
||||
}
|
||||
});
|
||||
@@ -133,6 +170,19 @@ public class LocalServerManagementPanel extends SimplePanel {
|
||||
}
|
||||
|
||||
private void updateServerConfiguration(ServerConfigurationDTO result) {
|
||||
isStandaloneServerCheckbox.setValue(result.isStandaloneServer(), true);
|
||||
isStandaloneServerCheckbox.setValue(result.isStandaloneServer(), false);
|
||||
if (result.isPublic() != null) {
|
||||
isPublicServerCheckbox.setEnabled(true);
|
||||
isPublicServerCheckbox.setValue(result.isPublic(), false);
|
||||
} else {
|
||||
isPublicServerCheckbox.setEnabled(false);
|
||||
}
|
||||
if (result.isSelfService() != null) {
|
||||
// isSelfServiceServerCheckbox.setEnabled(true);
|
||||
isSelfServiceServerCheckbox.setValue(result.isSelfService(), false);
|
||||
} else {
|
||||
isSelfServiceServerCheckbox.setEnabled(false);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+4
-1
@@ -2206,6 +2206,9 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages,
|
||||
String noStatisticsFoundForCompetitors();
|
||||
String sailorProfileRemoveMessage();
|
||||
String showInDataMining();
|
||||
|
||||
String confirmDeletion();
|
||||
String selfServiceServer();
|
||||
String publicServer();
|
||||
String updatedServerSetupError();
|
||||
String updatedServerSetup();
|
||||
}
|
||||
|
||||
+5
-1
@@ -2205,4 +2205,8 @@ pleaseSelectCompetitorFirst=Please select a competitor first.
|
||||
sailorProfileRemoveMessage=Are you sure you want to remove this Sailor Profile?
|
||||
moreLoginInformationSectionSailorProfilesDescription=After logging in, you can now find a new area under your user profile, which is called 'Sailor Profiles'. You can add some competitors to such a Sailor Profile and get more insights about these competitors. In a Sailor Profile, you can see all the events your favorite competitors participated in and compare their placements to each others. Additionally, you get an overview about the most important statistics, like the maximum speed or the average distance to the start line with direct links to the Race Map to see the exact point in time, where for example the fastest competitor reached his record. For aggregative statistics like the average distance to startline at racestart, you can click on the arrow and are forwarded to Data Mining where you can drill down on the average values.
|
||||
moreLoginInformationSectionSailorProfilesHeading=Sailor Profiles
|
||||
confirmDeletion=Confirm Deletion
|
||||
confirmDeletion=Confirm Deletion
|
||||
selfServiceServer=Server has Self-Service
|
||||
publicServer=Server is public
|
||||
updatedServerSetupError=Could not update Server Setup
|
||||
updatedServerSetup=Server Setup updated
|
||||
+5
-1
@@ -2200,4 +2200,8 @@ pleaseSelectCompetitorFirst=Bitte zunächst ein Team auswählen
|
||||
sailorProfileRemoveMessage=Sicher, dass das Seglerprofil gelöscht werden soll?
|
||||
moreLoginInformationSectionSailorProfilesDescription=Nach dem Login können Sie nun in ihrem Userprofil einen neuen Bereich namens 'Seglerprofile' finden. Dort können Sie ein solches Seglerprofil anlegen, um detailliertere Informationen zum ausgewählten Team zu erhalten. In einem Seglerprofil können Sie alle Events ihrer Lieblingsteams verfolgen und deren Platzierungen direkt miteinander vergleichen. Außerdem erhalten Sie eine Übersicht über die wichtigsten Statistiken, wie die Maximalgeschwindigkeit oder die durchschnittliche Distanz zur Startlinie am Rennstart. Von hier aus gibt es auch direkte Absprungpunkte zu den für die individuelle Statistik relevanten Orten in Sailing Analytics. So können Sie etwa direkt von der Maximalgeschwindigkeit in die Race Map springen, wo dann das Rennen zu dem Zeitpunkt der Maximalgeschwindigkeit startet. Von der durchschnittlichen Distanz zur Startlinie können Sie dagegen gleich ins Data Mining wechseln, wo Sie dann einen Drill Down der Durchschnittswerte sehen.
|
||||
moreLoginInformationSectionSailorProfilesHeading=Seglerprofile
|
||||
confirmDeletion=Löschen bestätigen
|
||||
confirmDeletion=Löschen bestätigen
|
||||
selfServiceServer=Self-Service Server
|
||||
publicServer=Server ist öffentlich
|
||||
updatedServerSetupError=Konnte Server Konfiguration nicht anpassen
|
||||
updatedServerSetup=Server Konfiguration angepasst
|
||||
+30
-23
@@ -7,6 +7,7 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.gwt.core.shared.GWT;
|
||||
import com.google.gwt.dom.client.BrowserEvents;
|
||||
import com.google.gwt.user.cellview.client.CellList;
|
||||
import com.google.gwt.user.cellview.client.HasKeyboardSelectionPolicy.KeyboardSelectionPolicy;
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
@@ -16,6 +17,8 @@ import com.google.gwt.user.client.ui.HeaderPanel;
|
||||
import com.google.gwt.user.client.ui.Label;
|
||||
import com.google.gwt.user.client.ui.Panel;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.google.gwt.view.client.CellPreviewEvent;
|
||||
import com.google.gwt.view.client.CellPreviewEvent.Handler;
|
||||
import com.google.gwt.view.client.NoSelectionModel;
|
||||
import com.google.gwt.view.client.SingleSelectionModel;
|
||||
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
|
||||
@@ -115,8 +118,8 @@ public class TaggingPanel extends ComponentWithoutSettings
|
||||
* 3) another user adds/deletes/changes any tag between the latest received tag and the current timer position<br/>
|
||||
* consecutively, the timer would jump to this new tag as the selection would change automatically as the latest tag
|
||||
* changed. This selection change would also trigger the timer to jump to the latest tag, which is not intended in
|
||||
* this case. Therefor any received changes on any tags will set this boolen to true which will ignore the time jump
|
||||
* at the selection change event and prevent this wrong behaviour.
|
||||
* this case. Therefor any received changes on any tags will set this boolean to true which will ignore the time jump
|
||||
* at the selection change event and prevent this wrong behavior.
|
||||
*
|
||||
* @see #raceTimesInfosReceived(Map, long, Date, long)
|
||||
*/
|
||||
@@ -180,26 +183,31 @@ public class TaggingPanel extends ComponentWithoutSettings
|
||||
tagCellList.setEmptyListWidget(new Label(stringMessages.tagNoTagsFound()));
|
||||
tagCellList.setKeyboardSelectionPolicy(KeyboardSelectionPolicy.DISABLED);
|
||||
tagCellList.setSelectionModel(tagSelectionModel);
|
||||
tagSelectionModel.addSelectionChangeHandler(event -> {
|
||||
// set time slider to corresponding position
|
||||
TagDTO selectedTag = tagSelectionModel.getSelectedObject();
|
||||
if (selectedTag != null) {
|
||||
/**
|
||||
* Do not set time of timer when {@link #preventTimeJumpAtSelectionChangeForOnce} is set to
|
||||
* <code>true</code>. In this case set {@link #preventTimeJumpAtSelectionChangeForOnce} to
|
||||
* <code>false</code> as selection change is ignored once.
|
||||
*
|
||||
* @see #preventTimeJumpAtSelectionChangeForOnce
|
||||
*/
|
||||
if (preventTimeJumpAtSelectionChangeForOnce) {
|
||||
preventTimeJumpAtSelectionChangeForOnce = false;
|
||||
} else {
|
||||
// remove time change listener when manual selecting tag cells as this could end in an infinite loop
|
||||
// of timer change -> automatic selection change -> timer change -> ...
|
||||
timer.removeTimeListener(this);
|
||||
timer.setTime(selectedTag.getRaceTimepoint().asMillis());
|
||||
// adding time change listener again
|
||||
timer.addTimeListener(this);
|
||||
tagCellList.addCellPreviewHandler(new Handler<TagDTO>() {
|
||||
@Override
|
||||
public void onCellPreview(CellPreviewEvent<TagDTO> event) {
|
||||
if (BrowserEvents.CLICK.equals(event.getNativeEvent().getType())) {
|
||||
// set time slider to corresponding position
|
||||
TagDTO selectedTag = event.getValue();
|
||||
if (selectedTag != null) {
|
||||
/**
|
||||
* Do not set time of timer when {@link #preventTimeJumpAtSelectionChangeForOnce} is set to
|
||||
* <code>true</code>. In this case set {@link #preventTimeJumpAtSelectionChangeForOnce} to
|
||||
* <code>false</code> as selection change is ignored once.
|
||||
*
|
||||
* @see #preventTimeJumpAtSelectionChangeForOnce
|
||||
*/
|
||||
if (preventTimeJumpAtSelectionChangeForOnce) {
|
||||
preventTimeJumpAtSelectionChangeForOnce = false;
|
||||
} else {
|
||||
// remove time change listener when manually selecting tag cells as this could end in an infinite loop
|
||||
// of timer change -> automatic selection change -> timer change -> ...
|
||||
timer.removeTimeListener(TaggingPanel.this);
|
||||
timer.setTime(selectedTag.getRaceTimepoint().asMillis());
|
||||
// adding time change listener again
|
||||
timer.addTimeListener(TaggingPanel.this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -209,7 +217,6 @@ public class TaggingPanel extends ComponentWithoutSettings
|
||||
createTagsButton.addClickHandler(event -> {
|
||||
setCurrentState(State.CREATE_TAG);
|
||||
});
|
||||
|
||||
taggingPanel.setContentWidget(contentPanel);
|
||||
updateContent();
|
||||
}
|
||||
|
||||
+1
@@ -87,6 +87,7 @@
|
||||
padding: 5px 5px 0;
|
||||
border: 1px solid lightgrey;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.tagCellHeading {
|
||||
|
||||
+1
-5
@@ -53,7 +53,6 @@ import com.sap.sse.datamining.shared.impl.dto.ReducedDimensionsDTO;
|
||||
import com.sap.sse.datamining.ui.client.DataMiningService;
|
||||
import com.sap.sse.i18n.ResourceBundleStringMessages;
|
||||
import com.sap.sse.security.SecurityService;
|
||||
import com.sap.sse.security.UserStore;
|
||||
import com.sap.sse.security.shared.HasPermissions.DefaultActions;
|
||||
import com.sap.sse.util.ServiceTrackerFactory;
|
||||
|
||||
@@ -64,7 +63,6 @@ public class DataMiningServiceImpl extends RemoteServiceServlet implements DataM
|
||||
|
||||
private final ServiceTracker<DataMiningServer, DataMiningServer> dataMiningServerTracker;
|
||||
private final ServiceTracker<SecurityService, SecurityService> securityServiceTracker;
|
||||
private final ServiceTracker<UserStore, UserStore> userStoreServiceTracker;
|
||||
|
||||
private final StoredDataMiningQueryPersister storedDataMiningQueryPersistor;
|
||||
|
||||
@@ -74,10 +72,8 @@ public class DataMiningServiceImpl extends RemoteServiceServlet implements DataM
|
||||
context = Activator.getDefault();
|
||||
dataMiningServerTracker = createAndOpenDataMiningServerTracker(context);
|
||||
securityServiceTracker = ServiceTrackerFactory.createAndOpen(context, SecurityService.class);
|
||||
userStoreServiceTracker = ServiceTrackerFactory.createAndOpen(context, UserStore.class);
|
||||
|
||||
storedDataMiningQueryPersistor = new StoredDataMiningQueryPersisterImpl(securityServiceTracker.getService(),
|
||||
userStoreServiceTracker.getService());
|
||||
storedDataMiningQueryPersistor = new StoredDataMiningQueryPersisterImpl(securityServiceTracker.getService());
|
||||
dtoFactory = new DataMiningDTOFactory();
|
||||
}
|
||||
|
||||
|
||||
+84
-5
@@ -525,6 +525,7 @@ import com.sap.sailing.server.operationaltransformation.UpdateRaceDelayToLive;
|
||||
import com.sap.sailing.server.operationaltransformation.UpdateSeries;
|
||||
import com.sap.sailing.server.operationaltransformation.UpdateServerConfiguration;
|
||||
import com.sap.sailing.server.operationaltransformation.UpdateSpecificRegatta;
|
||||
import com.sap.sailing.server.security.SailingViewerRole;
|
||||
import com.sap.sailing.server.simulation.SimulationService;
|
||||
import com.sap.sailing.server.util.WaitForTrackedRaceUtil;
|
||||
import com.sap.sailing.simulator.Path;
|
||||
@@ -594,7 +595,13 @@ import com.sap.sse.security.ActionWithResult;
|
||||
import com.sap.sse.security.SecurityService;
|
||||
import com.sap.sse.security.SessionUtils;
|
||||
import com.sap.sse.security.shared.HasPermissions.DefaultActions;
|
||||
import com.sap.sse.security.shared.RoleDefinition;
|
||||
import com.sap.sse.security.shared.RoleImpl;
|
||||
import com.sap.sse.security.shared.User;
|
||||
import com.sap.sse.security.shared.UserGroup;
|
||||
import com.sap.sse.security.shared.WildcardPermission;
|
||||
import com.sap.sse.security.shared.impl.SecuredSecurityTypes;
|
||||
import com.sap.sse.security.shared.impl.SecuredSecurityTypes.ServerActions;
|
||||
import com.sap.sse.security.shared.impl.WildcardPermissionEncoder;
|
||||
import com.sap.sse.security.ui.server.SecurityDTOUtil;
|
||||
import com.sap.sse.security.ui.shared.SuccessInfo;
|
||||
@@ -4699,14 +4706,49 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
|
||||
@Override
|
||||
public ServerConfigurationDTO getServerConfiguration() {
|
||||
SailingServerConfiguration sailingServerConfiguration = getService().getSailingServerConfiguration();
|
||||
ServerConfigurationDTO result = new ServerConfigurationDTO(sailingServerConfiguration.isStandaloneServer());
|
||||
ServerConfigurationDTO result = new ServerConfigurationDTO(sailingServerConfiguration.isStandaloneServer(),
|
||||
isPublicServer(), isSelfServiceServer());
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void updateServerConfiguration(ServerConfigurationDTO serverConfiguration) {
|
||||
SailingServerConfiguration newServerConfiguration = new SailingServerConfigurationImpl(serverConfiguration.isStandaloneServer());
|
||||
getService().apply(new UpdateServerConfiguration(newServerConfiguration));
|
||||
getService().apply(new UpdateServerConfiguration(
|
||||
new SailingServerConfigurationImpl(serverConfiguration.isStandaloneServer())));
|
||||
if (serverConfiguration.isPublic() != null || serverConfiguration.isSelfService() != null) {
|
||||
final User allUser = getSecurityService().getAllUser();
|
||||
if (allUser != null) {
|
||||
final WildcardPermission createObjectOnCurrentServerPermission = SecuredSecurityTypes.SERVER
|
||||
.getPermissionForObjects(ServerActions.CREATE_OBJECT, ServerInfo.getName());
|
||||
if (serverConfiguration.isSelfService() != null) {
|
||||
if (serverConfiguration.isSelfService()) {
|
||||
getSecurityService().addPermissionForUser(allUser.getName(),
|
||||
createObjectOnCurrentServerPermission);
|
||||
} else {
|
||||
getSecurityService().removePermissionFromUser(allUser.getName(),
|
||||
createObjectOnCurrentServerPermission);
|
||||
}
|
||||
}
|
||||
if (serverConfiguration.isPublic() != null) {
|
||||
final RoleDefinition viewerRole = getSecurityService()
|
||||
.getRoleDefinition(SailingViewerRole.getInstance().getId());
|
||||
final UserGroup defaultServerTenant = getSecurityService().getDefaultTenant();
|
||||
if (viewerRole != null && defaultServerTenant != null) {
|
||||
final RoleImpl publicAccessForServerRole = new RoleImpl(viewerRole, defaultServerTenant, null);
|
||||
if (serverConfiguration.isPublic()) {
|
||||
getSecurityService().addRoleForUser(allUser.getName(), publicAccessForServerRole);
|
||||
} else {
|
||||
getSecurityService().removeRoleFromUser(allUser.getName(), publicAccessForServerRole);
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("Viewerrole or defaultServerTenant is not existing");
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("Alluser is not exiting");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -8449,7 +8491,18 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
|
||||
|
||||
@Override
|
||||
public void setDefaultTenantForCurrentServer(String tennant) {
|
||||
// TODO Auto-generated method stub
|
||||
User user = getSecurityService().getCurrentUser();
|
||||
if (user != null) {
|
||||
if (tennant == null) {
|
||||
user.getDefaultTenantMap().remove(ServerInfo.getName());
|
||||
} else {
|
||||
UserGroup userGroupCandidate = getSecurityService().getUserGroupByName(tennant);
|
||||
// FIXME allow admins to pose as any tenant?
|
||||
if (Util.contains(user.getUserGroups(), userGroupCandidate)) {
|
||||
user.getDefaultTenantMap().put(ServerInfo.getName(), userGroupCandidate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -8461,4 +8514,30 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
|
||||
}
|
||||
return tenants;
|
||||
}
|
||||
|
||||
private Boolean isSelfServiceServer() {
|
||||
final Boolean result;
|
||||
final User allUser = getSecurityService().getAllUser();
|
||||
if (allUser != null) {
|
||||
result = Util.contains(allUser.getPermissions(), SecuredSecurityTypes.SERVER
|
||||
.getPermissionForObjects(ServerActions.CREATE_OBJECT, ServerInfo.getName()));
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Boolean isPublicServer() {
|
||||
final Boolean result;
|
||||
final User allUser = getSecurityService().getAllUser();
|
||||
final RoleDefinition viewerRole = getSecurityService()
|
||||
.getRoleDefinition(SailingViewerRole.getInstance().getId());
|
||||
final UserGroup defaultServerTenant = getSecurityService().getDefaultTenant();
|
||||
if (allUser != null && viewerRole != null && defaultServerTenant != null) {
|
||||
result = allUser.hasRole(new RoleImpl(viewerRole, defaultServerTenant, null));
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
+6
-10
@@ -17,18 +17,15 @@ import com.sap.sse.datamining.shared.dto.StoredDataMiningQueryDTO;
|
||||
import com.sap.sse.datamining.shared.impl.dto.StoredDataMiningQueryDTOImpl;
|
||||
import com.sap.sse.gwt.dispatch.shared.exceptions.ServerDispatchException;
|
||||
import com.sap.sse.security.SecurityService;
|
||||
import com.sap.sse.security.UserStore;
|
||||
import com.sap.sse.security.shared.User;
|
||||
import com.sap.sse.security.shared.SecurityUser;
|
||||
|
||||
/** Implementation of {@link StoredDataMiningQueryPersister}. */
|
||||
public class StoredDataMiningQueryPersisterImpl implements StoredDataMiningQueryPersister {
|
||||
|
||||
private final SecurityService securityService;
|
||||
private final UserStore userStore;
|
||||
|
||||
public StoredDataMiningQueryPersisterImpl(SecurityService securityService, UserStore userStore) {
|
||||
public StoredDataMiningQueryPersisterImpl(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
this.userStore = userStore;
|
||||
|
||||
}
|
||||
|
||||
@@ -97,10 +94,10 @@ public class StoredDataMiningQueryPersisterImpl implements StoredDataMiningQuery
|
||||
|
||||
/** Sets a preference for the current user. */
|
||||
private void setPreferenceForCurrentUser(String preferenceKey, Object preference) {
|
||||
User currentUser = securityService.getCurrentUser();
|
||||
SecurityUser currentUser = securityService.getCurrentUser();
|
||||
if (currentUser != null) {
|
||||
try {
|
||||
userStore.setPreferenceObject(currentUser.getName(), preferenceKey, preference);
|
||||
securityService.setPreferenceObject(currentUser.getName(), preferenceKey, preference);
|
||||
} catch (AuthorizationException e) {
|
||||
throw new ServerDispatchException(e);
|
||||
}
|
||||
@@ -121,11 +118,10 @@ public class StoredDataMiningQueryPersisterImpl implements StoredDataMiningQuery
|
||||
|
||||
/** @return the preference for the current user associated with {@link preferenceKey} */
|
||||
private <T> T getPreferenceForCurrentUser(String preferenceKey) {
|
||||
User currentUser = securityService.getCurrentUser();
|
||||
SecurityUser currentUser = securityService.getCurrentUser();
|
||||
if (currentUser != null) {
|
||||
return userStore.getPreferenceObject(currentUser.getName(), preferenceKey);
|
||||
return securityService.getPreferenceObject(currentUser.getName(), preferenceKey);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+13
-1
@@ -4,16 +4,28 @@ import com.google.gwt.user.client.rpc.IsSerializable;
|
||||
|
||||
public class ServerConfigurationDTO implements IsSerializable {
|
||||
private boolean isStandaloneServer;
|
||||
private Boolean isPublic;
|
||||
private Boolean isSelfService;
|
||||
|
||||
// for GWT
|
||||
ServerConfigurationDTO() {
|
||||
}
|
||||
|
||||
public ServerConfigurationDTO(boolean isStandaloneServer) {
|
||||
public ServerConfigurationDTO(boolean isStandaloneServer, Boolean isPublic, Boolean isSelfService) {
|
||||
this.isStandaloneServer = isStandaloneServer;
|
||||
this.isPublic = isPublic;
|
||||
this.isSelfService = isSelfService;
|
||||
}
|
||||
|
||||
public boolean isStandaloneServer() {
|
||||
return isStandaloneServer;
|
||||
}
|
||||
|
||||
public Boolean isPublic() {
|
||||
return isPublic;
|
||||
}
|
||||
|
||||
public Boolean isSelfService() {
|
||||
return isSelfService;
|
||||
}
|
||||
}
|
||||
|
||||
+7
-1
@@ -84,9 +84,15 @@ public abstract class AbstractJaxRsApiTest {
|
||||
}).when(securityService).setOwnershipCheckPermissionForObjectCreationAndRevertOnError(
|
||||
Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(Action.class));
|
||||
Mockito.doAnswer(new Answer<Object>() {
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
return invocation.getArgumentAt(4, ActionWithResult.class).run();
|
||||
for (Object arg : invocation.getArguments()) {
|
||||
if (arg instanceof ActionWithResult) {
|
||||
return ((ActionWithResult) arg).run();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}).when(securityService).setOwnershipCheckPermissionForObjectCreationAndRevertOnError(
|
||||
Mockito.any(), Mockito.any(), Mockito.any(),
|
||||
|
||||
+7
-7
@@ -15,7 +15,7 @@ import org.json.simple.parser.ParseException;
|
||||
|
||||
import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException;
|
||||
import com.sap.sailing.server.gateway.jaxrs.AbstractSailingServerResource;
|
||||
import com.sap.sse.security.UserStore;
|
||||
import com.sap.sse.security.SecurityService;
|
||||
|
||||
@Path("/v1/preferences")
|
||||
public class PreferencesResource extends AbstractSailingServerResource {
|
||||
@@ -27,8 +27,8 @@ public class PreferencesResource extends AbstractSailingServerResource {
|
||||
Response response = null;
|
||||
if (SecurityUtils.getSubject().isAuthenticated()) {
|
||||
String username = SecurityUtils.getSubject().getPrincipal().toString();
|
||||
UserStore userStore = getService(UserStore.class);
|
||||
String settings = userStore.getPreference(username, settingsKey);
|
||||
SecurityService securityService = getService(SecurityService.class);
|
||||
String settings = securityService.getPreference(username, settingsKey);
|
||||
if (settings == null) {
|
||||
response = Response.noContent().build();
|
||||
} else {
|
||||
@@ -48,8 +48,8 @@ public class PreferencesResource extends AbstractSailingServerResource {
|
||||
Response response = null;
|
||||
if (SecurityUtils.getSubject().isAuthenticated()) {
|
||||
String username = SecurityUtils.getSubject().getPrincipal().toString();
|
||||
UserStore userStore = getService(UserStore.class);
|
||||
userStore.setPreference(username, settingsKey, json);
|
||||
SecurityService securityService = getService(SecurityService.class);
|
||||
securityService.setPreference(username, settingsKey, json);
|
||||
response = Response.ok().build();
|
||||
} else {
|
||||
response = Response.status(401).build();
|
||||
@@ -65,8 +65,8 @@ public class PreferencesResource extends AbstractSailingServerResource {
|
||||
Response response = null;
|
||||
if (SecurityUtils.getSubject().isAuthenticated()) {
|
||||
String username = SecurityUtils.getSubject().getPrincipal().toString();
|
||||
UserStore userStore = getService(UserStore.class);
|
||||
userStore.unsetPreference(username, settingsKey);
|
||||
SecurityService securityService = getService(SecurityService.class);
|
||||
securityService.unsetPreference(username, settingsKey);
|
||||
response = Response.ok().build();
|
||||
} else {
|
||||
response = Response.status(401).build();
|
||||
|
||||
+9
-7
@@ -36,6 +36,7 @@ import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
|
||||
import com.sap.sailing.domain.masterdataimport.TopLevelMasterData;
|
||||
import com.sap.sailing.server.gateway.jaxrs.AbstractSailingServerResource;
|
||||
import com.sap.sse.security.SecurityService;
|
||||
import com.sap.sse.security.shared.User;
|
||||
|
||||
@Path("/v1/masterdata/leaderboardgroups")
|
||||
@@ -49,7 +50,8 @@ public class MasterDataResource extends AbstractSailingServerResource {
|
||||
@QueryParam("compress") Boolean compress, @QueryParam("exportWind") Boolean exportWind, @QueryParam("exportDeviceConfigs") Boolean exportDeviceConfigs)
|
||||
throws UnsupportedEncodingException {
|
||||
|
||||
User user = getSecurityService().getCurrentUser();
|
||||
final SecurityService securityService = getSecurityService();
|
||||
User user = securityService.getCurrentUser();
|
||||
if (user == null) {
|
||||
return Response.status(Status.FORBIDDEN).build();
|
||||
}
|
||||
@@ -73,7 +75,7 @@ public class MasterDataResource extends AbstractSailingServerResource {
|
||||
if (requestedLeaderboardGroups.isEmpty()) {
|
||||
// add all visible
|
||||
for (LeaderboardGroup group : allLeaderboardGroups.values()) {
|
||||
if (getSecurityService().hasCurrentUserReadPermission(group)) {
|
||||
if (securityService.hasCurrentUserReadPermission(group)) {
|
||||
groupsToExport.add(group);
|
||||
}
|
||||
}
|
||||
@@ -82,7 +84,7 @@ public class MasterDataResource extends AbstractSailingServerResource {
|
||||
for (String name : requestedLeaderboardGroups) {
|
||||
LeaderboardGroup group = allLeaderboardGroups.get(name);
|
||||
if (group != null) {
|
||||
if (getSecurityService().hasCurrentUserReadPermission(group)) {
|
||||
if (securityService.hasCurrentUserReadPermission(group)) {
|
||||
groupsToExport.add(group);
|
||||
}
|
||||
}
|
||||
@@ -93,7 +95,7 @@ public class MasterDataResource extends AbstractSailingServerResource {
|
||||
|
||||
for (LeaderboardGroup lg : groupsToExport) {
|
||||
for (Leaderboard leaderboard : lg.getLeaderboards()) {
|
||||
if (getSecurityService().hasCurrentUserReadPermission(leaderboard)) {
|
||||
if (securityService.hasCurrentUserReadPermission(leaderboard)) {
|
||||
for (Competitor competitor : leaderboard.getAllCompetitors()) {
|
||||
competitorIds.add(competitor.getId());
|
||||
}
|
||||
@@ -111,21 +113,21 @@ public class MasterDataResource extends AbstractSailingServerResource {
|
||||
|
||||
ArrayList<Event> events = new ArrayList<>();
|
||||
for (Event event : getService().getAllEvents()) {
|
||||
if (getSecurityService().hasCurrentUserReadPermission(event)) {
|
||||
if (securityService.hasCurrentUserReadPermission(event)) {
|
||||
events.add(event);
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList<MediaTrack> mediaTracks = new ArrayList<>();
|
||||
for (MediaTrack mediaTrack : getService().getAllMediaTracks()) {
|
||||
if (getSecurityService().hasCurrentUserReadPermission(mediaTrack)) {
|
||||
if (securityService.hasCurrentUserReadPermission(mediaTrack)) {
|
||||
mediaTracks.add(mediaTrack);
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, Regatta> regattaRaceIds = new HashMap<>();
|
||||
for (Entry<String, Regatta> regattaRaceMap : getService().getPersistentRegattasForRaceIDs().entrySet()) {
|
||||
if (getSecurityService().hasCurrentUserReadPermission(regattaRaceMap.getValue())) {
|
||||
if (securityService.hasCurrentUserReadPermission(regattaRaceMap.getValue())) {
|
||||
regattaRaceIds.put(regattaRaceMap.getKey(), regattaRaceMap.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,5 +32,6 @@ Require-Bundle: com.sap.sailing.server,
|
||||
javax.ws.rs,
|
||||
com.sap.sailing.server.testsupport,
|
||||
com.sap.sse.security,
|
||||
com.sap.sse.security.common
|
||||
com.sap.sse.security.common,
|
||||
com.sun.jersey;bundle-version="1.17.0"
|
||||
Automatic-Module-Name: com.sap.sailing.server.replication.test
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package com.sap.sailing.server.replication.test;
|
||||
|
||||
import com.sap.sailing.server.gateway.jaxrs.spi.MasterDataResource;
|
||||
import com.sap.sse.security.SecurityService;
|
||||
|
||||
public class DummyMasterDataResource extends MasterDataResource {
|
||||
@Override
|
||||
protected SecurityService getSecurityService() {
|
||||
return super.getSecurityService();
|
||||
}
|
||||
}
|
||||
+26
-10
@@ -37,7 +37,6 @@ import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
|
||||
import com.sap.sailing.domain.test.TrackBasedTest;
|
||||
import com.sap.sailing.server.RacingEventService;
|
||||
import com.sap.sailing.server.gateway.jaxrs.AbstractSailingServerResource;
|
||||
import com.sap.sailing.server.gateway.jaxrs.spi.MasterDataResource;
|
||||
import com.sap.sailing.server.impl.RacingEventServiceImpl;
|
||||
import com.sap.sailing.server.masterdata.MasterDataImporter;
|
||||
import com.sap.sse.common.Duration;
|
||||
@@ -49,6 +48,9 @@ import com.sap.sse.common.media.MimeType;
|
||||
import com.sap.sse.mongodb.MongoDBConfiguration;
|
||||
import com.sap.sse.mongodb.MongoDBService;
|
||||
import com.sap.sse.security.SecurityService;
|
||||
import com.sap.sse.security.shared.User;
|
||||
import com.sap.sse.security.shared.WithQualifiedObjectIdentifier;
|
||||
import com.sap.sse.security.shared.impl.UserGroupImpl;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
@@ -188,8 +190,22 @@ public class MediaReplicationTest extends AbstractServerReplicationTest {
|
||||
@Test
|
||||
public void testMasterDataImportForMediaTracks() throws MalformedURLException, IOException, InterruptedException,
|
||||
ClassNotFoundException {
|
||||
|
||||
UserGroupImpl defaultTenant = new UserGroupImpl(new UUID(0, 1), "defaultTenant");
|
||||
User currentUser = Mockito.mock(User.class);
|
||||
|
||||
SecurityService securityService = Mockito.mock(SecurityService.class);
|
||||
Mockito.doReturn(defaultTenant).when(securityService).getDefaultTenant();
|
||||
Mockito.doReturn(currentUser).when(securityService).getCurrentUser();
|
||||
Mockito.doReturn(true).when(securityService).hasCurrentUserReadPermission(Mockito.any());
|
||||
Mockito.doNothing().when(securityService).checkCurrentUserReadPermission(Mockito.any());
|
||||
Mockito.doReturn(true).when(securityService)
|
||||
.hasCurrentUserReadPermission(Mockito.any(WithQualifiedObjectIdentifier.class));
|
||||
|
||||
|
||||
// Setup source service
|
||||
RacingEventService sourceService = new RacingEventServiceImpl();
|
||||
RacingEventServiceImpl sourceService = Mockito.spy(new RacingEventServiceImpl());
|
||||
Mockito.doReturn(securityService).when(sourceService).getSecurityService();
|
||||
Set<RegattaAndRaceIdentifier> assignedRaces = new HashSet<RegattaAndRaceIdentifier>();
|
||||
String regattaName1 = "49er";
|
||||
String regattaName2 = "49er FX";
|
||||
@@ -234,32 +250,32 @@ public class MediaReplicationTest extends AbstractServerReplicationTest {
|
||||
List<String> groupNamesToExport = Collections.singletonList(leaderboardGroup.getName());
|
||||
|
||||
final DomainFactory domainFactory;
|
||||
MasterDataResource resource = new MasterDataResource();
|
||||
MasterDataResource spyResource = spyResource(resource, sourceService);
|
||||
DummyMasterDataResource spyResource = spyResource(new DummyMasterDataResource(), sourceService);
|
||||
Mockito.doReturn(securityService).when(spyResource).getSecurityService();
|
||||
Response response = spyResource.getMasterDataByLeaderboardGroups(groupNamesToExport, false, true, false);
|
||||
StreamingOutput streamingOutput = (StreamingOutput) response.getEntity();
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
UUID randomUUID = UUID.randomUUID();
|
||||
ByteArrayInputStream inputStream = null;
|
||||
try {
|
||||
RacingEventServiceImpl fmaster = Mockito.spy(master);
|
||||
Mockito.doReturn(securityService).when(fmaster).getSecurityService();
|
||||
streamingOutput.write(os);
|
||||
os.flush();
|
||||
// Delete all data above from the database, to allow recreating all of it on target server
|
||||
deleteAllDataFromDatabase();
|
||||
// Import in new service
|
||||
domainFactory = master.getBaseDomainFactory();
|
||||
domainFactory = fmaster.getBaseDomainFactory();
|
||||
// ensure that this class's class loader and with it the dependency to com.sap.sailing.domain.test
|
||||
// is known during de-serialization because anonymous inner classes from that bundle may be used
|
||||
// in the object graph, e.g., for RankingMetricConstructor objects based on locally-instantiated lambda
|
||||
// expressions
|
||||
master.addMasterDataClassLoader(this.getClass().getClassLoader());
|
||||
inputStream = new ByteArrayInputStream(os.toByteArray());
|
||||
MasterDataImporter importer = new MasterDataImporter(domainFactory, master,
|
||||
fmaster.addMasterDataClassLoader(this.getClass().getClassLoader());
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(os.toByteArray());
|
||||
MasterDataImporter importer = new MasterDataImporter(domainFactory, fmaster,
|
||||
Mockito.mock(SecurityService.class));
|
||||
importer.importFromStream(inputStream, randomUUID, false);
|
||||
} finally {
|
||||
os.close();
|
||||
inputStream.close();
|
||||
}
|
||||
|
||||
// ---Asserts---
|
||||
|
||||
@@ -23,7 +23,8 @@ Require-Bundle: com.sap.sailing.domain,
|
||||
org.eclipse.jetty.server;bundle-version="9.4.8",
|
||||
com.sap.sailing.domain.shared.android,
|
||||
com.sap.sailing.domain.racelogtrackingadapter.testsupport,
|
||||
com.sap.sailing.server.testsupport
|
||||
com.sap.sailing.server.testsupport,
|
||||
com.sun.jersey;bundle-version="1.17.0"
|
||||
Import-Package: com.sap.sailing.server.gateway.jaxrs,
|
||||
com.sap.sailing.server.gateway.jaxrs.spi,
|
||||
com.sap.sse.security.userstore.mongodb,
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package com.sap.sailing.server.test;
|
||||
|
||||
import com.sap.sailing.server.gateway.jaxrs.spi.MasterDataResource;
|
||||
import com.sap.sse.security.SecurityService;
|
||||
|
||||
/**
|
||||
* required for package visibility for getSecurityService
|
||||
*/
|
||||
public class DummyMasterDataRessource extends MasterDataResource {
|
||||
@Override
|
||||
protected SecurityService getSecurityService() {
|
||||
return super.getSecurityService();
|
||||
}
|
||||
}
|
||||
+115
-98
@@ -30,6 +30,9 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.StreamingOutput;
|
||||
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.apache.shiro.mgt.SecurityManager;
|
||||
import org.apache.shiro.subject.Subject;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -116,6 +119,8 @@ import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
|
||||
import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl;
|
||||
import com.sap.sailing.domain.leaderboard.impl.LowPoint;
|
||||
import com.sap.sailing.domain.leaderboard.meta.LeaderboardGroupMetaLeaderboard;
|
||||
import com.sap.sailing.domain.persistence.DomainObjectFactory;
|
||||
import com.sap.sailing.domain.persistence.MongoObjectFactory;
|
||||
import com.sap.sailing.domain.persistence.PersistenceFactory;
|
||||
import com.sap.sailing.domain.persistence.media.MediaDBFactory;
|
||||
import com.sap.sailing.domain.racelog.tracking.EmptySensorFixStore;
|
||||
@@ -130,7 +135,6 @@ import com.sap.sailing.domain.tracking.WindTrack;
|
||||
import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
|
||||
import com.sap.sailing.server.RacingEventService;
|
||||
import com.sap.sailing.server.gateway.jaxrs.AbstractSailingServerResource;
|
||||
import com.sap.sailing.server.gateway.jaxrs.spi.MasterDataResource;
|
||||
import com.sap.sailing.server.impl.RacingEventServiceImpl;
|
||||
import com.sap.sailing.server.masterdata.DummyTrackedRace;
|
||||
import com.sap.sailing.server.masterdata.MasterDataImporter;
|
||||
@@ -146,6 +150,9 @@ import com.sap.sse.common.media.MimeType;
|
||||
import com.sap.sse.mongodb.MongoDBConfiguration;
|
||||
import com.sap.sse.mongodb.MongoDBService;
|
||||
import com.sap.sse.security.SecurityService;
|
||||
import com.sap.sse.security.shared.User;
|
||||
import com.sap.sse.security.shared.WithQualifiedObjectIdentifier;
|
||||
import com.sap.sse.security.shared.impl.UserGroupImpl;
|
||||
import com.sap.sse.shared.media.ImageDescriptor;
|
||||
import com.sap.sse.shared.media.VideoDescriptor;
|
||||
|
||||
@@ -173,6 +180,8 @@ public class MasterDataImportTest {
|
||||
* Log Events created when running test. Will be removed from db at teardown
|
||||
*/
|
||||
private Set<Serializable> storedLogUUIDs = new HashSet<Serializable>();
|
||||
private RacingEventService sourceService;
|
||||
private DummyMasterDataRessource masterDataResource;
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
@@ -181,7 +190,44 @@ public class MasterDataImportTest {
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
UserGroupImpl defaultTenant = new UserGroupImpl(new UUID(0, 1), "defaultTenant");
|
||||
User currentUser = Mockito.mock(User.class);
|
||||
|
||||
securityService = Mockito.mock(SecurityService.class);
|
||||
SecurityManager securityManager = Mockito.mock(org.apache.shiro.mgt.SecurityManager.class);
|
||||
Subject fakeSubject = Mockito.mock(Subject.class);
|
||||
|
||||
SecurityUtils.setSecurityManager(securityManager);
|
||||
Mockito.doReturn(fakeSubject).when(securityManager).createSubject(Mockito.any());
|
||||
Mockito.doReturn(defaultTenant).when(securityService).getDefaultTenant();
|
||||
Mockito.doReturn(currentUser).when(securityService).getCurrentUser();
|
||||
Mockito.doReturn(true).when(securityService).hasCurrentUserReadPermission(Mockito.any());
|
||||
Mockito.doNothing().when(securityService).checkCurrentUserReadPermission(Mockito.any());
|
||||
|
||||
Mockito.doReturn(true).when(securityService)
|
||||
.hasCurrentUserReadPermission(Mockito.any(WithQualifiedObjectIdentifier.class));
|
||||
|
||||
|
||||
Mockito.doReturn(true).when(fakeSubject).isAuthenticated();
|
||||
|
||||
DomainFactory sourceDomainFactory = new DomainFactoryImpl((srlid) -> null);
|
||||
|
||||
DomainObjectFactory dbFactory = PersistenceFactory.INSTANCE.getDomainObjectFactory(MongoDBService.INSTANCE,
|
||||
sourceDomainFactory);
|
||||
MongoObjectFactory mongoObjectFactory = PersistenceFactory.INSTANCE
|
||||
.getMongoObjectFactory(MongoDBService.INSTANCE);
|
||||
|
||||
|
||||
sourceService = Mockito
|
||||
.spy(new RacingEventServiceImpl(dbFactory, mongoObjectFactory,
|
||||
MediaDBFactory.INSTANCE.getDefaultMediaDB(), EmptyWindStore.INSTANCE, EmptySensorFixStore.INSTANCE,
|
||||
false));
|
||||
|
||||
|
||||
masterDataResource = spyResource(new DummyMasterDataRessource(), sourceService);
|
||||
doReturn(securityService).when(masterDataResource).getSecurityService();
|
||||
doReturn(securityService).when(sourceService).getSecurityService();
|
||||
|
||||
deleteAllDataFromDatabase();
|
||||
}
|
||||
|
||||
@@ -213,7 +259,10 @@ public class MasterDataImportTest {
|
||||
ClassNotFoundException {
|
||||
// Setup source service
|
||||
MockSmartphoneImeiServiceFinderFactory serviceFinderFactory = new MockSmartphoneImeiServiceFinderFactory();
|
||||
RacingEventService sourceService = new RacingEventServiceImpl(null, null, serviceFinderFactory);
|
||||
RacingEventServiceImpl sourceService = Mockito
|
||||
.spy(new RacingEventServiceImpl(null, null, serviceFinderFactory));
|
||||
Mockito.doReturn(securityService).when(sourceService).getSecurityService();
|
||||
|
||||
Event event = sourceService.addEvent(TEST_EVENT_NAME, /* eventDescription */null, eventStartDate, eventEndDate,
|
||||
"testVenue", false, eventUUID);
|
||||
UUID courseAreaUUID = UUID.randomUUID();
|
||||
@@ -280,7 +329,8 @@ public class MasterDataImportTest {
|
||||
null, team2, /* timeOnTimeFactor */null, /* timeOnDistanceAllowancePerNauticalMile */null, null);
|
||||
competitors.add(competitorToSuppress);
|
||||
RaceColumn raceColumn = leaderboard.getRaceColumnByName(raceColumnName);
|
||||
TrackedRace trackedRace = new DummyTrackedRace(raceId, createCompetitorsAndBoatsMap(boatClass, competitors), regatta, null, sourceService.getWindStore());
|
||||
TrackedRace trackedRace = new DummyTrackedRace(raceId, createCompetitorsAndBoatsMap(boatClass, competitors),
|
||||
regatta, null, sourceService.getWindStore());
|
||||
|
||||
raceColumn.setTrackedRace(testFleet1, trackedRace);
|
||||
|
||||
@@ -355,10 +405,10 @@ public class MasterDataImportTest {
|
||||
List<String> groupNamesToExport = new ArrayList<String>();
|
||||
groupNamesToExport.add(group.getName());
|
||||
|
||||
RacingEventService destService;
|
||||
RacingEventServiceImplMock destService;
|
||||
DomainFactory domainFactory;
|
||||
MasterDataResource resource = new MasterDataResource();
|
||||
MasterDataResource spyResource = spyResource(resource, sourceService);
|
||||
DummyMasterDataRessource spyResource = spyResource(new DummyMasterDataRessource(), sourceService);
|
||||
Mockito.doReturn(securityService).when(spyResource).getSecurityService();
|
||||
Response response = spyResource.getMasterDataByLeaderboardGroups(groupNamesToExport, false, true, false);
|
||||
StreamingOutput streamingOutput = (StreamingOutput) response.getEntity();
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
@@ -370,7 +420,11 @@ public class MasterDataImportTest {
|
||||
// Delete all data above from the database, to allow recreating all of it on target server
|
||||
deleteAllDataFromDatabase();
|
||||
// Import in new service
|
||||
destService = new RacingEventServiceImplMock(new DataImportProgressImpl(randomUUID), serviceFinderFactory){};
|
||||
destService = Mockito.spy(
|
||||
new RacingEventServiceImplMock(new DataImportProgressImpl(randomUUID), serviceFinderFactory) {
|
||||
});
|
||||
Mockito.doReturn(securityService).when(destService).getSecurityService();
|
||||
|
||||
domainFactory = destService.getBaseDomainFactory();
|
||||
DB db = destService.getMongoObjectFactory().getDatabase();
|
||||
db.setWriteConcern(WriteConcern.SAFE);
|
||||
@@ -539,7 +593,6 @@ public class MasterDataImportTest {
|
||||
public void testMasterDataImportForScoreCorrections() throws MalformedURLException, IOException,
|
||||
InterruptedException, ClassNotFoundException {
|
||||
// Setup source service
|
||||
RacingEventService sourceService = new RacingEventServiceImpl();
|
||||
Event event = sourceService.addEvent(TEST_EVENT_NAME, /* eventDescription */null, eventStartDate, eventEndDate,
|
||||
"testVenue", false, eventUUID);
|
||||
UUID courseAreaUUID = UUID.randomUUID();
|
||||
@@ -557,7 +610,8 @@ public class MasterDataImportTest {
|
||||
FleetImpl testFleet1 = new FleetImpl("testFleet1");
|
||||
fleets.add(testFleet1);
|
||||
fleets.add(new FleetImpl("testFleet2"));
|
||||
series.add(new SeriesImpl("testSeries", false, /* isFleetsCanRunInParallel */ true, fleets, emptyRaceColumnNamesList, sourceService));
|
||||
series.add(new SeriesImpl("testSeries", false, /* isFleetsCanRunInParallel */ true, fleets,
|
||||
emptyRaceColumnNamesList, sourceService));
|
||||
UUID regattaUUID = UUID.randomUUID();
|
||||
Regatta regatta = sourceService.createRegatta(
|
||||
RegattaImpl.getDefaultName(TEST_REGATTA_NAME, TEST_BOAT_CLASS_NAME), TEST_BOAT_CLASS_NAME,
|
||||
@@ -599,7 +653,8 @@ public class MasterDataImportTest {
|
||||
/* timeOnTimeFactor */null, /* timeOnDistanceAllowancePerNauticalMile */null, null);
|
||||
competitors.add(competitor2);
|
||||
RaceColumn raceColumn = leaderboard.getRaceColumnByName(raceColumnName);
|
||||
TrackedRace trackedRace = new DummyTrackedRace(raceId, createCompetitorsAndBoatsMap(boatClass, competitors), regatta, null, sourceService.getWindStore());
|
||||
TrackedRace trackedRace = new DummyTrackedRace(raceId, createCompetitorsAndBoatsMap(boatClass, competitors),
|
||||
regatta, null, sourceService.getWindStore());
|
||||
|
||||
raceColumn.setTrackedRace(testFleet1, trackedRace);
|
||||
|
||||
@@ -620,29 +675,26 @@ public class MasterDataImportTest {
|
||||
groupNamesToExport.add(group.getName());
|
||||
|
||||
RacingEventService destService;
|
||||
DomainFactory domainFactory;
|
||||
MasterDataResource resource = new MasterDataResource();
|
||||
MasterDataResource spyResource = spyResource(resource, sourceService);
|
||||
Response response = spyResource.getMasterDataByLeaderboardGroups(groupNamesToExport, false, true, false);
|
||||
Response response = masterDataResource.getMasterDataByLeaderboardGroups(groupNamesToExport, false, true, false);
|
||||
StreamingOutput streamingOutput = (StreamingOutput) response.getEntity();
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
UUID randomUUID = UUID.randomUUID();
|
||||
ByteArrayInputStream inputStream = null;
|
||||
DomainFactory domainFactory;
|
||||
try {
|
||||
streamingOutput.write(os);
|
||||
os.flush();
|
||||
// Delete all data above from the database, to allow recreating all of it on target server
|
||||
deleteAllDataFromDatabase();
|
||||
// Import in new service
|
||||
destService = new RacingEventServiceImplMock(new DataImportProgressImpl(randomUUID)){};
|
||||
destService = getDestService(randomUUID);
|
||||
|
||||
domainFactory = destService.getBaseDomainFactory();
|
||||
inputStream = new ByteArrayInputStream(os.toByteArray());
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(os.toByteArray());
|
||||
|
||||
MasterDataImporter importer = new MasterDataImporter(domainFactory, destService, securityService);
|
||||
importer.importFromStream(inputStream, randomUUID, false);
|
||||
} finally {
|
||||
os.close();
|
||||
inputStream.close();
|
||||
}
|
||||
|
||||
MasterDataImportObjectCreationCount creationCount = destService.getDataImportLock().getProgress(randomUUID)
|
||||
@@ -695,6 +747,14 @@ public class MasterDataImportTest {
|
||||
raceColumnOnTarget, MillisecondsTimePoint.now()));
|
||||
}
|
||||
|
||||
private RacingEventService getDestService(UUID randomUUID) {
|
||||
RacingEventServiceImplMock destService = Mockito
|
||||
.spy(new RacingEventServiceImplMock(new DataImportProgressImpl(randomUUID)) {
|
||||
});
|
||||
Mockito.doReturn(securityService).when(destService).getSecurityService();
|
||||
return destService;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMasterDataImportForWind() throws MalformedURLException, IOException, InterruptedException,
|
||||
ClassNotFoundException {
|
||||
@@ -767,10 +827,10 @@ public class MasterDataImportTest {
|
||||
List<String> groupNamesToExport = new ArrayList<String>();
|
||||
groupNamesToExport.add(group.getName());
|
||||
|
||||
RacingEventService destService;
|
||||
RacingEventServiceImplMock destService;
|
||||
DomainFactory domainFactory;
|
||||
MasterDataResource resource = new MasterDataResource();
|
||||
MasterDataResource spyResource = spyResource(resource, sourceService);
|
||||
DummyMasterDataRessource spyResource = spyResource(new DummyMasterDataRessource(), sourceService);
|
||||
Mockito.doReturn(securityService).when(spyResource).getSecurityService();
|
||||
Response response = spyResource.getMasterDataByLeaderboardGroups(groupNamesToExport, false, true, false);
|
||||
StreamingOutput streamingOutput = (StreamingOutput) response.getEntity();
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
@@ -782,7 +842,9 @@ public class MasterDataImportTest {
|
||||
// Delete all data above from the database, to allow recreating all of it on target server
|
||||
deleteAllDataFromDatabase();
|
||||
// Import in new service
|
||||
destService = new RacingEventServiceImplMock(new DataImportProgressImpl(randomUUID)){};
|
||||
destService = Mockito.spy(new RacingEventServiceImplMock(new DataImportProgressImpl(randomUUID)) {
|
||||
});
|
||||
Mockito.doReturn(securityService).when(destService).getSecurityService();
|
||||
domainFactory = destService.getBaseDomainFactory();
|
||||
inputStream = new ByteArrayInputStream(os.toByteArray());
|
||||
|
||||
@@ -853,15 +915,10 @@ public class MasterDataImportTest {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testMasterDataImportForRaceLogEventsReferencingCompetitors() throws MalformedURLException, IOException,
|
||||
InterruptedException, ClassNotFoundException {
|
||||
// Setup source service
|
||||
DomainFactory sourceDomainFactory = new DomainFactoryImpl((srlid) -> null);
|
||||
RacingEventService sourceService = new RacingEventServiceImpl(
|
||||
PersistenceFactory.INSTANCE.getDomainObjectFactory(MongoDBService.INSTANCE, sourceDomainFactory),
|
||||
PersistenceFactory.INSTANCE.getMongoObjectFactory(MongoDBService.INSTANCE),
|
||||
MediaDBFactory.INSTANCE.getDefaultMediaDB(), EmptyWindStore.INSTANCE, EmptySensorFixStore.INSTANCE, /* restoreTrackedRaces */ false);
|
||||
Event event = sourceService.addEvent(TEST_EVENT_NAME, /* eventDescription */null, eventStartDate, eventEndDate,
|
||||
"testVenue", false, eventUUID);
|
||||
UUID courseAreaUUID = UUID.randomUUID();
|
||||
@@ -910,7 +967,7 @@ public class MasterDataImportTest {
|
||||
"Der Lennart halt");
|
||||
DynamicTeam team = new TeamImpl("Pros", sailors, coach);
|
||||
BoatClass boatClass = new BoatClassImpl("H16", true);
|
||||
Competitor competitor = sourceDomainFactory.getOrCreateCompetitor(competitorUUID, "Froderik", "F", Color.RED,
|
||||
Competitor competitor = sourceService.getBaseDomainFactory().getOrCreateCompetitor(competitorUUID, "Froderik", "F", Color.RED,
|
||||
"noone@nowhere.de", null, team, /* timeOnTimeFactor */null, /* timeOnDistanceAllowanceInSecondsPerNauticalMile */
|
||||
null, null);
|
||||
competitors.add(competitor);
|
||||
@@ -919,7 +976,7 @@ public class MasterDataImportTest {
|
||||
sailors2.add(new PersonImpl("Test Mustermann", new NationalityImpl("GER"), new Date(645487200000L), "desc"));
|
||||
DynamicPerson coach2 = new PersonImpl("Max Test", new NationalityImpl("GER"), new Date(645487200000L), "desc");
|
||||
DynamicTeam team2 = new TeamImpl("Pros2", sailors2, coach2);
|
||||
Competitor competitor2 = sourceDomainFactory.getCompetitorAndBoatStore().getOrCreateCompetitor(competitor2UUID,
|
||||
Competitor competitor2 = sourceService.getBaseDomainFactory().getCompetitorAndBoatStore().getOrCreateCompetitor(competitor2UUID,
|
||||
"Froderik", "F", Color.RED, "noone@nowhere.de", null, team2, /* timeOnTimeFactor */null, /* timeOnDistanceAllowanceInSecondsPerNauticalMile */
|
||||
null, null);
|
||||
competitors.add(competitor2);
|
||||
@@ -952,9 +1009,7 @@ public class MasterDataImportTest {
|
||||
|
||||
RacingEventService destService;
|
||||
DomainFactory domainFactory;
|
||||
MasterDataResource resource = new MasterDataResource();
|
||||
MasterDataResource spyResource = spyResource(resource, sourceService);
|
||||
Response response = spyResource.getMasterDataByLeaderboardGroups(groupNamesToExport, false, true, false);
|
||||
Response response = masterDataResource.getMasterDataByLeaderboardGroups(groupNamesToExport, false, true, false);
|
||||
StreamingOutput streamingOutput = (StreamingOutput) response.getEntity();
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
UUID randomUUID = UUID.randomUUID();
|
||||
@@ -965,7 +1020,7 @@ public class MasterDataImportTest {
|
||||
// Delete all data above from the database, to allow recreating all of it on target server
|
||||
deleteAllDataFromDatabase();
|
||||
// Import in new service
|
||||
destService = new RacingEventServiceImplMock(new DataImportProgressImpl(randomUUID)){};
|
||||
destService = getDestService(randomUUID);
|
||||
domainFactory = destService.getBaseDomainFactory();
|
||||
inputStream = new ByteArrayInputStream(os.toByteArray());
|
||||
|
||||
@@ -1116,9 +1171,7 @@ public class MasterDataImportTest {
|
||||
|
||||
RacingEventService destService;
|
||||
DomainFactory domainFactory;
|
||||
MasterDataResource resource = new MasterDataResource();
|
||||
MasterDataResource spyResource = spyResource(resource, sourceService);
|
||||
Response response = spyResource.getMasterDataByLeaderboardGroups(groupNamesToExport, false, true, false);
|
||||
Response response = masterDataResource.getMasterDataByLeaderboardGroups(groupNamesToExport, false, true, false);
|
||||
StreamingOutput streamingOutput = (StreamingOutput) response.getEntity();
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
UUID randomUUID = UUID.randomUUID();
|
||||
@@ -1134,7 +1187,7 @@ public class MasterDataImportTest {
|
||||
// Delete all data above from the database, to allow recreating all of it on target server
|
||||
deleteAllDataFromDatabase();
|
||||
// Import in new service
|
||||
destService = new RacingEventServiceImplMock(new DataImportProgressImpl(randomUUID)){};
|
||||
destService = getDestService(randomUUID);
|
||||
domainFactory = destService.getBaseDomainFactory();
|
||||
// Create existing data on target
|
||||
venueNameNotToOverride = "doNotOverride";
|
||||
@@ -1179,7 +1232,6 @@ public class MasterDataImportTest {
|
||||
importer.importFromStream(inputStream, randomUUID, false);
|
||||
} finally {
|
||||
os.close();
|
||||
inputStream.close();
|
||||
}
|
||||
|
||||
MasterDataImportObjectCreationCount creationCount = destService.getDataImportLock().getProgress(randomUUID)
|
||||
@@ -1224,8 +1276,6 @@ public class MasterDataImportTest {
|
||||
@Test
|
||||
public void testMasterDataImportWithOverrideWithoutHttpStack() throws MalformedURLException, IOException,
|
||||
InterruptedException, ClassNotFoundException {
|
||||
// Setup source service
|
||||
RacingEventService sourceService = new RacingEventServiceImpl();
|
||||
Event event = sourceService.addEvent(TEST_EVENT_NAME, /* eventDescription */null, eventStartDate, eventEndDate,
|
||||
"testVenue", false, eventUUID);
|
||||
UUID courseAreaUUID = UUID.randomUUID();
|
||||
@@ -1321,21 +1371,18 @@ public class MasterDataImportTest {
|
||||
|
||||
RacingEventService destService;
|
||||
DomainFactory domainFactory;
|
||||
MasterDataResource resource = new MasterDataResource();
|
||||
MasterDataResource spyResource = spyResource(resource, sourceService);
|
||||
Response response = spyResource.getMasterDataByLeaderboardGroups(groupNamesToExport, false, true, false);
|
||||
Response response = masterDataResource.getMasterDataByLeaderboardGroups(groupNamesToExport, false, true, false);
|
||||
StreamingOutput streamingOutput = (StreamingOutput) response.getEntity();
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
UUID randomUUID = UUID.randomUUID();
|
||||
RegattaAndRaceIdentifier identifierOfRegattaTrackedRace;
|
||||
ByteArrayInputStream inputStream = null;
|
||||
try {
|
||||
streamingOutput.write(os);
|
||||
os.flush();
|
||||
// Delete all data above from the database, to allow recreating all of it on target server
|
||||
deleteAllDataFromDatabase();
|
||||
// Import in new service
|
||||
destService = new RacingEventServiceImplMock(new DataImportProgressImpl(randomUUID)){};
|
||||
destService = getDestService(randomUUID);
|
||||
domainFactory = destService.getBaseDomainFactory();
|
||||
// Create existing data on target
|
||||
String venueNameToOverride = "Override";
|
||||
@@ -1395,13 +1442,12 @@ public class MasterDataImportTest {
|
||||
null, false, new ArrayList<String>(), null, null);
|
||||
destService.getLeaderboardGroupByName(TEST_GROUP_NAME).addLeaderboard(leaderboardToOverride);
|
||||
destService.addLeaderboard(leaderboardToOverride);
|
||||
inputStream = new ByteArrayInputStream(os.toByteArray());
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(os.toByteArray());
|
||||
|
||||
MasterDataImporter importer = new MasterDataImporter(domainFactory, destService, securityService);
|
||||
importer.importFromStream(inputStream, randomUUID, true);
|
||||
} finally {
|
||||
os.close();
|
||||
inputStream.close();
|
||||
}
|
||||
|
||||
MasterDataImportObjectCreationCount creationCount = destService.getDataImportLock().getProgress(randomUUID)
|
||||
@@ -1457,8 +1503,6 @@ public class MasterDataImportTest {
|
||||
@Test
|
||||
public void testMasterDataImportForRegattaDefaultProcedureAndDesigner() throws MalformedURLException, IOException,
|
||||
InterruptedException, ClassNotFoundException {
|
||||
RacingEventService sourceService = new RacingEventServiceImpl();
|
||||
|
||||
Event event = sourceService.createEventWithoutReplication("Test Event", /* eventDescription */null,
|
||||
new MillisecondsTimePoint(0), new MillisecondsTimePoint(10), "testvenue", false, UUID.randomUUID(),
|
||||
/* officialWebsiteURL */null, /*baseURL*/null,
|
||||
@@ -1492,9 +1536,7 @@ public class MasterDataImportTest {
|
||||
|
||||
RacingEventService destService;
|
||||
DomainFactory domainFactory;
|
||||
MasterDataResource resource = new MasterDataResource();
|
||||
MasterDataResource spyResource = spyResource(resource, sourceService);
|
||||
Response response = spyResource.getMasterDataByLeaderboardGroups(groupNamesToExport, false, true, false);
|
||||
Response response = masterDataResource.getMasterDataByLeaderboardGroups(groupNamesToExport, false, true, false);
|
||||
StreamingOutput streamingOutput = (StreamingOutput) response.getEntity();
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
UUID randomUUID = UUID.randomUUID();
|
||||
@@ -1505,7 +1547,7 @@ public class MasterDataImportTest {
|
||||
// Delete all data above from the database, to allow recreating all of it on target server
|
||||
deleteAllDataFromDatabase();
|
||||
// Import in new service
|
||||
destService = new RacingEventServiceImplMock(new DataImportProgressImpl(randomUUID)){};
|
||||
destService = getDestService(randomUUID);
|
||||
domainFactory = destService.getBaseDomainFactory();
|
||||
inputStream = new ByteArrayInputStream(os.toByteArray());
|
||||
|
||||
@@ -1527,9 +1569,6 @@ public class MasterDataImportTest {
|
||||
@Test
|
||||
public void testMasterDataImportForRegattaWithoutCourseArea() throws MalformedURLException, IOException,
|
||||
InterruptedException, ClassNotFoundException {
|
||||
// Setup source service
|
||||
RacingEventService sourceService = new RacingEventServiceImpl();
|
||||
|
||||
Event event = sourceService.createEventWithoutReplication("Test Event", /* eventDescription */null,
|
||||
new MillisecondsTimePoint(0), new MillisecondsTimePoint(10), "testvenue", false, UUID.randomUUID(),
|
||||
/* officialWebsiteURL */null, /*baseURL*/null,
|
||||
@@ -1627,28 +1666,24 @@ public class MasterDataImportTest {
|
||||
|
||||
RacingEventService destService;
|
||||
DomainFactory domainFactory;
|
||||
MasterDataResource resource = new MasterDataResource();
|
||||
MasterDataResource spyResource = spyResource(resource, sourceService);
|
||||
Response response = spyResource.getMasterDataByLeaderboardGroups(groupNamesToExport, false, true, false);
|
||||
Response response = masterDataResource.getMasterDataByLeaderboardGroups(groupNamesToExport, false, true, false);
|
||||
StreamingOutput streamingOutput = (StreamingOutput) response.getEntity();
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
UUID randomUUID = UUID.randomUUID();
|
||||
ByteArrayInputStream inputStream = null;
|
||||
try {
|
||||
streamingOutput.write(os);
|
||||
os.flush();
|
||||
// Delete all data above from the database, to allow recreating all of it on target server
|
||||
deleteAllDataFromDatabase();
|
||||
// Import in new service
|
||||
destService = new RacingEventServiceImplMock(new DataImportProgressImpl(randomUUID)){};
|
||||
destService = getDestService(randomUUID);
|
||||
domainFactory = destService.getBaseDomainFactory();
|
||||
inputStream = new ByteArrayInputStream(os.toByteArray());
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(os.toByteArray());
|
||||
|
||||
MasterDataImporter importer = new MasterDataImporter(domainFactory, destService, securityService);
|
||||
importer.importFromStream(inputStream, randomUUID, false);
|
||||
} finally {
|
||||
os.close();
|
||||
inputStream.close();
|
||||
}
|
||||
|
||||
MasterDataImportObjectCreationCount creationCount = destService.getDataImportLock().getProgress(randomUUID)
|
||||
@@ -1665,8 +1700,6 @@ public class MasterDataImportTest {
|
||||
@Test
|
||||
public void testMasterDataImportForPersistentRegattaRaceIDsWithoutHttpStack() throws MalformedURLException,
|
||||
IOException, InterruptedException, ClassNotFoundException {
|
||||
// Setup source service
|
||||
RacingEventService sourceService = new RacingEventServiceImpl();
|
||||
Event event = sourceService.addEvent(TEST_EVENT_NAME, /* eventDescription */null, eventStartDate, eventEndDate,
|
||||
"testVenue", false, eventUUID);
|
||||
UUID courseAreaUUID = UUID.randomUUID();
|
||||
@@ -1765,28 +1798,24 @@ public class MasterDataImportTest {
|
||||
|
||||
RacingEventService destService;
|
||||
DomainFactory domainFactory;
|
||||
MasterDataResource resource = new MasterDataResource();
|
||||
MasterDataResource spyResource = spyResource(resource, sourceService);
|
||||
Response response = spyResource.getMasterDataByLeaderboardGroups(groupNamesToExport, false, true, false);
|
||||
Response response = masterDataResource.getMasterDataByLeaderboardGroups(groupNamesToExport, false, true, false);
|
||||
StreamingOutput streamingOutput = (StreamingOutput) response.getEntity();
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
UUID randomUUID = UUID.randomUUID();
|
||||
ByteArrayInputStream inputStream = null;
|
||||
try {
|
||||
streamingOutput.write(os);
|
||||
os.flush();
|
||||
// Delete all data above from the database, to allow recreating all of it on target server
|
||||
deleteAllDataFromDatabase();
|
||||
// Import in new service
|
||||
destService = new RacingEventServiceImplMock(new DataImportProgressImpl(randomUUID)){};
|
||||
destService = getDestService(randomUUID);
|
||||
domainFactory = destService.getBaseDomainFactory();
|
||||
inputStream = new ByteArrayInputStream(os.toByteArray());
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(os.toByteArray());
|
||||
|
||||
MasterDataImporter importer = new MasterDataImporter(domainFactory, destService, securityService);
|
||||
importer.importFromStream(inputStream, randomUUID, false);
|
||||
} finally {
|
||||
os.close();
|
||||
inputStream.close();
|
||||
}
|
||||
|
||||
MasterDataImportObjectCreationCount creationCount = destService.getDataImportLock().getProgress(randomUUID)
|
||||
@@ -1862,10 +1891,10 @@ public class MasterDataImportTest {
|
||||
// Serialize
|
||||
List<String> groupNamesToExport = Collections.singletonList(leaderboardGroup.getName());
|
||||
|
||||
RacingEventService destService;
|
||||
RacingEventServiceImplMock destService;
|
||||
DomainFactory domainFactory;
|
||||
MasterDataResource resource = new MasterDataResource();
|
||||
MasterDataResource spyResource = spyResource(resource, sourceService);
|
||||
DummyMasterDataRessource spyResource = spyResource(new DummyMasterDataRessource(), sourceService);
|
||||
Mockito.doReturn(securityService).when(spyResource).getSecurityService();
|
||||
Response response = spyResource.getMasterDataByLeaderboardGroups(groupNamesToExport, false, true, false);
|
||||
StreamingOutput streamingOutput = (StreamingOutput) response.getEntity();
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
@@ -1877,7 +1906,9 @@ public class MasterDataImportTest {
|
||||
// Delete all data above from the database, to allow recreating all of it on target server
|
||||
deleteAllDataFromDatabase();
|
||||
// Import in new service
|
||||
destService = new RacingEventServiceImplMock(new DataImportProgressImpl(randomUUID)){};
|
||||
destService = Mockito.spy(new RacingEventServiceImplMock(new DataImportProgressImpl(randomUUID)) {
|
||||
});
|
||||
Mockito.doReturn(securityService).when(destService).getSecurityService();
|
||||
domainFactory = destService.getBaseDomainFactory();
|
||||
inputStream = new ByteArrayInputStream(os.toByteArray());
|
||||
|
||||
@@ -1911,7 +1942,6 @@ public class MasterDataImportTest {
|
||||
public void testMasterDataImportWithTwoLgsWithSameLeaderboard() throws MalformedURLException, IOException,
|
||||
InterruptedException, ClassNotFoundException {
|
||||
// Setup source service
|
||||
RacingEventService sourceService = new RacingEventServiceImpl();
|
||||
Event event = sourceService.addEvent(TEST_EVENT_NAME, /* eventDescription */null, eventStartDate, eventEndDate,
|
||||
"testVenue", false, eventUUID);
|
||||
UUID courseAreaUUID = UUID.randomUUID();
|
||||
@@ -1990,9 +2020,7 @@ public class MasterDataImportTest {
|
||||
|
||||
RacingEventService destService;
|
||||
DomainFactory domainFactory;
|
||||
MasterDataResource resource = new MasterDataResource();
|
||||
MasterDataResource spyResource = spyResource(resource, sourceService);
|
||||
Response response = spyResource.getMasterDataByLeaderboardGroups(groupNamesToExport, false, true, false);
|
||||
Response response = masterDataResource.getMasterDataByLeaderboardGroups(groupNamesToExport, false, true, false);
|
||||
StreamingOutput streamingOutput = (StreamingOutput) response.getEntity();
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
UUID randomUUID = UUID.randomUUID();
|
||||
@@ -2003,7 +2031,7 @@ public class MasterDataImportTest {
|
||||
// Delete all data above from the database, to allow recreating all of it on target server
|
||||
deleteAllDataFromDatabase();
|
||||
// Import in new service
|
||||
destService = new RacingEventServiceImplMock(new DataImportProgressImpl(randomUUID)){};
|
||||
destService = getDestService(randomUUID);
|
||||
domainFactory = destService.getBaseDomainFactory();
|
||||
inputStream = new ByteArrayInputStream(os.toByteArray());
|
||||
|
||||
@@ -2038,9 +2066,6 @@ public class MasterDataImportTest {
|
||||
@Test
|
||||
public void testMasterDataImportWithOverallLeaderboard() throws MalformedURLException, IOException,
|
||||
InterruptedException, ClassNotFoundException {
|
||||
// Setup source service
|
||||
RacingEventService sourceService = new RacingEventServiceImpl();
|
||||
|
||||
int[] discardRule = { 1, 2, 3, 4 };
|
||||
ScoringScheme scheme = new LowPoint();
|
||||
List<String> leaderboardNames = new ArrayList<String>();
|
||||
@@ -2062,28 +2087,24 @@ public class MasterDataImportTest {
|
||||
|
||||
RacingEventService destService;
|
||||
DomainFactory domainFactory;
|
||||
MasterDataResource resource = new MasterDataResource();
|
||||
MasterDataResource spyResource = spyResource(resource, sourceService);
|
||||
Response response = spyResource.getMasterDataByLeaderboardGroups(groupNamesToExport, false, true, false);
|
||||
Response response = masterDataResource.getMasterDataByLeaderboardGroups(groupNamesToExport, false, true, false);
|
||||
StreamingOutput streamingOutput = (StreamingOutput) response.getEntity();
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
UUID randomUUID = UUID.randomUUID();
|
||||
ByteArrayInputStream inputStream = null;
|
||||
try {
|
||||
streamingOutput.write(os);
|
||||
os.flush();
|
||||
// Delete all data above from the database, to allow recreating all of it on target server
|
||||
deleteAllDataFromDatabase();
|
||||
// Import in new service
|
||||
destService = new RacingEventServiceImplMock(new DataImportProgressImpl(randomUUID)){};
|
||||
destService = getDestService(randomUUID);
|
||||
domainFactory = destService.getBaseDomainFactory();
|
||||
inputStream = new ByteArrayInputStream(os.toByteArray());
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(os.toByteArray());
|
||||
|
||||
MasterDataImporter importer = new MasterDataImporter(domainFactory, destService, securityService);
|
||||
importer.importFromStream(inputStream, randomUUID, false);
|
||||
} finally {
|
||||
os.close();
|
||||
inputStream.close();
|
||||
}
|
||||
|
||||
MasterDataImportObjectCreationCount creationCount = destService.getDataImportLock().getProgress(randomUUID)
|
||||
@@ -2142,8 +2163,6 @@ public class MasterDataImportTest {
|
||||
@Test
|
||||
public void testMasterDataImportWithFlexibleLeaderboard() throws MalformedURLException, IOException,
|
||||
InterruptedException, ClassNotFoundException {
|
||||
// Setup source service
|
||||
RacingEventService sourceService = new RacingEventServiceImpl();
|
||||
UUID courseAreaUUID = UUID.randomUUID();
|
||||
|
||||
List<String> raceColumnNames = new ArrayList<String>();
|
||||
@@ -2250,9 +2269,7 @@ public class MasterDataImportTest {
|
||||
|
||||
RacingEventService destService;
|
||||
DomainFactory domainFactory;
|
||||
MasterDataResource resource = new MasterDataResource();
|
||||
MasterDataResource spyResource = spyResource(resource, sourceService);
|
||||
Response response = spyResource.getMasterDataByLeaderboardGroups(groupNamesToExport, false, true, false);
|
||||
Response response = masterDataResource.getMasterDataByLeaderboardGroups(groupNamesToExport, false, true, false);
|
||||
StreamingOutput streamingOutput = (StreamingOutput) response.getEntity();
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
UUID randomUUID = UUID.randomUUID();
|
||||
@@ -2263,7 +2280,7 @@ public class MasterDataImportTest {
|
||||
// Delete all data above from the database, to allow recreating all of it on target server
|
||||
deleteAllDataFromDatabase();
|
||||
// Import in new service
|
||||
destService = new RacingEventServiceImplMock(new DataImportProgressImpl(randomUUID)){};
|
||||
destService = getDestService(randomUUID);
|
||||
domainFactory = destService.getBaseDomainFactory();
|
||||
inputStream = new ByteArrayInputStream(os.toByteArray());
|
||||
|
||||
|
||||
+32
-1
@@ -17,8 +17,12 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.apache.shiro.mgt.SecurityManager;
|
||||
import org.apache.shiro.subject.Subject;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import com.sap.sailing.domain.base.CompetitorWithBoat;
|
||||
import com.sap.sailing.domain.base.DomainFactory;
|
||||
@@ -71,12 +75,17 @@ import com.sap.sailing.server.operationaltransformation.RemoveEvent;
|
||||
import com.sap.sailing.server.operationaltransformation.RemoveLeaderboard;
|
||||
import com.sap.sailing.server.operationaltransformation.RemoveLeaderboardGroup;
|
||||
import com.sap.sailing.server.operationaltransformation.RemoveRegatta;
|
||||
import com.sap.sailing.server.tagging.TaggingServiceImpl;
|
||||
import com.sap.sse.common.Color;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
import com.sap.sse.common.search.KeywordQuery;
|
||||
import com.sap.sse.common.search.Result;
|
||||
import com.sap.sse.security.SecurityService;
|
||||
import com.sap.sse.security.shared.User;
|
||||
import com.sap.sse.security.shared.WithQualifiedObjectIdentifier;
|
||||
import com.sap.sse.security.shared.impl.UserGroupImpl;
|
||||
import com.sap.sse.shared.media.ImageDescriptor;
|
||||
import com.sap.sse.shared.media.VideoDescriptor;
|
||||
|
||||
@@ -101,10 +110,32 @@ public class SearchServiceTest {
|
||||
private DynamicTrackedRace pfingstbusch470TrackedR2;
|
||||
private DynamicTrackedRace aalOrcTrackedR1;
|
||||
private DynamicTrackedRace aalOrcTrackedR2;
|
||||
private SecurityService securityService;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
server = new RacingEventServiceImpl();
|
||||
UserGroupImpl defaultTenant = new UserGroupImpl(new UUID(0, 1), "defaultTenant");
|
||||
User currentUser = Mockito.mock(User.class);
|
||||
|
||||
securityService = Mockito.mock(SecurityService.class);
|
||||
SecurityManager securityManager = Mockito.mock(org.apache.shiro.mgt.SecurityManager.class);
|
||||
Subject fakeSubject = Mockito.mock(Subject.class);
|
||||
|
||||
SecurityUtils.setSecurityManager(securityManager);
|
||||
Mockito.doReturn(fakeSubject).when(securityManager).createSubject(Mockito.any());
|
||||
Mockito.doReturn(defaultTenant).when(securityService).getDefaultTenant();
|
||||
Mockito.doReturn(currentUser).when(securityService).getCurrentUser();
|
||||
Mockito.doReturn(true).when(securityService).hasCurrentUserReadPermission(Mockito.any());
|
||||
Mockito.doNothing().when(securityService).checkCurrentUserReadPermission(Mockito.any());
|
||||
|
||||
Mockito.doReturn(true).when(securityService)
|
||||
.hasCurrentUserReadPermission(Mockito.any(WithQualifiedObjectIdentifier.class));
|
||||
Mockito.doReturn(true).when(fakeSubject).isAuthenticated();
|
||||
|
||||
server = Mockito.spy(new RacingEventServiceImpl());
|
||||
Mockito.doReturn(securityService).when(server).getSecurityService();
|
||||
TaggingServiceImpl taggingServer = Mockito.spy(new TaggingServiceImpl(server));
|
||||
Mockito.doReturn(taggingServer).when(server).getTaggingService();
|
||||
List<Event> allEvents = new ArrayList<>();
|
||||
Util.addAll(server.getAllEvents(), allEvents);
|
||||
for (final Event e : allEvents) {
|
||||
|
||||
+10
-9
@@ -7,6 +7,7 @@ import static org.junit.Assert.fail;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
@@ -17,6 +18,7 @@ import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import com.sap.sailing.domain.abstractlog.race.RaceLog;
|
||||
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
|
||||
@@ -79,7 +81,7 @@ public class TaggingServiceTest {
|
||||
throws MalformedURLException, IOException, InterruptedException, UserManagementException, MailException, UserGroupManagementException {
|
||||
MongoDBService.INSTANCE.getDB().dropDatabase();
|
||||
// setup racing service and racelog
|
||||
racingService = new RacingEventServiceImpl();
|
||||
racingService = Mockito.spy(new RacingEventServiceImpl());
|
||||
RacingEventServiceOperation<FlexibleLeaderboard> addLeaderboardOp = new CreateFlexibleLeaderboard(
|
||||
leaderboardName, leaderboardName, new int[] { 5 }, new LowPoint(), null);
|
||||
racingService.apply(addLeaderboardOp);
|
||||
@@ -89,11 +91,15 @@ public class TaggingServiceTest {
|
||||
// setup security service
|
||||
securityService = new SecurityBundleTestWrapper().initializeSecurityServiceForTesting();
|
||||
// create & login user
|
||||
securityService.createSimpleUser(username, email, password, fullName, company, null);
|
||||
securityService.createSimpleUser(username, email, password, fullName, company, Locale.ENGLISH, null);
|
||||
|
||||
subject = SecurityUtils.getSubject();
|
||||
subject.login(new UsernamePasswordToken(username, password));
|
||||
// setup tagging service
|
||||
taggingService = new TaggingServiceImpl(racingService);
|
||||
taggingService = Mockito.spy(new TaggingServiceImpl(racingService));
|
||||
|
||||
Mockito.doReturn(securityService).when(racingService).getSecurityService();
|
||||
Mockito.doReturn(taggingService).when(racingService).getTaggingService();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
@@ -213,13 +219,12 @@ public class TaggingServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTags() {
|
||||
public void testGetTags() throws Exception {
|
||||
logger.entering(getClass().getName(), "testGetTags");
|
||||
final String tag = "TagToLoad";
|
||||
final String comment = "Comment To Load";
|
||||
final String imageURL = "localhost";
|
||||
final TimePoint raceTimepoint = new MillisecondsTimePoint(1000);
|
||||
try {
|
||||
logger.info("Adding tags which should be loaded via getTags() afterwards.");
|
||||
taggingService.addTag(leaderboardName, raceColumnName, fleetName, tag, comment, imageURL, imageURL, false,
|
||||
raceTimepoint);
|
||||
@@ -236,10 +241,6 @@ public class TaggingServiceTest {
|
||||
assertEquals("Public tags do not contain added tag with non-matching creation date filter", 0,
|
||||
taggingService.getPublicTags(leaderboardName, raceColumnName, fleetName,
|
||||
MillisecondsTimePoint.now(), false).size());
|
||||
} catch (Exception e) {
|
||||
fail("Caught unexpected exception while loading tags which were previously added! " + e.getClass().getName()
|
||||
+ ", message: " + e.getMessage());
|
||||
}
|
||||
logger.exiting(getClass().getName(), "testGetTags");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.sap.sailing.server.impl;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.net.MalformedURLException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Dictionary;
|
||||
import java.util.HashSet;
|
||||
@@ -9,7 +10,11 @@ import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.management.InstanceAlreadyExistsException;
|
||||
import javax.management.MBeanRegistrationException;
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.MalformedObjectNameException;
|
||||
import javax.management.NotCompliantMBeanException;
|
||||
import javax.management.ObjectName;
|
||||
|
||||
import org.osgi.framework.BundleActivator;
|
||||
@@ -43,6 +48,7 @@ import com.sap.sailing.server.impl.preferences.model.CompetitorNotificationPrefe
|
||||
import com.sap.sailing.server.impl.preferences.model.SailorProfilePreferences;
|
||||
import com.sap.sailing.server.impl.preferences.model.StoredDataMiningQueryPreferences;
|
||||
import com.sap.sailing.server.notification.impl.SailingNotificationServiceImpl;
|
||||
import com.sap.sailing.server.security.SailingViewerRole;
|
||||
import com.sap.sailing.server.statistics.TrackedRaceStatisticsCache;
|
||||
import com.sap.sailing.server.statistics.TrackedRaceStatisticsCacheImpl;
|
||||
import com.sap.sse.MasterDataImportClassLoaderService;
|
||||
@@ -54,6 +60,7 @@ import com.sap.sse.mail.queue.impl.ExecutorMailQueue;
|
||||
import com.sap.sse.osgi.CachedOsgiTypeBasedServiceFinderFactory;
|
||||
import com.sap.sse.replication.Replicable;
|
||||
import com.sap.sse.security.PreferenceConverter;
|
||||
import com.sap.sse.security.RolePrototypeProvider;
|
||||
import com.sap.sse.security.SecurityService;
|
||||
import com.sap.sse.security.shared.HasPermissionsProvider;
|
||||
import com.sap.sse.util.ClearStateTestSupport;
|
||||
@@ -115,77 +122,23 @@ public class Activator implements BundleActivator {
|
||||
|
||||
mailServiceTracker = ServiceTrackerFactory.createAndOpen(context, MailService.class);
|
||||
securityServiceTracker = ServiceTrackerFactory.createAndOpen(context, SecurityService.class);
|
||||
mailQueue = new ExecutorMailQueue(mailServiceTracker);
|
||||
notificationService = new SailingNotificationServiceImpl(context, mailQueue);
|
||||
|
||||
trackedRegattaListener = new OSGiBasedTrackedRegattaListener(context);
|
||||
if (securityServiceTracker != null) {
|
||||
new Thread("Racingevent wait for securityservice for migration thread") {
|
||||
public void run() {
|
||||
try {
|
||||
// only continue once we have the service, as some of the services require it to start properly
|
||||
securityServiceTracker.waitForService(0);
|
||||
internalStartBundle(context);
|
||||
} catch (InterruptedException | MalformedObjectNameException | InstanceAlreadyExistsException
|
||||
| MBeanRegistrationException | NotCompliantMBeanException | MalformedURLException e) {
|
||||
logger.log(Level.SEVERE, "Could not start RacingEvent service properly", e);
|
||||
}
|
||||
};
|
||||
}.start();
|
||||
}
|
||||
|
||||
registrations.add(context.registerService(HasPermissionsProvider.class.getName(),
|
||||
(HasPermissionsProvider) SecuredDomainType::getAllInstances, null));
|
||||
|
||||
final TrackedRaceStatisticsCache trackedRaceStatisticsCache = new TrackedRaceStatisticsCacheImpl();
|
||||
registrations.add(context.registerService(TrackedRaceStatisticsCache.class.getName(), trackedRaceStatisticsCache, null));
|
||||
registrations.add(context.registerService(TrackedRegattaListener.class.getName(), trackedRaceStatisticsCache, null));
|
||||
|
||||
// At this point the OSGi resolver is used as device type service finder.
|
||||
// In the case that we are not in an OSGi context (e.g. running a JUnit test instead),
|
||||
// this code block is not run, and the test case can inject some other type of finder
|
||||
// instead.
|
||||
serviceFinderFactory = new CachedOsgiTypeBasedServiceFinderFactory(context);
|
||||
|
||||
|
||||
racingEventService = new RacingEventServiceImpl(clearPersistentCompetitors, serviceFinderFactory,
|
||||
trackedRegattaListener, notificationService, trackedRaceStatisticsCache, restoreTrackedRaces,
|
||||
securityServiceTracker);
|
||||
notificationService.setRacingEventService(racingEventService);
|
||||
|
||||
masterDataImportClassLoaderServiceTracker = new ServiceTracker<MasterDataImportClassLoaderService, MasterDataImportClassLoaderService>(
|
||||
context, MasterDataImportClassLoaderService.class,
|
||||
new MasterDataImportClassLoaderServiceTrackerCustomizer(context, racingEventService));
|
||||
masterDataImportClassLoaderServiceTracker.open();
|
||||
|
||||
polarDataServiceTracker = new ServiceTracker<PolarDataService, PolarDataService>(context,
|
||||
PolarDataService.class, new PolarDataServiceTrackerCustomizer(context, racingEventService));
|
||||
polarDataServiceTracker.open();
|
||||
|
||||
// register the racing service in the OSGi registry
|
||||
racingEventService.setBundleContext(context);
|
||||
context.registerService(MongoObjectFactory.class, racingEventService.getMongoObjectFactory(), /* properties */ null);
|
||||
context.registerService(DomainObjectFactory.class, racingEventService.getDomainObjectFactory(), /* properties */ null);
|
||||
final Dictionary<String, String> replicableServiceProperties = new Hashtable<>();
|
||||
replicableServiceProperties.put(Replicable.OSGi_Service_Registry_ID_Property_Name, racingEventService.getId().toString());
|
||||
context.registerService(Replicable.class, racingEventService, replicableServiceProperties);
|
||||
context.registerService(RacingEventService.class, racingEventService, null);
|
||||
context.registerService(RaceLogResolver.class, racingEventService, null);
|
||||
context.registerService(ClearStateTestSupport.class, racingEventService, null);
|
||||
context.registerService(SensorFixStoreSupplier.class, racingEventService, null);
|
||||
context.registerService(WindFinderReviewedSpotsCollectionIdProvider.class, racingEventService, null);
|
||||
Dictionary<String, String> properties = new Hashtable<String, String>();
|
||||
final GPSFixMongoHandlerImpl gpsFixMongoHandler = new GPSFixMongoHandlerImpl(
|
||||
racingEventService.getMongoObjectFactory(), racingEventService.getDomainObjectFactory());
|
||||
properties.put(TypeBasedServiceFinder.TYPE, GPSFixImpl.class.getName());
|
||||
registrations.add(context.registerService(FixMongoHandler.class, gpsFixMongoHandler, properties));
|
||||
// legacy type name; some DBs may still contain fixes marked with this old package name:
|
||||
properties.put(TypeBasedServiceFinder.TYPE, "com.sap.sailing.domain.tracking.impl.GPSFixImpl");
|
||||
registrations.add(context.registerService(FixMongoHandler.class, gpsFixMongoHandler, properties));
|
||||
final GPSFixMovingMongoHandlerImpl gpsFixMovingMongoHandler = new GPSFixMovingMongoHandlerImpl(
|
||||
racingEventService.getMongoObjectFactory(), racingEventService.getDomainObjectFactory());
|
||||
properties.put(TypeBasedServiceFinder.TYPE, GPSFixMovingImpl.class.getName());
|
||||
registrations.add(context.registerService(FixMongoHandler.class, gpsFixMovingMongoHandler, properties));
|
||||
// legacy type name; some DBs may still contain fixes marked with this old package name:
|
||||
properties.put(TypeBasedServiceFinder.TYPE, "com.sap.sailing.domain.tracking.impl.GPSFixMovingImpl");
|
||||
registrations.add(context.registerService(FixMongoHandler.class, gpsFixMovingMongoHandler, properties));
|
||||
properties.put(TypeBasedServiceFinder.TYPE, DoubleVectorFixImpl.class.getName());
|
||||
registrations.add(context.registerService(FixMongoHandler.class, new DoubleVectorFixMongoHandlerImpl(
|
||||
racingEventService.getMongoObjectFactory(), racingEventService.getDomainObjectFactory()), properties));
|
||||
registerPreferenceConvertersForUserStore(context);
|
||||
// Add an MBean for the service to the JMX bean server:
|
||||
RacingEventServiceMXBean mbean = new RacingEventServiceMXBeanImpl(racingEventService);
|
||||
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
|
||||
mBeanName = new ObjectName("com.sap.sailing:type=RacingEventService");
|
||||
mbs.registerMBean(mbean, mBeanName);
|
||||
logger.log(Level.INFO, "Started " + context.getBundle().getSymbolicName() + ". Character encoding: "
|
||||
+ Charset.defaultCharset());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -243,6 +196,103 @@ public class Activator implements BundleActivator {
|
||||
mbs.unregisterMBean(mBeanName);
|
||||
}
|
||||
|
||||
private void internalStartBundle(BundleContext context) throws MalformedURLException, MalformedObjectNameException,
|
||||
InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException {
|
||||
mailQueue = new ExecutorMailQueue(mailServiceTracker);
|
||||
notificationService = new SailingNotificationServiceImpl(context, mailQueue);
|
||||
|
||||
trackedRegattaListener = new OSGiBasedTrackedRegattaListener(context);
|
||||
|
||||
registrations.add(context.registerService(HasPermissionsProvider.class,
|
||||
(HasPermissionsProvider) SecuredDomainType::getAllInstances, null));
|
||||
|
||||
registrations.add(context.registerService(RolePrototypeProvider.class,
|
||||
(RolePrototypeProvider) SailingViewerRole::getInstance, null));
|
||||
|
||||
final TrackedRaceStatisticsCache trackedRaceStatisticsCache = new TrackedRaceStatisticsCacheImpl();
|
||||
registrations.add(context.registerService(TrackedRaceStatisticsCache.class.getName(),
|
||||
trackedRaceStatisticsCache, null));
|
||||
registrations.add(context.registerService(TrackedRegattaListener.class.getName(),
|
||||
trackedRaceStatisticsCache, null));
|
||||
|
||||
// At this point the OSGi resolver is used as device type service finder.
|
||||
// In the case that we are not in an OSGi context (e.g. running a JUnit test instead),
|
||||
// this code block is not run, and the test case can inject some other type of finder
|
||||
// instead.
|
||||
serviceFinderFactory = new CachedOsgiTypeBasedServiceFinderFactory(context);
|
||||
|
||||
racingEventService = new RacingEventServiceImpl(clearPersistentCompetitors,
|
||||
serviceFinderFactory, trackedRegattaListener, notificationService,
|
||||
trackedRaceStatisticsCache, restoreTrackedRaces, securityServiceTracker);
|
||||
notificationService.setRacingEventService(racingEventService);
|
||||
|
||||
masterDataImportClassLoaderServiceTracker = new ServiceTracker<MasterDataImportClassLoaderService, MasterDataImportClassLoaderService>(
|
||||
context, MasterDataImportClassLoaderService.class,
|
||||
new MasterDataImportClassLoaderServiceTrackerCustomizer(context, racingEventService));
|
||||
masterDataImportClassLoaderServiceTracker.open();
|
||||
|
||||
polarDataServiceTracker = new ServiceTracker<PolarDataService, PolarDataService>(context,
|
||||
PolarDataService.class,
|
||||
new PolarDataServiceTrackerCustomizer(context, racingEventService));
|
||||
polarDataServiceTracker.open();
|
||||
|
||||
// register the racing service in the OSGi registry
|
||||
racingEventService.setBundleContext(context);
|
||||
context.registerService(MongoObjectFactory.class, racingEventService.getMongoObjectFactory(),
|
||||
/* properties */ null);
|
||||
context.registerService(DomainObjectFactory.class, racingEventService.getDomainObjectFactory(),
|
||||
/* properties */ null);
|
||||
final Dictionary<String, String> replicableServiceProperties = new Hashtable<>();
|
||||
replicableServiceProperties.put(Replicable.OSGi_Service_Registry_ID_Property_Name,
|
||||
racingEventService.getId().toString());
|
||||
context.registerService(Replicable.class, racingEventService, replicableServiceProperties);
|
||||
context.registerService(RacingEventService.class, racingEventService, null);
|
||||
context.registerService(RaceLogResolver.class, racingEventService, null);
|
||||
context.registerService(ClearStateTestSupport.class, racingEventService, null);
|
||||
context.registerService(SensorFixStoreSupplier.class, racingEventService, null);
|
||||
context.registerService(WindFinderReviewedSpotsCollectionIdProvider.class, racingEventService,
|
||||
null);
|
||||
Dictionary<String, String> properties = new Hashtable<String, String>();
|
||||
final GPSFixMongoHandlerImpl gpsFixMongoHandler = new GPSFixMongoHandlerImpl(
|
||||
racingEventService.getMongoObjectFactory(),
|
||||
racingEventService.getDomainObjectFactory());
|
||||
properties.put(TypeBasedServiceFinder.TYPE, GPSFixImpl.class.getName());
|
||||
registrations
|
||||
.add(context.registerService(FixMongoHandler.class, gpsFixMongoHandler, properties));
|
||||
// legacy type name; some DBs may still contain fixes marked with this old package name:
|
||||
properties.put(TypeBasedServiceFinder.TYPE, "com.sap.sailing.domain.tracking.impl.GPSFixImpl");
|
||||
registrations
|
||||
.add(context.registerService(FixMongoHandler.class, gpsFixMongoHandler, properties));
|
||||
final GPSFixMovingMongoHandlerImpl gpsFixMovingMongoHandler = new GPSFixMovingMongoHandlerImpl(
|
||||
racingEventService.getMongoObjectFactory(),
|
||||
racingEventService.getDomainObjectFactory());
|
||||
properties.put(TypeBasedServiceFinder.TYPE, GPSFixMovingImpl.class.getName());
|
||||
registrations.add(
|
||||
context.registerService(FixMongoHandler.class, gpsFixMovingMongoHandler, properties));
|
||||
// legacy type name; some DBs may still contain fixes marked with this old package name:
|
||||
properties.put(TypeBasedServiceFinder.TYPE,
|
||||
"com.sap.sailing.domain.tracking.impl.GPSFixMovingImpl");
|
||||
registrations.add(
|
||||
context.registerService(FixMongoHandler.class, gpsFixMovingMongoHandler, properties));
|
||||
properties.put(TypeBasedServiceFinder.TYPE, DoubleVectorFixImpl.class.getName());
|
||||
registrations.add(context.registerService(FixMongoHandler.class,
|
||||
new DoubleVectorFixMongoHandlerImpl(racingEventService.getMongoObjectFactory(),
|
||||
racingEventService.getDomainObjectFactory()),
|
||||
properties));
|
||||
registerPreferenceConvertersForUserStore(context);
|
||||
// Add an MBean for the service to the JMX bean server:
|
||||
RacingEventServiceMXBean mbean = new RacingEventServiceMXBeanImpl(racingEventService);
|
||||
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
|
||||
mBeanName = new ObjectName("com.sap.sailing:type=RacingEventService");
|
||||
mbs.registerMBean(mbean, mBeanName);
|
||||
logger.log(Level.INFO, "Started " + context.getBundle().getSymbolicName()
|
||||
+ ". Character encoding: " + Charset.defaultCharset());
|
||||
|
||||
// do initial setup/migration logic
|
||||
racingEventService.ensureOwnerships();
|
||||
racingEventService.ensureServerIsInitiallyPublic();
|
||||
}
|
||||
|
||||
private class MasterDataImportClassLoaderServiceTrackerCustomizer implements
|
||||
ServiceTrackerCustomizer<MasterDataImportClassLoaderService, MasterDataImportClassLoaderService> {
|
||||
|
||||
|
||||
+24
-14
@@ -254,6 +254,7 @@ import com.sap.sailing.server.operationaltransformation.UpdateStartTimeReceived;
|
||||
import com.sap.sailing.server.operationaltransformation.UpdateTrackedRaceStatus;
|
||||
import com.sap.sailing.server.operationaltransformation.UpdateWindAveragingTime;
|
||||
import com.sap.sailing.server.operationaltransformation.UpdateWindSourcesToExclude;
|
||||
import com.sap.sailing.server.security.SailingViewerRole;
|
||||
import com.sap.sailing.server.simulation.SimulationService;
|
||||
import com.sap.sailing.server.simulation.SimulationServiceFactory;
|
||||
import com.sap.sailing.server.statistics.StatisticsAggregator;
|
||||
@@ -289,6 +290,10 @@ import com.sap.sse.replication.OperationWithResult;
|
||||
import com.sap.sse.replication.ReplicationMasterDescriptor;
|
||||
import com.sap.sse.replication.impl.OperationWithResultWithIdWrapper;
|
||||
import com.sap.sse.security.SecurityService;
|
||||
import com.sap.sse.security.shared.RoleDefinition;
|
||||
import com.sap.sse.security.shared.RoleImpl;
|
||||
import com.sap.sse.security.shared.User;
|
||||
import com.sap.sse.security.shared.UserGroup;
|
||||
import com.sap.sse.shared.media.ImageDescriptor;
|
||||
import com.sap.sse.shared.media.VideoDescriptor;
|
||||
import com.sap.sse.util.ClearStateTestSupport;
|
||||
@@ -825,19 +830,23 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes
|
||||
new QuarterChecker(), new SameDigitChecker());
|
||||
raceChangeObserverForAnniversaryDetection = new RaceChangeObserverForAnniversaryDetection(anniversaryRaceDeterminator);
|
||||
this.trackedRegattaListener.addListener(raceChangeObserverForAnniversaryDetection);
|
||||
|
||||
if (securityServiceTracker != null) {
|
||||
new Thread("Racingevent wait for securityservice for migration thread") {
|
||||
public void run() {
|
||||
SecurityService securityService;
|
||||
try {
|
||||
securityService = securityServiceTracker.waitForService(0);
|
||||
ensureOwnerships(securityService);
|
||||
} catch (InterruptedException e) {
|
||||
logger.warning("Could not obtain SecurityService " + e.getMessage());
|
||||
}
|
||||
};
|
||||
}.start();
|
||||
}
|
||||
|
||||
public void ensureServerIsInitiallyPublic() {
|
||||
try {
|
||||
final User allUser = getSecurityService().getAllUser();
|
||||
String initializedKey = ("serverInitialized " + ServerInfo.getName()).replaceAll("[\\W]|_", "");
|
||||
if (!Boolean.TRUE.equals(getSecurityService().getSetting(initializedKey, Boolean.class))) {
|
||||
getSecurityService().addSetting(initializedKey, Boolean.class);
|
||||
final RoleDefinition viewerRole = getSecurityService()
|
||||
.getRoleDefinition(SailingViewerRole.getInstance().getId());
|
||||
final UserGroup defaultServerTenant = getSecurityService().getDefaultTenant();
|
||||
final RoleImpl publicAccessForServerRole = new RoleImpl(viewerRole, defaultServerTenant, null);
|
||||
getSecurityService().addRoleForUser(allUser.getName(), publicAccessForServerRole);
|
||||
getSecurityService().setSetting(initializedKey, true);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.WARNING, "Error determining Server initialisation state", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -934,7 +943,8 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes
|
||||
return domainObjectFactory;
|
||||
}
|
||||
|
||||
private void ensureOwnerships(SecurityService securityService) {
|
||||
public void ensureOwnerships() {
|
||||
SecurityService securityService = getSecurityService();
|
||||
securityService.assumeOwnershipMigrated(SecuredDomainType.MANAGE_MARK_PASSINGS.getName(),
|
||||
SecuredDomainType.getAllInstances());
|
||||
securityService.assumeOwnershipMigrated(SecuredDomainType.MANAGE_MARK_POSITIONS.getName(),
|
||||
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
package com.sap.sailing.server.security;
|
||||
|
||||
import com.sap.sse.ServerStartupConstants;
|
||||
import com.sap.sse.ServerInfo;
|
||||
import com.sap.sse.security.BearerTokenOrBasicOrFormAuthenticationFilter;
|
||||
|
||||
public class BearerTokenOrBasicOrFormAuthenticationFilterSAPSailing extends
|
||||
BearerTokenOrBasicOrFormAuthenticationFilter {
|
||||
public BearerTokenOrBasicOrFormAuthenticationFilterSAPSailing() {
|
||||
super("SAP Sailing Analytics ("+/* application name */ ServerStartupConstants.SERVER_NAME+")");
|
||||
super("SAP Sailing Analytics ("+/* application name */ ServerInfo.getName()+")");
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -3,14 +3,14 @@ package com.sap.sailing.server.security;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
|
||||
import com.sap.sse.ServerStartupConstants;
|
||||
import com.sap.sse.ServerInfo;
|
||||
import com.sap.sse.security.BearerTokenOrBasicOrFormAuthenticationFilter;
|
||||
|
||||
public class BearerTokenOrBasicOrFormOrAnonymousAuthenticationFilterSAPSailing
|
||||
extends BearerTokenOrBasicOrFormAuthenticationFilter {
|
||||
|
||||
public BearerTokenOrBasicOrFormOrAnonymousAuthenticationFilterSAPSailing() {
|
||||
super("SAP Sailing Analytics (" + /* application name */ ServerStartupConstants.SERVER_NAME + ")");
|
||||
super("SAP Sailing Analytics (" + /* application name */ ServerInfo.getName() + ")");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package com.sap.sailing.server.security;
|
||||
|
||||
import com.sap.sailing.domain.common.security.SecuredDomainType;
|
||||
import com.sap.sse.security.shared.HasPermissions.DefaultActions;
|
||||
import com.sap.sse.security.shared.RolePrototype;
|
||||
import com.sap.sse.security.shared.WildcardPermission;
|
||||
|
||||
/**
|
||||
* Specifies a role that when associated to a user gives read access to the sailing domain parts needed for the home
|
||||
* page and basic analytical frontends.
|
||||
*/
|
||||
public class SailingViewerRole extends RolePrototype {
|
||||
private static final long serialVersionUID = 3291793984984443193L;
|
||||
|
||||
private static final SailingViewerRole INSTANCE = new SailingViewerRole();
|
||||
|
||||
SailingViewerRole() {
|
||||
super("sailing_viewer", "c42948df-517b-45cb-9fa9-d1e79f18e115",
|
||||
WildcardPermission.builder().withTypes(SecuredDomainType.EVENT, SecuredDomainType.LEADERBOARD_GROUP,
|
||||
SecuredDomainType.LEADERBOARD, SecuredDomainType.REGATTA, SecuredDomainType.TRACKED_RACE)
|
||||
.withActions(DefaultActions.READ).build());
|
||||
}
|
||||
|
||||
public static SailingViewerRole getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
}
|
||||
+1
-13
@@ -5,7 +5,6 @@ import java.util.List;
|
||||
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.apache.shiro.authz.AuthorizationException;
|
||||
import org.osgi.util.tracker.ServiceTracker;
|
||||
|
||||
import com.sap.sailing.domain.abstractlog.race.RaceLog;
|
||||
import com.sap.sailing.domain.abstractlog.race.RaceLogTagEvent;
|
||||
@@ -23,23 +22,19 @@ import com.sap.sailing.domain.common.tagging.TagAlreadyExistsException;
|
||||
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.server.RacingEventService;
|
||||
import com.sap.sailing.server.impl.Activator;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
import com.sap.sse.security.SecurityService;
|
||||
import com.sap.sse.util.ServiceTrackerFactory;
|
||||
|
||||
public class TaggingServiceImpl implements TaggingService {
|
||||
|
||||
private final ServiceTracker<SecurityService, SecurityService> securityServiceTracker;
|
||||
private final RacingEventService racingService;
|
||||
private final TagDTODeSerializer serializer;
|
||||
|
||||
public TaggingServiceImpl(RacingEventService racingService) {
|
||||
this.racingService = racingService;
|
||||
serializer = new TagDTODeSerializer();
|
||||
securityServiceTracker = ServiceTrackerFactory.createAndOpen(Activator.getContext(), SecurityService.class);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -62,14 +57,7 @@ public class TaggingServiceImpl implements TaggingService {
|
||||
* @return instance of {@link SecurityService}
|
||||
*/
|
||||
private SecurityService getSecurityService() {
|
||||
SecurityService securityService = null;
|
||||
if (securityServiceTracker != null) {
|
||||
securityService = securityServiceTracker.getService();
|
||||
}
|
||||
if (securityService == null) {
|
||||
throw new RuntimeException("Security service not found!");
|
||||
}
|
||||
return securityService;
|
||||
return racingService.getSecurityService();
|
||||
}
|
||||
|
||||
private void addPublicTag(String leaderboardName, String raceColumnName, String fleetName, String tag,
|
||||
|
||||
@@ -821,6 +821,12 @@ public class Util {
|
||||
return toStringOrNull.toString();
|
||||
}
|
||||
|
||||
public static boolean equalStringsWithEmptyIsNull(String o1, String o2) {
|
||||
String effectiveO1 = o1 == null || o1.isEmpty() ? null : o1;
|
||||
String effectiveO2 = o2 == null || o2.isEmpty() ? null : o2;
|
||||
return equalsWithNull(effectiveO1, effectiveO2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pads a numerical value with '0' characters up to a number of digits left and right of the decimal point. If the
|
||||
* number of digits right of the decimal point is zero, no decimal point will be rendered in the result. The sum of
|
||||
|
||||
Executable → Regular
+3
-2
@@ -91,9 +91,10 @@ public interface HasPermissions {
|
||||
*
|
||||
*/
|
||||
public enum DefaultActions implements Action {
|
||||
CREATE, READ, UPDATE, DELETE, CHANGE_OWNERSHIP;
|
||||
CREATE, READ, UPDATE, DELETE, CHANGE_OWNERSHIP, CHANGE_ACL;
|
||||
|
||||
public static final Action[] MUTATION_ACTIONS = new Action[] {CREATE, UPDATE, DELETE, CHANGE_OWNERSHIP};
|
||||
public static final Action[] MUTATION_ACTIONS = new Action[] { CREATE, UPDATE, DELETE, CHANGE_OWNERSHIP,
|
||||
CHANGE_ACL };
|
||||
|
||||
public static final Action[] READ_AND_WRITE_ACTIONS = new Action[] { CREATE, READ, UPDATE, DELETE };
|
||||
/**
|
||||
|
||||
+6
-8
@@ -5,10 +5,9 @@ package com.sap.sse.security.shared;
|
||||
*/
|
||||
public class NamedSecuredObjectDTO extends NamedDTO implements SecuredObject {
|
||||
|
||||
private static final long serialVersionUID = 4448408121977185408L;
|
||||
private static final long serialVersionUID = 2642220699434177353L;
|
||||
|
||||
private AccessControlList accessControlList;
|
||||
private Ownership ownership;
|
||||
private SecurityInformationDTO securityInformation = new SecurityInformationDTO();
|
||||
|
||||
protected NamedSecuredObjectDTO() {
|
||||
}
|
||||
@@ -19,22 +18,21 @@ public class NamedSecuredObjectDTO extends NamedDTO implements SecuredObject {
|
||||
|
||||
@Override
|
||||
public final AccessControlList getAccessControlList() {
|
||||
return accessControlList;
|
||||
return securityInformation.getAccessControlList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Ownership getOwnership() {
|
||||
return ownership;
|
||||
return securityInformation.getOwnership();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setAccessControlList(final AccessControlList accessControlList) {
|
||||
this.accessControlList = accessControlList;
|
||||
this.securityInformation.setAccessControlList(accessControlList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setOwnership(final Ownership ownership) {
|
||||
this.ownership = ownership;
|
||||
this.securityInformation.setOwnership(ownership);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+3
-3
@@ -198,7 +198,7 @@ public class PermissionChecker {
|
||||
final boolean isTypePartWildcard;
|
||||
if (parts.size() >= 1) {
|
||||
typeParts = parts.get(0);
|
||||
isTypePartWildcard = typeParts.contains(WildcardPermission.WILDCARD_TOKEN);
|
||||
isTypePartWildcard = typeParts.isEmpty() || typeParts.contains(WildcardPermission.WILDCARD_TOKEN);
|
||||
} else {
|
||||
typeParts = null;
|
||||
isTypePartWildcard = true;
|
||||
@@ -208,7 +208,7 @@ public class PermissionChecker {
|
||||
final boolean isActionPartWildcard;
|
||||
if (parts.size() >= 2) {
|
||||
actionParts = parts.get(1);
|
||||
isActionPartWildcard = actionParts.contains(WildcardPermission.WILDCARD_TOKEN);
|
||||
isActionPartWildcard = actionParts.isEmpty() || actionParts.contains(WildcardPermission.WILDCARD_TOKEN);
|
||||
} else {
|
||||
actionParts = null;
|
||||
isActionPartWildcard = true;
|
||||
@@ -218,7 +218,7 @@ public class PermissionChecker {
|
||||
final boolean isIdPartWildcard;
|
||||
if (parts.size() >= 3) {
|
||||
idParts = parts.get(2);
|
||||
isIdPartWildcard = idParts.contains(WildcardPermission.WILDCARD_TOKEN);
|
||||
isIdPartWildcard = idParts.isEmpty() || idParts.contains(WildcardPermission.WILDCARD_TOKEN);
|
||||
} else {
|
||||
idParts = null;
|
||||
isIdPartWildcard = true;
|
||||
|
||||
@@ -44,4 +44,9 @@ public interface Role extends Named {
|
||||
* qualified by a user argument}.
|
||||
*/
|
||||
SecurityUser getQualifiedForUser();
|
||||
|
||||
/**
|
||||
* @return the {@link #getQualifiedForTenant()} and {@link #getQualificationAsOwnership()} as {@link Ownership}
|
||||
*/
|
||||
Ownership getQualificationAsOwnership();
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.sap.sse.security.shared;
|
||||
import java.util.Set;
|
||||
|
||||
import com.sap.sse.common.Util.Triple;
|
||||
import com.sap.sse.security.shared.impl.OwnershipImpl;
|
||||
|
||||
/**
|
||||
* For equality and hash code, the {@link RoleDefinition#getId() role definition ID}, the {@link Tenant#getId() tenant ID} of a
|
||||
@@ -117,4 +118,12 @@ public class RoleImpl implements Role {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Ownership getQualificationAsOwnership() {
|
||||
if (qualifiedForTenant != null || qualifiedForUser != null) {
|
||||
return new OwnershipImpl(qualifiedForUser, qualifiedForTenant);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package com.sap.sse.security.shared;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.sap.sse.security.shared.AccessControlList;
|
||||
import com.sap.sse.security.shared.Ownership;
|
||||
import com.sap.sse.security.shared.SecuredObject;
|
||||
|
||||
/**
|
||||
* Data transfer object wrapping security information such as {@link AccessControlList access control list} and
|
||||
* {@link Ownership ownership} of and {@link SecuredObject secured object}.
|
||||
*/
|
||||
public class SecurityInformationDTO implements SecuredObject, Serializable {
|
||||
|
||||
private static final long serialVersionUID = -292250850983164293L;
|
||||
|
||||
private AccessControlList accessControlList;
|
||||
private Ownership ownership;
|
||||
|
||||
@Override
|
||||
public final AccessControlList getAccessControlList() {
|
||||
return accessControlList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Ownership getOwnership() {
|
||||
return ownership;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setAccessControlList(final AccessControlList accessControlList) {
|
||||
this.accessControlList = accessControlList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setOwnership(final Ownership ownership) {
|
||||
this.ownership = ownership;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,12 +10,8 @@ public class UserRole extends RolePrototype {
|
||||
|
||||
UserRole() {
|
||||
super("user", "ad1d5148-b13d-4464-90c4-7c396e4d4e2e",
|
||||
new WildcardPermission(SecuredSecurityTypes.USER.getStringPermission(DefaultActions.UPDATE)),
|
||||
new WildcardPermission(SecuredSecurityTypes.USER.getStringPermission(DefaultActions.READ)),
|
||||
new WildcardPermission("*:" + DefaultActions.CREATE.name() + ":*"),
|
||||
new WildcardPermission("*:" + DefaultActions.READ.name() + ":*"),
|
||||
new WildcardPermission("*:" + DefaultActions.UPDATE.name() + ":*"),
|
||||
new WildcardPermission("*:" + DefaultActions.DELETE.name() + ":*"));
|
||||
new WildcardPermission(SecuredSecurityTypes.USER.getStringPermission(DefaultActions.READ, DefaultActions.UPDATE)),
|
||||
WildcardPermission.builder().withActions(DefaultActions.READ_AND_WRITE_ACTIONS).build());
|
||||
}
|
||||
|
||||
public static UserRole getInstance() {
|
||||
|
||||
+65
-8
@@ -21,10 +21,12 @@ package com.sap.sse.security.shared;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import com.sap.sse.security.shared.HasPermissions.Action;
|
||||
import com.sap.sse.security.shared.impl.QualifiedObjectIdentifierImpl;
|
||||
import com.sap.sse.security.shared.impl.WildcardPermissionEncoder;
|
||||
|
||||
@@ -126,26 +128,31 @@ public class WildcardPermission implements Serializable {
|
||||
WildcardPermission() {}
|
||||
|
||||
public WildcardPermission(String wildcardString) {
|
||||
setParts(wildcardString);
|
||||
this(createPartsFromString(wildcardString));
|
||||
}
|
||||
|
||||
protected void setParts(String wildcardString) {
|
||||
|
||||
private WildcardPermission(List<Set<String>> parts) {
|
||||
this.parts = parts;
|
||||
}
|
||||
|
||||
protected static List<Set<String>> createPartsFromString(String wildcardString) {
|
||||
if (wildcardString == null || wildcardString.trim().length() == 0) {
|
||||
throw new IllegalArgumentException("Wildcard string cannot be null or empty. Make sure permission strings are properly formatted.");
|
||||
}
|
||||
wildcardString = wildcardString.trim();
|
||||
List<String> parts = Arrays.asList(wildcardString.split(PART_DIVIDER_TOKEN));
|
||||
this.parts = new ArrayList<Set<String>>();
|
||||
for (String part : parts) {
|
||||
List<String> rawParts = Arrays.asList(wildcardString.split(PART_DIVIDER_TOKEN));
|
||||
List<Set<String>> parts = new ArrayList<>();
|
||||
for (String part : rawParts) {
|
||||
Set<String> subparts = new HashSet<>(Arrays.asList(part.split(SUBPART_DIVIDER_TOKEN)));
|
||||
if (subparts.isEmpty()) {
|
||||
throw new IllegalArgumentException("Wildcard string cannot contain parts with only dividers. Make sure permission strings are properly formatted.");
|
||||
}
|
||||
this.parts.add(subparts);
|
||||
parts.add(subparts);
|
||||
}
|
||||
if (this.parts.isEmpty()) {
|
||||
if (parts.isEmpty()) {
|
||||
throw new IllegalArgumentException("Wildcard string cannot contain only dividers. Make sure permission strings are properly formatted.");
|
||||
}
|
||||
return parts;
|
||||
}
|
||||
|
||||
/*--------------------------------------------
|
||||
@@ -251,5 +258,55 @@ public class WildcardPermission implements Serializable {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static WildcardPermissionBuilder builder() {
|
||||
return new WildcardPermissionBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder to create non-primitive {@link WildcardPermission} instances. E.g. it is possible to use multiple types
|
||||
* or wildcard for the type part.
|
||||
*/
|
||||
public static class WildcardPermissionBuilder {
|
||||
private Set<String> types = new HashSet<>();
|
||||
private Set<String> actions = new HashSet<>();
|
||||
private Set<String> ids = new HashSet<>();
|
||||
|
||||
public WildcardPermissionBuilder withTypes(HasPermissions... types) {
|
||||
for (HasPermissions hasPermissions : types) {
|
||||
this.types.add(hasPermissions.getName());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public WildcardPermissionBuilder withActions(Action... actions) {
|
||||
for (Action action : actions) {
|
||||
this.actions.add(action.name());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public WildcardPermissionBuilder withIds(String... ids) {
|
||||
this.ids.addAll(Arrays.asList(ids));
|
||||
return this;
|
||||
}
|
||||
|
||||
public WildcardPermission build() {
|
||||
final List<Set<String>> parts = new ArrayList<>(2);
|
||||
if (types.isEmpty()) {
|
||||
parts.add(new HashSet<>(Collections.singleton(WILDCARD_TOKEN)));
|
||||
} else {
|
||||
parts.add(types);
|
||||
}
|
||||
if (!actions.isEmpty()) {
|
||||
parts.add(actions);
|
||||
} else if (!ids.isEmpty()) {
|
||||
parts.add(new HashSet<>(Collections.singleton(WILDCARD_TOKEN)));
|
||||
}
|
||||
if (!ids.isEmpty()) {
|
||||
parts.add(ids);
|
||||
}
|
||||
return new WildcardPermission(parts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-7
@@ -5,7 +5,6 @@ import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import com.sap.sse.security.shared.HasPermissions;
|
||||
import com.sap.sse.security.shared.QualifiedObjectIdentifier;
|
||||
import com.sap.sse.security.shared.RoleDefinition;
|
||||
import com.sap.sse.security.shared.User;
|
||||
import com.sap.sse.security.shared.UserGroup;
|
||||
@@ -50,12 +49,6 @@ public class SecuredSecurityTypes extends HasPermissionsImpl {
|
||||
*/
|
||||
public static final HasPermissions USER_GROUP = new SecuredSecurityTypes("USER_GROUP");
|
||||
|
||||
/**
|
||||
* type-relative identifier is the string representation of the {@link QualifiedObjectIdentifier} of the object
|
||||
* that the access control list governs access to.
|
||||
*/
|
||||
public static final HasPermissions ACCESS_CONTROL_LIST = new SecuredSecurityTypes("ACCESS_CONTROL_LIST");
|
||||
|
||||
public static enum ServerActions implements Action {
|
||||
CONFIGURE_FILE_STORAGE,
|
||||
CONFIGURE_LOCAL_SERVER,
|
||||
|
||||
+3
-2
@@ -7,6 +7,7 @@ import static org.mockito.Mockito.mock;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
@@ -106,7 +107,7 @@ public class SecurityReplicationLeadingToEmailReplicationTest extends AbstractSe
|
||||
final String password = "BertMyFriend";
|
||||
final String validationBaseURL = null; //so that validation email is not sent
|
||||
masterSecurityService.createSimpleUser(username, email, password,
|
||||
/* fullName */ null, /* company */ null, validationBaseURL);
|
||||
/* fullName */ null, /* company */ null, Locale.ENGLISH, validationBaseURL);
|
||||
masterSecurityService.sendMail(username, "subject", "body");
|
||||
securitySetUp.getReplicaReplicator().waitUntilQueueIsEmpty();
|
||||
mailSetUp.getReplicaReplicator().waitUntilQueueIsEmpty();
|
||||
@@ -132,7 +133,7 @@ public class SecurityReplicationLeadingToEmailReplicationTest extends AbstractSe
|
||||
final String password = "BertMyFriend";
|
||||
final String validationBaseURL = null; //so that validation email is not sent
|
||||
replicaSecurityService.createSimpleUser(username, email, password,
|
||||
/* fullName */ null, /* company */ null, validationBaseURL);
|
||||
/* fullName */ null, /* company */ null, Locale.ENGLISH, validationBaseURL);
|
||||
replicaSecurityService.sendMail(username, "subject", "body");
|
||||
securitySetUp.getReplicaReplicator().waitUntilQueueIsEmpty();
|
||||
mailSetUp.getReplicaReplicator().waitUntilQueueIsEmpty();
|
||||
|
||||
+3
-1
@@ -4,6 +4,7 @@ import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -37,7 +38,8 @@ public class SecurityServiceInitialLoadTest extends AbstractServerWithSingleServ
|
||||
final AccessControlStore accessControlStore = new AccessControlStoreImpl(PersistenceFactory.INSTANCE.getDefaultDomainObjectFactory(),
|
||||
PersistenceFactory.INSTANCE.getDefaultMongoObjectFactory(), userStore);
|
||||
final SecurityServiceImpl newMaster = new SecurityServiceImpl(userStore, accessControlStore);
|
||||
newMaster.createSimpleUser(username, email, password, fullName, company, /* validationBaseURL */ null);
|
||||
newMaster.createSimpleUser(username, email, password, fullName, company,
|
||||
/* validationBaseURL */ Locale.ENGLISH, null);
|
||||
accessToken = newMaster.createAccessToken(username);
|
||||
return newMaster;
|
||||
}
|
||||
|
||||
+8
-4
@@ -7,6 +7,8 @@ import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sap.sse.common.mail.MailException;
|
||||
@@ -25,7 +27,8 @@ public class SimpleSecurityReplicationTest extends AbstractSecurityReplicationTe
|
||||
final String company = "Ernie's Company";
|
||||
final String validationBaseURL = "http://me.to.back.com";
|
||||
assertNull(master.getUserByName(username));
|
||||
UserImpl user = master.createSimpleUser(username, email, password, fullName, company, validationBaseURL);
|
||||
UserImpl user = master.createSimpleUser(username, email, password, fullName, company, Locale.ENGLISH,
|
||||
validationBaseURL);
|
||||
assertNotNull(user);
|
||||
assertSame(user, master.getUserByName(username));
|
||||
assertTrue(master.checkPassword(username, password));
|
||||
@@ -59,7 +62,8 @@ public class SimpleSecurityReplicationTest extends AbstractSecurityReplicationTe
|
||||
final String validationBaseURL = "http://me.to.back.com";
|
||||
final String fullName = "Ernie's Full Name";
|
||||
final String company = "Ernie's Company";
|
||||
UserImpl user = master.createSimpleUser(username, email, password, fullName, company, validationBaseURL);
|
||||
UserImpl user = master.createSimpleUser(username, email, password, fullName, company, Locale.ENGLISH,
|
||||
validationBaseURL);
|
||||
user.setFullName(fullName);
|
||||
user.setCompany(company);
|
||||
final String emailValidationSecretAfterCreation = user.getValidationSecret();
|
||||
@@ -88,7 +92,7 @@ public class SimpleSecurityReplicationTest extends AbstractSecurityReplicationTe
|
||||
final String newPassword = "ErnieAndBert";
|
||||
final String validationBaseURL = "http://me.to.back.com";
|
||||
master.createSimpleUser(username, email, password,
|
||||
/* fullName */ null, /* company */ null, validationBaseURL);
|
||||
/* fullName */ null, /* company */ null, Locale.ENGLISH, validationBaseURL);
|
||||
master.updateSimpleUserPassword(username, newPassword);
|
||||
assertTrue(master.checkPassword(username, newPassword));
|
||||
|
||||
@@ -110,7 +114,7 @@ public class SimpleSecurityReplicationTest extends AbstractSecurityReplicationTe
|
||||
final String validationBaseURL = "http://me.to.back.com/validateemail";
|
||||
final String passwordResetBaseURL = "http://me.to.back.com/passwordreset";
|
||||
UserImpl user = master.createSimpleUser(username, email, password,
|
||||
/* fullName */ null, /* company */ null, validationBaseURL);
|
||||
/* fullName */ null, /* company */ null, Locale.ENGLISH, validationBaseURL);
|
||||
master.validateEmail(username, user.getValidationSecret());
|
||||
assertTrue(user.isEmailValidated());
|
||||
master.resetPassword(username, passwordResetBaseURL);
|
||||
|
||||
+4
-1
@@ -6,6 +6,8 @@ import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
@@ -55,7 +57,8 @@ public class SecurityResourceTest {
|
||||
store, accessControlStore, /* hasPermissionsProvider */null,
|
||||
/* setAsActivatorSecurityService */ true);
|
||||
SecurityUtils.setSecurityManager(service.getSecurityManager());
|
||||
service.createSimpleUser(USERNAME, "a@b.c", PASSWORD, "The User", "SAP SE", /* validation URL */ null);
|
||||
service.createSimpleUser(USERNAME, "a@b.c", PASSWORD, "The User", "SAP SE",
|
||||
/* validation URL */ Locale.ENGLISH, null);
|
||||
authenticatedAdmin = SecurityUtils.getSubject();
|
||||
authenticatedAdmin.login(new UsernamePasswordToken(USERNAME, PASSWORD));
|
||||
Session session = authenticatedAdmin.getSession();
|
||||
|
||||
+18
-17
@@ -75,8 +75,8 @@ public class PermissionCheckerTest {
|
||||
if (userStore.getUserByName("jonas") != null) {
|
||||
userStore.deleteUser("jonas");
|
||||
}
|
||||
user = userStore.createUser("jonas", "jonas@dann.io", userTenant);
|
||||
userTenant = userStore.createUserGroup(userTenantId, "jonas-tenant");
|
||||
user = userStore.createUser("jonas", "jonas@dann.io", userTenant);
|
||||
userTenant.add(user);
|
||||
ownership = new OwnershipImpl(user, userTenant);
|
||||
adminTenant.add(adminUser);
|
||||
@@ -210,7 +210,7 @@ public class PermissionCheckerTest {
|
||||
|
||||
@Test
|
||||
public void testMetaPermissionCheck() {
|
||||
final WildcardPermission allPermission = new WildcardPermission("*");
|
||||
final WildcardPermission allPermission = WildcardPermission.builder().build();
|
||||
final WildcardPermission singleTypePermission = type1.getPermission();
|
||||
assertFalse(checkMetaPermissionWithGrantedUserPermissions(singleTypePermission));
|
||||
assertTrue(checkMetaPermissionWithGrantedUserPermissions(singleTypePermission, allPermission));
|
||||
@@ -221,8 +221,7 @@ public class PermissionCheckerTest {
|
||||
assertTrue(checkMetaPermissionWithGrantedUserPermissions(singleTypePermission,
|
||||
type1.getPermission(DefaultActions.READ, DefaultActions.UPDATE)));
|
||||
|
||||
final WildcardPermission combinedTypePermission = new WildcardPermission(
|
||||
type1.getName() + "," + type2.getName());
|
||||
final WildcardPermission combinedTypePermission = WildcardPermission.builder().withTypes(type1, type2).build();
|
||||
assertFalse(checkMetaPermissionWithGrantedUserPermissions(combinedTypePermission));
|
||||
assertFalse(checkMetaPermissionWithGrantedUserPermissions(combinedTypePermission, type1.getPermission()));
|
||||
assertTrue(checkMetaPermissionWithGrantedUserPermissions(combinedTypePermission, allPermission));
|
||||
@@ -234,8 +233,8 @@ public class PermissionCheckerTest {
|
||||
assertTrue(checkMetaPermissionWithGrantedUserPermissions(combinedTypePermission, type1.getPermission(),
|
||||
type2.getPermission(DefaultActions.READ, DefaultActions.DELETE)));
|
||||
|
||||
final WildcardPermission combinedTypeWithDistinctActionPermission = new WildcardPermission(
|
||||
type1.getName() + "," + type2.getName() + ":" + DefaultActions.READ);
|
||||
final WildcardPermission combinedTypeWithDistinctActionPermission = WildcardPermission.builder()
|
||||
.withTypes(type1, type2).withActions(DefaultActions.READ).build();
|
||||
assertFalse(checkMetaPermissionWithGrantedUserPermissions(combinedTypeWithDistinctActionPermission));
|
||||
assertFalse(checkMetaPermissionWithGrantedUserPermissions(combinedTypeWithDistinctActionPermission,
|
||||
type1.getPermission()));
|
||||
@@ -265,7 +264,7 @@ public class PermissionCheckerTest {
|
||||
|
||||
@Test
|
||||
public void testAnyPermissionCheck() {
|
||||
final WildcardPermission allPermission = new WildcardPermission("*");
|
||||
final WildcardPermission allPermission = WildcardPermission.builder().build();
|
||||
final WildcardPermission singleTypePermission = type1.getPermission();
|
||||
assertFalse(checkAnyPermissionWithGrantedUserPermissions(singleTypePermission));
|
||||
assertTrue(checkAnyPermissionWithGrantedUserPermissions(singleTypePermission, allPermission));
|
||||
@@ -276,9 +275,10 @@ public class PermissionCheckerTest {
|
||||
assertTrue(checkAnyPermissionWithGrantedUserPermissions(singleTypePermission,
|
||||
type1.getPermission(DefaultActions.READ, DefaultActions.UPDATE)));
|
||||
assertTrue(checkAnyPermissionWithGrantedUserPermissions(singleTypePermission,
|
||||
new WildcardPermission("*:" + DefaultActions.READ + "," + DefaultActions.UPDATE)));
|
||||
WildcardPermission.builder().withActions(DefaultActions.READ, DefaultActions.UPDATE).build()));
|
||||
assertTrue(checkAnyPermissionWithGrantedUserPermissions(singleTypePermission,
|
||||
new WildcardPermission("*:" + DefaultActions.READ + "," + DefaultActions.UPDATE + ":*")));
|
||||
WildcardPermission.builder().withActions(DefaultActions.READ, DefaultActions.UPDATE)
|
||||
.withIds(WildcardPermission.WILDCARD_TOKEN).build()));
|
||||
|
||||
final WildcardPermission singleTypePermissionWithAction = type1.getPermission(DefaultActions.READ);
|
||||
assertFalse(checkAnyPermissionWithGrantedUserPermissions(singleTypePermissionWithAction));
|
||||
@@ -290,12 +290,12 @@ public class PermissionCheckerTest {
|
||||
assertTrue(checkAnyPermissionWithGrantedUserPermissions(singleTypePermissionWithAction,
|
||||
type1.getPermission(DefaultActions.READ, DefaultActions.UPDATE)));
|
||||
assertTrue(checkAnyPermissionWithGrantedUserPermissions(singleTypePermissionWithAction,
|
||||
new WildcardPermission("*:" + DefaultActions.READ + "," + DefaultActions.UPDATE)));
|
||||
WildcardPermission.builder().withActions(DefaultActions.READ, DefaultActions.UPDATE).build()));
|
||||
assertTrue(checkAnyPermissionWithGrantedUserPermissions(singleTypePermissionWithAction,
|
||||
new WildcardPermission("*:" + DefaultActions.READ + "," + DefaultActions.UPDATE + ":*")));
|
||||
WildcardPermission.builder().withActions(DefaultActions.READ, DefaultActions.UPDATE)
|
||||
.withIds(WildcardPermission.WILDCARD_TOKEN).build()));
|
||||
|
||||
final WildcardPermission combinedTypePermission = new WildcardPermission(
|
||||
type1.getName() + "," + type2.getName());
|
||||
final WildcardPermission combinedTypePermission = WildcardPermission.builder().withTypes(type1, type2).build();
|
||||
assertFalse(checkAnyPermissionWithGrantedUserPermissions(combinedTypePermission));
|
||||
assertTrue(checkAnyPermissionWithGrantedUserPermissions(combinedTypePermission, type1.getPermission()));
|
||||
assertTrue(checkAnyPermissionWithGrantedUserPermissions(combinedTypePermission, allPermission));
|
||||
@@ -309,12 +309,13 @@ public class PermissionCheckerTest {
|
||||
assertTrue(checkAnyPermissionWithGrantedUserPermissions(combinedTypePermission,
|
||||
type1.getPermission(DefaultActions.READ, DefaultActions.UPDATE)));
|
||||
assertTrue(checkAnyPermissionWithGrantedUserPermissions(combinedTypePermission,
|
||||
new WildcardPermission("*:" + DefaultActions.READ + "," + DefaultActions.UPDATE)));
|
||||
WildcardPermission.builder().withActions(DefaultActions.READ, DefaultActions.UPDATE).build()));
|
||||
assertTrue(checkAnyPermissionWithGrantedUserPermissions(combinedTypePermission,
|
||||
new WildcardPermission("*:" + DefaultActions.READ + "," + DefaultActions.UPDATE + ":*")));
|
||||
WildcardPermission.builder().withActions(DefaultActions.READ, DefaultActions.UPDATE)
|
||||
.withIds(WildcardPermission.WILDCARD_TOKEN).build()));
|
||||
|
||||
final WildcardPermission combinedTypeWithDistinctActionPermission = new WildcardPermission(
|
||||
type1.getName() + "," + type2.getName() + ":" + DefaultActions.READ);
|
||||
final WildcardPermission combinedTypeWithDistinctActionPermission = WildcardPermission.builder()
|
||||
.withTypes(type1, type2).withActions(DefaultActions.READ).build();
|
||||
assertFalse(checkAnyPermissionWithGrantedUserPermissions(combinedTypeWithDistinctActionPermission));
|
||||
assertTrue(checkAnyPermissionWithGrantedUserPermissions(combinedTypeWithDistinctActionPermission,
|
||||
type1.getPermission()));
|
||||
|
||||
+125
-6
@@ -1,16 +1,21 @@
|
||||
package com.sap.sse.security.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -18,10 +23,17 @@ import org.junit.Test;
|
||||
import com.mongodb.DB;
|
||||
import com.mongodb.MongoException;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.Util.Pair;
|
||||
import com.sap.sse.mongodb.MongoDBConfiguration;
|
||||
import com.sap.sse.mongodb.MongoDBService;
|
||||
import com.sap.sse.security.UserImpl;
|
||||
import com.sap.sse.security.UserStore;
|
||||
import com.sap.sse.security.shared.Ownership;
|
||||
import com.sap.sse.security.shared.Role;
|
||||
import com.sap.sse.security.shared.RoleDefinition;
|
||||
import com.sap.sse.security.shared.RoleDefinitionImpl;
|
||||
import com.sap.sse.security.shared.RoleImpl;
|
||||
import com.sap.sse.security.shared.SecurityUser;
|
||||
import com.sap.sse.security.shared.User;
|
||||
import com.sap.sse.security.shared.UserGroup;
|
||||
import com.sap.sse.security.shared.UserGroupManagementException;
|
||||
@@ -40,7 +52,7 @@ public class UserStoreWithPersistenceTest {
|
||||
private final String serverName = "dummyServer";
|
||||
private final String prefKey = "pk";
|
||||
private final String prefValue = "pv";
|
||||
|
||||
|
||||
private final UUID userGroupId = UUID.randomUUID();
|
||||
private final String userGroupName = "usergroup";
|
||||
private UserGroup defaultTenant;
|
||||
@@ -81,7 +93,7 @@ public class UserStoreWithPersistenceTest {
|
||||
assertNotNull(store.getUserByName(username));
|
||||
assertNotNull(store.getUserByEmail(email));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testMasterdataIsSaved() throws UserManagementException {
|
||||
store.createUser(username, email, defaultTenant);
|
||||
@@ -142,7 +154,7 @@ public class UserStoreWithPersistenceTest {
|
||||
newStore();
|
||||
assertNull(store.getPreference(username, prefKey));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testCreateUserGroup() throws UserGroupManagementException, UserManagementException {
|
||||
store.deleteUserGroup(defaultTenant);
|
||||
@@ -157,9 +169,9 @@ public class UserStoreWithPersistenceTest {
|
||||
assertNotNull(store.getUserGroup(userGroupId));
|
||||
assertNotNull(store.getUserGroupByName(userGroupName));
|
||||
final User loadedUser = store.getUserByName(username);
|
||||
assertTrue(Util.contains(Util.map(loadedUser.getUserGroups(), g->g.getName()), userGroupName));
|
||||
assertTrue(Util.contains(Util.map(loadedUser.getUserGroups(), g -> g.getName()), userGroupName));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testDeleteUserGroup() throws UserGroupManagementException {
|
||||
UserGroup userGroup = store.createUserGroup(userGroupId, userGroupName);
|
||||
@@ -171,12 +183,14 @@ public class UserStoreWithPersistenceTest {
|
||||
assertNull(store.getUserGroup(userGroupId));
|
||||
assertNull(store.getUserGroupByName(userGroupName));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testTenantUsers() throws UserManagementException, UserGroupManagementException {
|
||||
final User user = store.createUser(username, email, defaultTenant);
|
||||
defaultTenant.add(user);
|
||||
store.updateUserGroup(defaultTenant);
|
||||
user.getDefaultTenantMap().put(serverName, defaultTenant);
|
||||
store.updateUser(user);
|
||||
assertSame(defaultTenant, user.getDefaultTenant(serverName));
|
||||
assertEquals(1, Util.size(defaultTenant.getUsers()));
|
||||
assertSame(user, defaultTenant.getUsers().iterator().next());
|
||||
@@ -214,4 +228,109 @@ public class UserStoreWithPersistenceTest {
|
||||
assertEquals(1, Util.size(store.getUserGroupsOfUser(loadedUser)));
|
||||
assertSame(loadedGroup, store.getUserGroupsOfUser(loadedUser).iterator().next());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetExistingQualificationsForRoleDefinition()
|
||||
throws UserManagementException, UserGroupManagementException {
|
||||
User user = store.createUser("def", "d@test.de", store.createUserGroup(UUID.randomUUID(), "def-tentant"));
|
||||
RoleDefinitionImpl roleDefinition = new RoleDefinitionImpl(UUID.randomUUID(), "My-Test-Role");
|
||||
store.createRoleDefinition(roleDefinition.getId(), roleDefinition.getName(), new ArrayList<>());
|
||||
UserGroup userGroup = store.createUserGroup(UUID.randomUUID(), "Test-Usergroup");
|
||||
|
||||
// tenant is null
|
||||
testWithTenantNull(user, roleDefinition, userGroup);
|
||||
|
||||
// user is null
|
||||
testWithUserNull(user, roleDefinition, userGroup);
|
||||
|
||||
// tenant and user are both given (both not null)
|
||||
testWithTenantAndUserNotNull(user, roleDefinition, userGroup);
|
||||
|
||||
// neither tenant nor user are given (both null)
|
||||
testWithTenantAndUserNull(user, roleDefinition);
|
||||
}
|
||||
|
||||
/** Test getExistingQualificationsForRoleDefinition with both tenant and user null. */
|
||||
private void testWithTenantAndUserNull(User user, RoleDefinitionImpl roleDefinition)
|
||||
throws UserManagementException {
|
||||
Role role = new RoleImpl(roleDefinition, null, null);
|
||||
store.addRoleForUser(user.getName(), role);
|
||||
|
||||
Iterable<Role> rolesFromUser = store.getRolesFromUser(user.getName());
|
||||
assertFalse(Util.size(rolesFromUser) == 0);
|
||||
assertTrue(Util.contains(rolesFromUser, role));
|
||||
Pair<Boolean, Set<Ownership>> result = store.getExistingQualificationsForRoleDefinition(roleDefinition);
|
||||
assertTrue(result.getA());
|
||||
assertNull(result.getB());
|
||||
|
||||
store.removeRoleFromUser(user.getName(), role);
|
||||
}
|
||||
|
||||
/** Test getExistingQualificationsForRoleDefinition with both tenant and user not null. */
|
||||
private void testWithTenantAndUserNotNull(User user, RoleDefinitionImpl roleDefinition, UserGroup userGroup)
|
||||
throws UserManagementException {
|
||||
Role role = new RoleImpl(roleDefinition, userGroup, user);
|
||||
store.addRoleForUser(user.getName(), role);
|
||||
assertThatNoUserHasWildcardRole(userGroup, user, roleDefinition);
|
||||
store.removeRoleFromUser(user.getName(), role);
|
||||
}
|
||||
|
||||
/** Test getExistingQualificationsForRoleDefinition with user null. */
|
||||
private void testWithUserNull(User user, RoleDefinitionImpl roleDefinition, UserGroup userGroup)
|
||||
throws UserManagementException {
|
||||
Role role = new RoleImpl(roleDefinition, userGroup, null);
|
||||
store.addRoleForUser(user.getName(), role);
|
||||
assertThatNoUserHasWildcardRole(userGroup, user, roleDefinition);
|
||||
store.removeRoleFromUser(user.getName(), role);
|
||||
}
|
||||
|
||||
/** Test getExistingQualificationsForRoleDefinition with tenant null. */
|
||||
private void testWithTenantNull(User user, RoleDefinitionImpl roleDefinition, UserGroup userGroup)
|
||||
throws UserManagementException {
|
||||
Role role = new RoleImpl(roleDefinition, null, user);
|
||||
store.addRoleForUser(user.getName(), role);
|
||||
assertThatNoUserHasWildcardRole(userGroup, user, roleDefinition);
|
||||
store.removeRoleFromUser(user.getName(), role);
|
||||
}
|
||||
|
||||
/**
|
||||
* assert that no user has a wildcard role getExistingQualificationsForRoleDefinition with both tenant and user not
|
||||
* null.
|
||||
*/
|
||||
private void assertThatNoUserHasWildcardRole(UserGroup userGroup, User user, RoleDefinition roleDefinition)
|
||||
throws UserManagementException {
|
||||
Iterable<Role> rolesFromUser = store.getRolesFromUser(user.getName());
|
||||
// check that role was added correctly
|
||||
assertFalse(Util.size(rolesFromUser) == 0);
|
||||
boolean containsRole = false;
|
||||
for (Role role : rolesFromUser) {
|
||||
containsRole |= role.getRoleDefinition().equals(roleDefinition);
|
||||
}
|
||||
assertTrue(containsRole);
|
||||
|
||||
// check if other users have the role
|
||||
Pair<Boolean, Set<Ownership>> result = store.getExistingQualificationsForRoleDefinition(roleDefinition);
|
||||
assertFalse(result.getA());
|
||||
assertNotNull(result.getB());
|
||||
|
||||
// This stream effectively executes B.getUserOwner.getName for each ownership and filters null values to avoid
|
||||
// exceptions.
|
||||
List<String> usernamesWithRole = result.getB().stream().map(Ownership::getUserOwner).filter(uo -> uo != null)
|
||||
.map(SecurityUser::getName).filter(su -> su != null).collect(Collectors.toList());
|
||||
|
||||
// Similar to above just with B.getTenantOwner.getId.
|
||||
List<UUID> groupNamesWithRole = result.getB().stream().map(Ownership::getTenantOwner).filter(uo -> uo != null)
|
||||
.map(UserGroup::getId).filter(su -> su != null).collect(Collectors.toList());
|
||||
|
||||
// check that either one is not false
|
||||
assertFalse(usernamesWithRole.isEmpty() && groupNamesWithRole.isEmpty());
|
||||
|
||||
if (!usernamesWithRole.isEmpty()) {
|
||||
// list of usernames is not empty -> check if user name is contained in it
|
||||
assertTrue(usernamesWithRole.contains(user.getName()));
|
||||
} else if (!groupNamesWithRole.isEmpty()) {
|
||||
// list of groupids is not empty -> check if group id is contained in it
|
||||
assertTrue(groupNamesWithRole.contains(userGroup.getId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -61,7 +61,7 @@ public interface UserManagementService extends RemoteService {
|
||||
|
||||
void deleteRoleDefinition(String roleDefinitionIdAsString);
|
||||
|
||||
void updateRoleDefinition(RoleDefinitionDTO roleWithNewProperties);
|
||||
void updateRoleDefinition(RoleDefinitionDTO roleWithNewProperties) throws UnauthorizedException;
|
||||
|
||||
ArrayList<RoleDefinitionDTO> getRoleDefinitions();
|
||||
|
||||
|
||||
+9
@@ -12,6 +12,7 @@ public class DefaultActionsImagesBarCell extends ImagesBarCell {
|
||||
public static final String ACTION_DELETE = DefaultActions.DELETE.name();
|
||||
public static final String ACTION_UPDATE = DefaultActions.UPDATE.name();
|
||||
public static final String ACTION_CHANGE_OWNERSHIP = DefaultActions.CHANGE_OWNERSHIP.name();
|
||||
public static final String ACTION_CHANGE_ACL = DefaultActions.CHANGE_ACL.name();
|
||||
|
||||
private final StringMessages stringMessages;
|
||||
|
||||
@@ -46,4 +47,12 @@ public class DefaultActionsImagesBarCell extends ImagesBarCell {
|
||||
IconResources.INSTANCE.changeOwnershipIcon());
|
||||
}
|
||||
|
||||
/**
|
||||
* @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());
|
||||
}
|
||||
|
||||
}
|
||||
+18
-11
@@ -10,6 +10,7 @@ import com.google.gwt.user.client.ui.Grid;
|
||||
import com.google.gwt.user.client.ui.Label;
|
||||
import com.google.gwt.user.client.ui.TextBox;
|
||||
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;
|
||||
@@ -37,34 +38,39 @@ public class EditOwnershipDialog extends DataEntryDialog<OwnershipDialogResult>
|
||||
private SecurityUser resolvedUser;
|
||||
private UserGroup resolvedUserGroup;
|
||||
|
||||
public static class OwnershipDialogResult {
|
||||
static class OwnershipDialogResult {
|
||||
private final Ownership ownership;
|
||||
private final String username;
|
||||
private final String userGroupName;
|
||||
private final boolean resolvingUsername;
|
||||
private final boolean resolvingUserGroupName;
|
||||
public OwnershipDialogResult(Ownership ownership, String username, String userGroupName,
|
||||
boolean resolvingUsername, boolean resolvingUserGroupName) {
|
||||
super();
|
||||
|
||||
private OwnershipDialogResult(final Ownership ownership, final String username, final String userGroupName,
|
||||
final boolean resolvingUsername, final boolean resolvingUserGroupName) {
|
||||
this.ownership = ownership;
|
||||
this.username = username;
|
||||
this.userGroupName = userGroupName;
|
||||
this.resolvingUsername = resolvingUsername;
|
||||
this.resolvingUserGroupName = resolvingUserGroupName;
|
||||
}
|
||||
public Ownership getOwnership() {
|
||||
|
||||
private Ownership getOwnership() {
|
||||
return ownership;
|
||||
}
|
||||
public boolean isResolvingUsername() {
|
||||
|
||||
private boolean isResolvingUsername() {
|
||||
return resolvingUsername;
|
||||
}
|
||||
public boolean isResolvingUserGroupName() {
|
||||
|
||||
private boolean isResolvingUserGroupName() {
|
||||
return resolvingUserGroupName;
|
||||
}
|
||||
public String getUsername() {
|
||||
|
||||
private String getUsername() {
|
||||
return username;
|
||||
}
|
||||
public String getUserGroupName() {
|
||||
|
||||
private String getUserGroupName() {
|
||||
return userGroupName;
|
||||
}
|
||||
}
|
||||
@@ -95,7 +101,7 @@ public class EditOwnershipDialog extends DataEntryDialog<OwnershipDialogResult>
|
||||
}
|
||||
}
|
||||
|
||||
public EditOwnershipDialog(UserManagementServiceAsync userManagementService, Ownership ownership,
|
||||
private EditOwnershipDialog(UserManagementServiceAsync userManagementService, Ownership ownership,
|
||||
StringMessages stringMessages, DialogCallback<OwnershipDialogResult> callback) {
|
||||
super(stringMessages.ownership(), stringMessages.editObjectOwnership(), stringMessages.ok(),
|
||||
stringMessages.cancel(), new Validator(stringMessages), callback);
|
||||
@@ -161,7 +167,8 @@ public class EditOwnershipDialog extends DataEntryDialog<OwnershipDialogResult>
|
||||
|
||||
@Override
|
||||
protected OwnershipDialogResult getResult() {
|
||||
return new OwnershipDialogResult(new OwnershipImpl(resolvedUser, resolvedUserGroup), usernameBox.getText(), groupnameBox.getText(), resolvingUsername, resolvingUserGroupName);
|
||||
return new OwnershipDialogResult(new OwnershipImpl(resolvedUser, resolvedUserGroup), usernameBox.getText(),
|
||||
groupnameBox.getText(), resolvingUsername, resolvingUserGroupName);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+5
-7
@@ -28,7 +28,7 @@ import com.sap.sse.security.ui.client.i18n.StringMessages;
|
||||
*/
|
||||
public class SecuredObjectOwnerColumn<T extends SecuredObject> extends TextColumn<T> {
|
||||
|
||||
private final Function<T, Named> ownerResolver;
|
||||
private final Function<T, Optional<Named>> ownerResolver;
|
||||
|
||||
/**
|
||||
* Creates a new {@link SecuredObjectOwnerColumn} instance used the provided {@link Function resolver} to determine
|
||||
@@ -39,12 +39,12 @@ public class SecuredObjectOwnerColumn<T extends SecuredObject> extends TextColum
|
||||
*/
|
||||
public SecuredObjectOwnerColumn(final Function<Ownership, Named> ownerResolver) {
|
||||
final Function<T, Ownership> ownershipResolver = SecuredObject::getOwnership;
|
||||
this.ownerResolver = ownershipResolver.andThen(ownerResolver);
|
||||
this.ownerResolver = ownershipResolver.andThen(ownership -> Optional.ofNullable(ownership).map(ownerResolver));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String getValue(final T object) {
|
||||
return Optional.ofNullable(ownerResolver.apply(object)).map(Named::getName).orElse("");
|
||||
return ownerResolver.apply(object).map(Named::getName).orElse("");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -58,16 +58,14 @@ public class SecuredObjectOwnerColumn<T extends SecuredObject> extends TextColum
|
||||
* @return {@link SecuredObjectOwnerColumn} instance showing the {@link Ownership#getTenantOwner() tenant owner}
|
||||
*/
|
||||
public static <T extends SecuredObject> SecuredObjectOwnerColumn<T> getGroupOwnerColumn() {
|
||||
return new SecuredObjectOwnerColumn<>(
|
||||
(Ownership t) -> Optional.ofNullable(t).map(x -> x.getTenantOwner()).orElse(null));
|
||||
return new SecuredObjectOwnerColumn<>(Ownership::getTenantOwner);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@link SecuredObjectOwnerColumn} instance showing the {@link Ownership#getUserOwner() user owner}
|
||||
*/
|
||||
public static <T extends SecuredObject> SecuredObjectOwnerColumn<T> getUserOwnerColumn() {
|
||||
return new SecuredObjectOwnerColumn<>(
|
||||
(Ownership t) -> Optional.ofNullable(t).map(x -> x.getUserOwner()).orElse(null));
|
||||
return new SecuredObjectOwnerColumn<>(Ownership::getUserOwner);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+28
-3
@@ -230,10 +230,35 @@ public class UserEditDialog extends DataEntryDialog<Pair<UserDTO, Iterable<Tripl
|
||||
roles = Util.map(rolesEditor.getValue(), roleName->{
|
||||
final Triple<String, String, String> roleDefinitionNameAndTenantQualifierNameAndUserQualifierName = RoleImpl
|
||||
.getRoleDefinitionNameAndTenantQualifierNameAndUserQualifierName(roleName);
|
||||
RoleDefinition roleDefinition = serverRoleDefinitionsByName.get(roleDefinitionNameAndTenantQualifierNameAndUserQualifierName.getA());
|
||||
|
||||
final String roleNameToAdd = roleDefinitionNameAndTenantQualifierNameAndUserQualifierName.getA();
|
||||
final String roleTenantNameToAdd = roleDefinitionNameAndTenantQualifierNameAndUserQualifierName.getB();
|
||||
final String roleUsernameToAdd = roleDefinitionNameAndTenantQualifierNameAndUserQualifierName.getC();
|
||||
|
||||
RoleDefinition roleDefinition = null;
|
||||
for (Role role : userToEdit.getRoles()) {
|
||||
final String existingRoleName = role.getName();
|
||||
final String existingTenantName = role.getQualifiedForTenant() == null ? null : role.getQualifiedForTenant().getName();
|
||||
final String existingUsername = role.getQualifiedForUser() == null ? null : role.getQualifiedForUser().getName();
|
||||
|
||||
// handles unchanged role associations to not rely on the current user to see all roles already associated
|
||||
if (Util.equalsWithNull(existingRoleName, roleNameToAdd)
|
||||
&& Util.equalStringsWithEmptyIsNull(existingTenantName, roleTenantNameToAdd)
|
||||
&& Util.equalStringsWithEmptyIsNull(existingUsername, roleUsernameToAdd)) {
|
||||
roleDefinition =role.getRoleDefinition();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (roleDefinition == null) {
|
||||
roleDefinition = serverRoleDefinitionsByName.get(roleDefinitionNameAndTenantQualifierNameAndUserQualifierName.getA());
|
||||
}
|
||||
if (roleDefinition == null) {
|
||||
// FIXME how to handle this case? Use null as role definition ID and validate afterwards
|
||||
return null;
|
||||
}
|
||||
return new Triple<>(
|
||||
roleDefinition.getId(), /* qualifying tenant name */ roleDefinitionNameAndTenantQualifierNameAndUserQualifierName.getB(),
|
||||
/* qualifying user name */ roleDefinitionNameAndTenantQualifierNameAndUserQualifierName.getC());
|
||||
roleDefinition.getId(), /* qualifying tenant name */ roleTenantNameToAdd,
|
||||
/* qualifying user name */ roleUsernameToAdd);
|
||||
});
|
||||
} else {
|
||||
roles = Collections.emptyList();
|
||||
|
||||
+23
@@ -1,6 +1,7 @@
|
||||
package com.sap.sse.security.ui.server;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -250,4 +251,26 @@ public class SecurityDTOFactory {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* prunes the {@link AccessControlList} for the given {@link SecurityUser filterForUser} by removing all user groups
|
||||
* the user is not in from the resulting ACL.
|
||||
*/
|
||||
public AccessControlList pruneAccessControlListForUser(AccessControlList acl, SecurityUser filterForUser) {
|
||||
final AccessControlList result;
|
||||
if (acl == null || filterForUser == null) {
|
||||
result = acl;
|
||||
} else {
|
||||
final Collection<UserGroup> userGroups = Util.createSet(filterForUser.getUserGroups());
|
||||
|
||||
final Map<UserGroup, Set<String>> actionsByUserGroup = new HashMap<>();
|
||||
for (final Entry<UserGroup, Set<String>> entry : acl.getActionsByUserGroup().entrySet()) {
|
||||
if (userGroups.contains(entry.getKey())) {
|
||||
actionsByUserGroup.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
result = new AccessControlListImpl(actionsByUserGroup);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
+11
-4
@@ -57,7 +57,7 @@ public abstract class SecurityDTOUtil {
|
||||
final Map<SecurityUser, SecurityUser> fromOriginalToStrippedDownUser = new HashMap<>();
|
||||
final Map<UserGroup, UserGroup> fromOriginalToStrippedDownUserGroup = new HashMap<>();
|
||||
addSecurityInformation(securityDTOFactory, securityService, securedObject, objectId,
|
||||
fromOriginalToStrippedDownUser, fromOriginalToStrippedDownUserGroup);
|
||||
fromOriginalToStrippedDownUser, fromOriginalToStrippedDownUserGroup, false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,11 +89,18 @@ public abstract class SecurityDTOUtil {
|
||||
final SecurityService securityService, final SecuredObject securedObject,
|
||||
final QualifiedObjectIdentifier objectId,
|
||||
final Map<SecurityUser, SecurityUser> fromOriginalToStrippedDownUser,
|
||||
final Map<UserGroup, UserGroup> fromOriginalToStrippedDownUserGroup) {
|
||||
final Map<UserGroup, UserGroup> fromOriginalToStrippedDownUserGroup,
|
||||
final boolean disablePruningForCurrentUser) {
|
||||
final AccessControlListAnnotation accessControlList = securityService.getAccessControlList(objectId);
|
||||
securedObject.setAccessControlList(securityDTOFactory.createAccessControlListDTO(
|
||||
AccessControlList accessControlListDTO = securityDTOFactory.createAccessControlListDTO(
|
||||
accessControlList == null ? null : accessControlList.getAnnotation(), fromOriginalToStrippedDownUser,
|
||||
fromOriginalToStrippedDownUserGroup));
|
||||
fromOriginalToStrippedDownUserGroup);
|
||||
if (disablePruningForCurrentUser) {
|
||||
securedObject.setAccessControlList(accessControlListDTO);
|
||||
} else {
|
||||
securedObject.setAccessControlList(securityDTOFactory.pruneAccessControlListForUser(accessControlListDTO,
|
||||
securityService.getCurrentUser()));
|
||||
}
|
||||
final OwnershipAnnotation ownership = securityService.getOwnership(objectId);
|
||||
securedObject.setOwnership(
|
||||
securityDTOFactory.createOwnershipDTO(ownership == null ? null : ownership.getAnnotation(),
|
||||
|
||||
+80
-32
@@ -42,6 +42,7 @@ import com.sap.sse.security.shared.Ownership;
|
||||
import com.sap.sse.security.shared.OwnershipAnnotation;
|
||||
import com.sap.sse.security.shared.QualifiedObjectIdentifier;
|
||||
import com.sap.sse.security.shared.Role;
|
||||
import com.sap.sse.security.shared.RoleDefinition;
|
||||
import com.sap.sse.security.shared.RoleImpl;
|
||||
import com.sap.sse.security.shared.SecurityUser;
|
||||
import com.sap.sse.security.shared.UnauthorizedException;
|
||||
@@ -118,11 +119,22 @@ public class UserManagementServiceImpl extends RemoteServiceServlet implements U
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateRoleDefinition(RoleDefinitionDTO roleDefinitionWithNewProperties) {
|
||||
// FIXME an additional check is needed to verify that a user may only grant/revoke permissions that he owns
|
||||
// In case of role permissions this means the user needs to own an unqualified version of the permission because
|
||||
// any user may own the role with any qualification
|
||||
SecurityUtils.getSubject().checkPermission(SecuredSecurityTypes.ROLE_DEFINITION.getStringPermissionForObjects(DefaultActions.UPDATE, roleDefinitionWithNewProperties.getId().toString()));
|
||||
public void updateRoleDefinition(RoleDefinitionDTO roleDefinitionWithNewProperties) throws UnauthorizedException {
|
||||
SecurityUtils.getSubject().checkPermission(SecuredSecurityTypes.ROLE_DEFINITION.getStringPermissionForObjects(
|
||||
DefaultActions.UPDATE, roleDefinitionWithNewProperties.getId().toString()));
|
||||
|
||||
RoleDefinition existingRole = getSecurityService().getRoleDefinition(roleDefinitionWithNewProperties.getId());
|
||||
if (existingRole == null) {
|
||||
throw new UnauthorizedException("Role does not exist");
|
||||
}
|
||||
Set<WildcardPermission> addedPermissions = new HashSet<>(roleDefinitionWithNewProperties.getPermissions());
|
||||
addedPermissions.removeAll(existingRole.getPermissions());
|
||||
|
||||
if (!getSecurityService().hasUserAllWildcardPermissionsForAlreadyRealizedQualifications(existingRole, addedPermissions)) {
|
||||
throw new UnauthorizedException("Not permitted to grant permissions for role "
|
||||
+ roleDefinitionWithNewProperties.getName());
|
||||
}
|
||||
|
||||
getSecurityService().updateRoleDefinition(roleDefinitionWithNewProperties);
|
||||
}
|
||||
|
||||
@@ -154,9 +166,11 @@ public class UserManagementServiceImpl extends RemoteServiceServlet implements U
|
||||
|
||||
@Override
|
||||
public Collection<AccessControlListAnnotation> getAccessControlLists() throws UnauthorizedException {
|
||||
// TODO decide whether a global getAccessControlList functionality is needed
|
||||
List<AccessControlListAnnotation> acls = new ArrayList<>();
|
||||
for (AccessControlListAnnotation acl : getSecurityService().getAccessControlLists()) {
|
||||
if (SecurityUtils.getSubject().isPermitted(SecuredSecurityTypes.ACCESS_CONTROL_LIST.getStringPermissionForObjects(DefaultActions.READ, acl.getIdOfAnnotatedObject().toString()))) {
|
||||
if (SecurityUtils.getSubject()
|
||||
.isPermitted(acl.getIdOfAnnotatedObject().getStringPermission(DefaultActions.CHANGE_ACL))) {
|
||||
acls.add(securityDTOFactory.createAccessControlListAnnotationDTO(acl));
|
||||
}
|
||||
}
|
||||
@@ -165,13 +179,15 @@ public class UserManagementServiceImpl extends RemoteServiceServlet implements U
|
||||
|
||||
@Override
|
||||
public AccessControlListAnnotation getAccessControlList(QualifiedObjectIdentifier idOfAccessControlledObject) {
|
||||
SecurityUtils.getSubject().checkPermission(SecuredSecurityTypes.ACCESS_CONTROL_LIST.getStringPermissionForObjects(DefaultActions.READ, idOfAccessControlledObject.toString()));
|
||||
SecurityUtils.getSubject()
|
||||
.checkPermission(idOfAccessControlledObject.getStringPermission(DefaultActions.CHANGE_ACL));
|
||||
return securityDTOFactory.createAccessControlListAnnotationDTO(getSecurityService().getAccessControlList(idOfAccessControlledObject));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessControlList updateAccessControlList(QualifiedObjectIdentifier idOfAccessControlledObject, Map<String, Set<String>> permissionStrings) throws UnauthorizedException {
|
||||
if (SecurityUtils.getSubject().isPermitted(SecuredSecurityTypes.ACCESS_CONTROL_LIST.getStringPermissionForObjects(DefaultActions.UPDATE, idOfAccessControlledObject.toString()))) {
|
||||
if (SecurityUtils.getSubject()
|
||||
.isPermitted(idOfAccessControlledObject.getStringPermission(DefaultActions.CHANGE_ACL))) {
|
||||
Map<UserGroup, Set<String>> permissionMap = new HashMap<>();
|
||||
for (String group : permissionStrings.keySet()) {
|
||||
permissionMap.put(getSecurityService().getUserGroupByName(group), permissionStrings.get(group));
|
||||
@@ -184,7 +200,8 @@ public class UserManagementServiceImpl extends RemoteServiceServlet implements U
|
||||
|
||||
@Override
|
||||
public AccessControlList addToAccessControlList(QualifiedObjectIdentifier idOfAccessControlledObject, String groupIdAsString, String action) throws UnauthorizedException {
|
||||
if (SecurityUtils.getSubject().isPermitted(SecuredSecurityTypes.ACCESS_CONTROL_LIST.getStringPermissionForObjects(DefaultActions.UPDATE, idOfAccessControlledObject.toString()))) {
|
||||
if (SecurityUtils.getSubject()
|
||||
.isPermitted(idOfAccessControlledObject.getStringPermission(DefaultActions.CHANGE_ACL))) {
|
||||
UserGroup userGroup = getUserGroup(groupIdAsString);
|
||||
return securityDTOFactory.createAccessControlListDTO(getSecurityService().addToAccessControlList(idOfAccessControlledObject, userGroup, action));
|
||||
} else {
|
||||
@@ -435,12 +452,15 @@ public class UserManagementServiceImpl extends RemoteServiceServlet implements U
|
||||
public SuccessInfo setRolesForUser(String username,
|
||||
Iterable<Triple<UUID, String, String>> roleDefinitionIdAndTenantQualifierNameAndUsernames)
|
||||
throws UnauthorizedException {
|
||||
// FIXME an additional check is needed to verify that a user may only grant/revoke permissions that he owns
|
||||
// The current user needs to have all permissions of a role for the specific tenant qualification or an unqualified version
|
||||
if (SecurityUtils.getSubject().isPermitted(
|
||||
SecuredSecurityTypes.USER.getStringPermissionForObjects(UserActions.GRANT_PERMISSION, username))
|
||||
&& SecurityUtils.getSubject().isPermitted(SecuredSecurityTypes.USER
|
||||
.getStringPermissionForObjects(UserActions.REVOKE_PERMISSION, username))) {
|
||||
final boolean isUserPermittedToGrantPermissionsForOtherUser = SecurityUtils.getSubject().isPermitted(
|
||||
SecuredSecurityTypes.USER.getStringPermissionForObjects(UserActions.GRANT_PERMISSION, username));
|
||||
final boolean isUserPermittedToRevokePermissionsForOtherUser = SecurityUtils.getSubject().isPermitted(SecuredSecurityTypes.USER
|
||||
.getStringPermissionForObjects(UserActions.REVOKE_PERMISSION, username));
|
||||
if (!isUserPermittedToGrantPermissionsForOtherUser
|
||||
&& !isUserPermittedToRevokePermissionsForOtherUser) {
|
||||
return new SuccessInfo(false, "Not permitted to grant or revoke permissions for user " + username,
|
||||
/* redirectURL */null, null);
|
||||
} else {
|
||||
User u = getSecurityService().getUserByName(username);
|
||||
if (u == null) {
|
||||
return new SuccessInfo(false, "User does not exist.", /* redirectURL */ null, null);
|
||||
@@ -467,12 +487,29 @@ public class UserManagementServiceImpl extends RemoteServiceServlet implements U
|
||||
Set<Role> roleDefinitionsToRemove = new HashSet<>();
|
||||
Util.addAll(u.getRoles(), roleDefinitionsToRemove);
|
||||
Util.removeAll(rolesToSet, roleDefinitionsToRemove);
|
||||
for (Role roleToRemove : roleDefinitionsToRemove) {
|
||||
getSecurityService().removeRoleFromUser(u, roleToRemove);
|
||||
if (!roleDefinitionsToRemove.isEmpty() && !isUserPermittedToRevokePermissionsForOtherUser) {
|
||||
return new SuccessInfo(false, "Not permitted to revoke permissions for user " + username,
|
||||
/* redirectURL */null, null);
|
||||
}
|
||||
Set<Role> rolesToAdd = new HashSet<>();
|
||||
Util.addAll(rolesToSet, rolesToAdd);
|
||||
Util.removeAll(u.getRoles(), rolesToAdd);
|
||||
if (!rolesToAdd.isEmpty() && !isUserPermittedToGrantPermissionsForOtherUser) {
|
||||
return new SuccessInfo(false, "Not permitted to grant permissions for user " + username,
|
||||
/* redirectURL */null, null);
|
||||
}
|
||||
for (Role roleToAdd : rolesToAdd) {
|
||||
for (WildcardPermission permissionOfRoleToAdd : roleToAdd.getPermissions()) {
|
||||
if (!getSecurityService().hasCurrentUserMetaPermission(permissionOfRoleToAdd, roleToAdd.getQualificationAsOwnership())) {
|
||||
return new SuccessInfo(false,
|
||||
"Not permitted to grant role " + roleToAdd.getName() + " for user " + username,
|
||||
/* redirectURL */null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (Role roleToRemove : roleDefinitionsToRemove) {
|
||||
getSecurityService().removeRoleFromUser(u, roleToRemove);
|
||||
}
|
||||
for (Role roleToAdd : rolesToAdd) {
|
||||
getSecurityService().addRoleForUser(u, roleToAdd);
|
||||
}
|
||||
@@ -481,8 +518,6 @@ public class UserManagementServiceImpl extends RemoteServiceServlet implements U
|
||||
final UserDTO userDTO = securityDTOFactory.createUserDTOFromUser(u, getSecurityService());
|
||||
return new SuccessInfo(true, message, /* redirectURL */null,
|
||||
new Pair<UserDTO, UserDTO>(userDTO, getAllUser()));
|
||||
} else {
|
||||
throw new UnauthorizedException("Not permitted to grant permissions to user");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -503,10 +538,15 @@ public class UserManagementServiceImpl extends RemoteServiceServlet implements U
|
||||
|
||||
@Override
|
||||
public SuccessInfo setPermissionsForUser(String username, Iterable<WildcardPermission> permissions) throws UnauthorizedException {
|
||||
if (SecurityUtils.getSubject().isPermitted(
|
||||
SecuredSecurityTypes.USER.getStringPermissionForObjects(UserActions.GRANT_PERMISSION, username))
|
||||
&& SecurityUtils.getSubject().isPermitted(SecuredSecurityTypes.USER
|
||||
.getStringPermissionForObjects(UserActions.REVOKE_PERMISSION, username))) {
|
||||
final boolean isUserPermittedToGrantPermissionsForOtherUser = SecurityUtils.getSubject().isPermitted(
|
||||
SecuredSecurityTypes.USER.getStringPermissionForObjects(UserActions.GRANT_PERMISSION, username));
|
||||
final boolean isUserPermittedToRevokePermissionsForOtherUser = SecurityUtils.getSubject().isPermitted(SecuredSecurityTypes.USER
|
||||
.getStringPermissionForObjects(UserActions.REVOKE_PERMISSION, username));
|
||||
if (!isUserPermittedToGrantPermissionsForOtherUser
|
||||
&& !isUserPermittedToRevokePermissionsForOtherUser) {
|
||||
return new SuccessInfo(false, "Not permitted to grant or revoke permissions for user " + username,
|
||||
/* redirectURL */null, null);
|
||||
} else {
|
||||
User u = getSecurityService().getUserByName(username);
|
||||
if (u == null) {
|
||||
return new SuccessInfo(false, "User does not exist.", /* redirectURL */null, null);
|
||||
@@ -514,26 +554,34 @@ public class UserManagementServiceImpl extends RemoteServiceServlet implements U
|
||||
Set<WildcardPermission> permissionsToRemove = new HashSet<>();
|
||||
Util.addAll(u.getPermissions(), permissionsToRemove);
|
||||
Util.removeAll(permissions, permissionsToRemove);
|
||||
for (WildcardPermission permissionToRemove : permissionsToRemove) {
|
||||
getSecurityService().removePermissionFromUser(username, permissionToRemove);
|
||||
if (!permissionsToRemove.isEmpty() && !isUserPermittedToRevokePermissionsForOtherUser) {
|
||||
return new SuccessInfo(false, "Not permitted to revoke permissions for user " + username,
|
||||
/* redirectURL */null, null);
|
||||
}
|
||||
Set<WildcardPermission> permissionsToAdd = new HashSet<>();
|
||||
Util.addAll(permissions, permissionsToAdd);
|
||||
Util.removeAll(u.getPermissions(), permissionsToAdd);
|
||||
if (!permissionsToAdd.isEmpty() && !isUserPermittedToGrantPermissionsForOtherUser) {
|
||||
return new SuccessInfo(false, "Not permitted to grant permissions for user " + username,
|
||||
/* redirectURL */null, null);
|
||||
}
|
||||
for (WildcardPermission permissionToAdd : permissionsToAdd) {
|
||||
OwnershipAnnotation currentUsersOwnerShip = getSecurityService()
|
||||
.getOwnership(getSecurityService().getCurrentUser().getIdentifier());
|
||||
if (getSecurityService().hasCurrentUserMetaPermission(permissionToAdd,
|
||||
currentUsersOwnerShip.getAnnotation())) {
|
||||
getSecurityService().addPermissionForUser(username, permissionToAdd);
|
||||
if (!getSecurityService().hasCurrentUserMetaPermission(permissionToAdd, null)) {
|
||||
return new SuccessInfo(false,
|
||||
"Not permitted to grant permission " + permissionToAdd + " for user " + username,
|
||||
/* redirectURL */null, null);
|
||||
}
|
||||
}
|
||||
for (WildcardPermission permissionToRemove : permissionsToRemove) {
|
||||
getSecurityService().removePermissionFromUser(username, permissionToRemove);
|
||||
}
|
||||
for (WildcardPermission permissionToAdd : permissionsToAdd) {
|
||||
getSecurityService().addPermissionForUser(username, permissionToAdd);
|
||||
}
|
||||
final String message = "Set roles " + permissions + " for user " + username;
|
||||
final UserDTO userDTO = securityDTOFactory.createUserDTOFromUser(u, getSecurityService());
|
||||
return new SuccessInfo(true, message, /* redirectURL */null,
|
||||
new Pair<UserDTO, UserDTO>(userDTO, getAllUser()));
|
||||
} else {
|
||||
throw new UnauthorizedException("Not permitted to grant or revoke permissions for user "+username);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+8
-8
@@ -6,13 +6,13 @@ import com.sap.sse.security.shared.AccessControlList;
|
||||
import com.sap.sse.security.shared.Ownership;
|
||||
import com.sap.sse.security.shared.RoleDefinitionImpl;
|
||||
import com.sap.sse.security.shared.SecuredObject;
|
||||
import com.sap.sse.security.shared.SecurityInformationDTO;
|
||||
|
||||
public class RoleDefinitionDTO extends RoleDefinitionImpl implements SecuredObject {
|
||||
|
||||
private static final long serialVersionUID = -2580755958300866126L;
|
||||
|
||||
private AccessControlList accessControlList;
|
||||
private Ownership ownership;
|
||||
private static final long serialVersionUID = -3340211553071045099L;
|
||||
|
||||
private SecurityInformationDTO securityInformation = new SecurityInformationDTO();
|
||||
|
||||
@Deprecated
|
||||
RoleDefinitionDTO() {} // for GWT serialization only
|
||||
@@ -23,22 +23,22 @@ public class RoleDefinitionDTO extends RoleDefinitionImpl implements SecuredObje
|
||||
|
||||
@Override
|
||||
public final AccessControlList getAccessControlList() {
|
||||
return accessControlList;
|
||||
return securityInformation.getAccessControlList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Ownership getOwnership() {
|
||||
return ownership;
|
||||
return securityInformation.getOwnership();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setAccessControlList(final AccessControlList accessControlList) {
|
||||
this.accessControlList = accessControlList;
|
||||
this.securityInformation.setAccessControlList(accessControlList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setOwnership(final Ownership ownership) {
|
||||
this.ownership = ownership;
|
||||
this.securityInformation.setOwnership(ownership);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+9
-8
@@ -9,13 +9,15 @@ import com.sap.sse.security.shared.AccessControlList;
|
||||
import com.sap.sse.security.shared.Ownership;
|
||||
import com.sap.sse.security.shared.Role;
|
||||
import com.sap.sse.security.shared.SecuredObject;
|
||||
import com.sap.sse.security.shared.SecurityInformationDTO;
|
||||
import com.sap.sse.security.shared.UserGroup;
|
||||
import com.sap.sse.security.shared.WildcardPermission;
|
||||
import com.sap.sse.security.shared.impl.SecurityUserImpl;
|
||||
|
||||
public class UserDTO extends SecurityUserImpl implements IsSerializable, SecuredObject {
|
||||
private static final long serialVersionUID = -4807678211983511872L;
|
||||
|
||||
|
||||
private static final long serialVersionUID = 7556217539893146187L;
|
||||
|
||||
private String email;
|
||||
private String fullName;
|
||||
private String company;
|
||||
@@ -23,8 +25,7 @@ public class UserDTO extends SecurityUserImpl implements IsSerializable, Secured
|
||||
private List<AccountDTO> accounts;
|
||||
private boolean emailValidated;
|
||||
private List<UserGroup> groups;
|
||||
private AccessControlList accessControlList;
|
||||
private Ownership ownership;
|
||||
private SecurityInformationDTO securityInformation = new SecurityInformationDTO();
|
||||
private UserGroup defaultTenantForCurrentServer;
|
||||
|
||||
/**
|
||||
@@ -108,22 +109,22 @@ public class UserDTO extends SecurityUserImpl implements IsSerializable, Secured
|
||||
|
||||
@Override
|
||||
public final AccessControlList getAccessControlList() {
|
||||
return accessControlList;
|
||||
return securityInformation.getAccessControlList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Ownership getOwnership() {
|
||||
return ownership;
|
||||
return securityInformation.getOwnership();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setAccessControlList(final AccessControlList accessControlList) {
|
||||
this.accessControlList = accessControlList;
|
||||
this.securityInformation.setAccessControlList(accessControlList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setOwnership(final Ownership ownership) {
|
||||
this.ownership = ownership;
|
||||
this.securityInformation.setOwnership(ownership);
|
||||
}
|
||||
|
||||
public void setDefaultTenantForCurrentServer(UserGroup defaultTenant) {
|
||||
|
||||
+28
@@ -18,6 +18,7 @@ import org.apache.shiro.SecurityUtils;
|
||||
|
||||
import com.sap.sse.ServerInfo;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.Util.Pair;
|
||||
import com.sap.sse.concurrent.LockUtil;
|
||||
import com.sap.sse.concurrent.NamedReentrantReadWriteLock;
|
||||
import com.sap.sse.security.PreferenceConverter;
|
||||
@@ -27,6 +28,7 @@ import com.sap.sse.security.UserImpl;
|
||||
import com.sap.sse.security.UserStore;
|
||||
import com.sap.sse.security.shared.Account;
|
||||
import com.sap.sse.security.shared.AdminRole;
|
||||
import com.sap.sse.security.shared.Ownership;
|
||||
import com.sap.sse.security.shared.PredefinedRoles;
|
||||
import com.sap.sse.security.shared.Role;
|
||||
import com.sap.sse.security.shared.RoleDefinition;
|
||||
@@ -38,6 +40,7 @@ import com.sap.sse.security.shared.UserGroupManagementException;
|
||||
import com.sap.sse.security.shared.UserManagementException;
|
||||
import com.sap.sse.security.shared.UserRole;
|
||||
import com.sap.sse.security.shared.WildcardPermission;
|
||||
import com.sap.sse.security.shared.impl.OwnershipImpl;
|
||||
import com.sap.sse.security.shared.impl.UserGroupImpl;
|
||||
import com.sap.sse.security.userstore.mongodb.impl.FieldNames.Tenant;
|
||||
|
||||
@@ -702,6 +705,31 @@ public class UserStoreImpl implements UserStore {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<Boolean, Set<Ownership>> getExistingQualificationsForRoleDefinition(RoleDefinition roleToCheck) {
|
||||
final Set<Ownership> ownerships = new HashSet<>();
|
||||
for (User user : getUsers()) {
|
||||
try {
|
||||
for (Role role : getRolesFromUser(user.getName())) {
|
||||
if (!role.getRoleDefinition().equals(roleToCheck)) {
|
||||
// wrong role
|
||||
continue;
|
||||
}
|
||||
if (role.getQualifiedForTenant() == null && role.getQualifiedForUser() == null) {
|
||||
// wildcard rule exists -> return A=true
|
||||
return new Pair<>(true, null);
|
||||
} else {
|
||||
ownerships.add(new OwnershipImpl(role.getQualifiedForUser(), role.getQualifiedForTenant()));
|
||||
}
|
||||
}
|
||||
} catch (UserManagementException e) {
|
||||
// user did not exist -> should not happen
|
||||
logger.log(Level.SEVERE, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
return new Pair<>(false, ownerships);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Role> getRolesFromUser(String username) throws UserManagementException {
|
||||
if (users.get(username) == null) {
|
||||
|
||||
+2
-4
@@ -8,7 +8,7 @@ import org.osgi.framework.BundleContext;
|
||||
import org.osgi.framework.ServiceRegistration;
|
||||
import org.osgi.util.tracker.ServiceTracker;
|
||||
|
||||
import com.sap.sse.ServerStartupConstants;
|
||||
import com.sap.sse.ServerInfo;
|
||||
import com.sap.sse.mongodb.MongoDBService;
|
||||
import com.sap.sse.security.AccessControlStore;
|
||||
import com.sap.sse.security.PreferenceConverterRegistrationManager;
|
||||
@@ -39,7 +39,7 @@ public class Activator implements BundleActivator {
|
||||
public void start(BundleContext bundleContext) throws Exception {
|
||||
Activator.context = bundleContext;
|
||||
logger.info("Creating user store");
|
||||
final String defaultTenantName = System.getProperty(UserStore.DEFAULT_TENANT_NAME_PROPERTY_NAME, ServerStartupConstants.SERVER_NAME);
|
||||
final String defaultTenantName = System.getProperty(UserStore.DEFAULT_TENANT_NAME_PROPERTY_NAME, ServerInfo.getName());
|
||||
final UserStoreImpl userStore = new UserStoreImpl(defaultTenantName);
|
||||
AccessControlStoreImpl accessControlStore = new AccessControlStoreImpl(userStore);
|
||||
accessControlStoreRegistration = context.registerService(AccessControlStore.class.getName(),
|
||||
@@ -65,8 +65,6 @@ public class Activator implements BundleActivator {
|
||||
for (UserGroup group : userStore.getUserGroups()) {
|
||||
securityService.migrateOwnership(group, SecuredSecurityTypes.getAllInstances());
|
||||
}
|
||||
securityService.assumeOwnershipMigrated(SecuredSecurityTypes.ACCESS_CONTROL_LIST.getName(),
|
||||
SecuredSecurityTypes.getAllInstances());
|
||||
securityService.assumeOwnershipMigrated(SecuredSecurityTypes.ROLE_DEFINITION.getName(),
|
||||
SecuredSecurityTypes.getAllInstances());
|
||||
securityService.assumeOwnershipMigrated(SecuredSecurityTypes.SERVER.getName(),
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.sap.sse.security;
|
||||
|
||||
import com.sap.sse.security.shared.RolePrototype;
|
||||
|
||||
/**
|
||||
* Implementations provide {@link RolePrototype} instances to be automatically created in the system if a role with the
|
||||
* specific ID does not already exist. Implementations need to be registered as OSGi service.
|
||||
*/
|
||||
public interface RolePrototypeProvider {
|
||||
|
||||
RolePrototype getRolePrototype();
|
||||
|
||||
}
|
||||
@@ -149,11 +149,6 @@ public interface SecurityService extends ReplicableWithObjectInputStream<Replica
|
||||
|
||||
void logout();
|
||||
|
||||
/**
|
||||
* @param validationBaseURL if <code>null</code>, no validation will be attempted
|
||||
*/
|
||||
SecurityUser createSimpleUser(String username, String email, String password, String fullName, String company, String validationBaseURL) throws UserManagementException, MailException, UserGroupManagementException;
|
||||
|
||||
/**
|
||||
* @param validationBaseURL if <code>null</code>, no validation will be attempted
|
||||
*/
|
||||
@@ -285,6 +280,12 @@ public interface SecurityService extends ReplicableWithObjectInputStream<Replica
|
||||
*/
|
||||
String getPreference(String username, String key);
|
||||
|
||||
/**
|
||||
* Gets a preference object. Always returns null if there is no converter associated with the given key -> see
|
||||
* {@link #registerPreferenceConverter(String, PreferenceConverter)}.
|
||||
*/
|
||||
<T> T getPreferenceObject(String username, String key);
|
||||
|
||||
/**
|
||||
* @return all preferences of the given user
|
||||
*/
|
||||
@@ -377,4 +378,14 @@ public interface SecurityService extends ReplicableWithObjectInputStream<Replica
|
||||
void setOwnershipIfNotSet(QualifiedObjectIdentifier identifier, UserGroup defaultTenant);
|
||||
|
||||
UserGroup getDefaultTenantForCurrentUser();
|
||||
|
||||
/**
|
||||
* When a user adds permissions to a role, he needs to hold the permissions for all existing qualifications. This
|
||||
* method checks all given permissions for all existing qualifications of the given role.
|
||||
*
|
||||
* @return {@code true} if the current user holds all given meta permissions for all existing qualifications of the
|
||||
* given role.
|
||||
*/
|
||||
boolean hasUserAllWildcardPermissionsForAlreadyRealizedQualifications(RoleDefinition role,
|
||||
Iterable<WildcardPermission> permissionsToCheck);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,9 @@ import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.sap.sse.common.Named;
|
||||
import com.sap.sse.common.Util.Pair;
|
||||
import com.sap.sse.security.shared.Account;
|
||||
import com.sap.sse.security.shared.Ownership;
|
||||
import com.sap.sse.security.shared.Role;
|
||||
import com.sap.sse.security.shared.RoleDefinition;
|
||||
import com.sap.sse.security.shared.SecurityUser;
|
||||
@@ -230,4 +232,12 @@ public interface UserStore extends UserGroupProvider, Named {
|
||||
* Ensures that the predefined role definitions, particularly the "admin" and the "user" role, exist.
|
||||
*/
|
||||
void ensureDefaultRolesExist();
|
||||
|
||||
/**
|
||||
* @return a pair with: <br/>
|
||||
* If A is true, at least one user has an unqualified version of the {@link #roleToCheck} (without tenant or
|
||||
* user qualification). In this case, B is null.<br/>
|
||||
* If A is false, B contains all the ownerships of {@link #roleToCheck}
|
||||
*/
|
||||
Pair<Boolean, Set<Ownership>> getExistingQualificationsForRoleDefinition(RoleDefinition roleToCheck);
|
||||
}
|
||||
|
||||
@@ -7,16 +7,21 @@ import java.util.logging.Logger;
|
||||
|
||||
import org.osgi.framework.BundleActivator;
|
||||
import org.osgi.framework.BundleContext;
|
||||
import org.osgi.framework.ServiceReference;
|
||||
import org.osgi.framework.ServiceRegistration;
|
||||
import org.osgi.util.tracker.ServiceTracker;
|
||||
import org.osgi.util.tracker.ServiceTrackerCustomizer;
|
||||
|
||||
import com.sap.sse.mail.MailService;
|
||||
import com.sap.sse.replication.Replicable;
|
||||
import com.sap.sse.security.AccessControlStore;
|
||||
import com.sap.sse.security.RolePrototypeProvider;
|
||||
import com.sap.sse.security.SecurityService;
|
||||
import com.sap.sse.security.UserStore;
|
||||
import com.sap.sse.security.UsernamePasswordRealm;
|
||||
import com.sap.sse.security.shared.HasPermissionsProvider;
|
||||
import com.sap.sse.security.shared.RoleDefinition;
|
||||
import com.sap.sse.security.shared.RolePrototype;
|
||||
import com.sap.sse.util.ClearStateTestSupport;
|
||||
import com.sap.sse.util.ServiceTrackerFactory;
|
||||
|
||||
@@ -37,6 +42,8 @@ public class Activator implements BundleActivator {
|
||||
*/
|
||||
private static UserStore testUserStore;
|
||||
private static AccessControlStore testAccessControlStore;
|
||||
|
||||
private ServiceTracker<RolePrototypeProvider, RolePrototypeProvider> rolePrototypeProviderTracker;
|
||||
|
||||
public static void setTestStores(UserStore theTestUserStore, AccessControlStore theTestAccessControlStore) {
|
||||
testUserStore = theTestUserStore;
|
||||
@@ -84,6 +91,33 @@ public class Activator implements BundleActivator {
|
||||
context.registerService(ClearStateTestSupport.class.getName(), securityService, null);
|
||||
Logger.getLogger(Activator.class.getName()).info("Security Service registered.");
|
||||
}
|
||||
|
||||
private void createRoleDefinitionsFromPrototypes(BundleContext bundleContext, UserStore userStore) {
|
||||
rolePrototypeProviderTracker = new ServiceTracker<>(
|
||||
bundleContext, RolePrototypeProvider.class, /* customizer */ new ServiceTrackerCustomizer<RolePrototypeProvider, RolePrototypeProvider>() {
|
||||
@Override
|
||||
public RolePrototypeProvider addingService(ServiceReference<RolePrototypeProvider> reference) {
|
||||
final RolePrototypeProvider service = context.getService(reference);
|
||||
final RolePrototype rolePrototype = service.getRolePrototype();
|
||||
final RoleDefinition potentiallyExistingRoleDefinition = userStore.getRoleDefinition(rolePrototype.getId());
|
||||
if (potentiallyExistingRoleDefinition == null) {
|
||||
userStore.createRoleDefinition(rolePrototype.getId(), rolePrototype.getName(), rolePrototype.getPermissions());
|
||||
}
|
||||
return service;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void modifiedService(ServiceReference<RolePrototypeProvider> reference,
|
||||
RolePrototypeProvider service) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removedService(ServiceReference<RolePrototypeProvider> reference,
|
||||
RolePrototypeProvider service) {
|
||||
}
|
||||
});
|
||||
rolePrototypeProviderTracker.open();
|
||||
}
|
||||
|
||||
private void waitForUserStoreService(BundleContext bundleContext) {
|
||||
context = bundleContext;
|
||||
@@ -100,6 +134,7 @@ public class Activator implements BundleActivator {
|
||||
final UserStore userStore = tracker.waitForService(0);
|
||||
final AccessControlStore accessControlStore = accessControlStoreTracker.waitForService(0);
|
||||
logger.info("Obtained UserStore service "+userStore);
|
||||
createRoleDefinitionsFromPrototypes(bundleContext, userStore);
|
||||
createAndRegisterSecurityService(bundleContext, userStore, accessControlStore);
|
||||
} catch (InterruptedException e) {
|
||||
logger.log(Level.SEVERE, "Interrupted while waiting for UserStore service", e);
|
||||
@@ -114,6 +149,10 @@ public class Activator implements BundleActivator {
|
||||
* @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
|
||||
*/
|
||||
public void stop(BundleContext bundleContext) throws Exception {
|
||||
if (rolePrototypeProviderTracker != null) {
|
||||
rolePrototypeProviderTracker.close();
|
||||
rolePrototypeProviderTracker = null;
|
||||
}
|
||||
if (registration != null) {
|
||||
registration.unregister();
|
||||
}
|
||||
|
||||
@@ -75,7 +75,6 @@ import org.scribe.model.Token;
|
||||
import org.scribe.oauth.OAuthService;
|
||||
|
||||
import com.sap.sse.ServerInfo;
|
||||
import com.sap.sse.ServerStartupConstants;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.Util.Pair;
|
||||
import com.sap.sse.common.mail.MailException;
|
||||
@@ -265,7 +264,7 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat
|
||||
final User adminUser = createSimpleUser(ADMIN_USERNAME, "nobody@sapsailing.com",
|
||||
ADMIN_DEFAULT_PASSWORD,
|
||||
/* fullName */ null, /* company */ null, Locale.ENGLISH, /* validationBaseURL */ null,
|
||||
getDefaultTenant());
|
||||
null);
|
||||
|
||||
apply(s -> s.internalSetOwnership(
|
||||
SecuredSecurityTypes.USER.getQualifiedObjectIdentifier(ADMIN_USERNAME), ADMIN_USERNAME, null,
|
||||
@@ -368,10 +367,7 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat
|
||||
}
|
||||
|
||||
public UserGroup getDefaultTenantForUser(User user) {
|
||||
if (getCurrentUser() == null) {
|
||||
return null;
|
||||
}
|
||||
UserGroup specificTenant = getCurrentUser().getDefaultTenant(ServerInfo.getName());
|
||||
UserGroup specificTenant = user.getDefaultTenant(ServerInfo.getName());
|
||||
if (specificTenant == null) {
|
||||
String defaultTenantName = getDefaultTenantNameForUsername(user.getName());
|
||||
specificTenant = getUserGroupByName(defaultTenantName);
|
||||
@@ -381,6 +377,9 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat
|
||||
|
||||
@Override
|
||||
public UserGroup getDefaultTenantForCurrentUser() {
|
||||
if (SecurityUtils.getSecurityManager() != null && getCurrentUser() == null) {
|
||||
return null;
|
||||
}
|
||||
return getDefaultTenantForUser(getCurrentUser());
|
||||
}
|
||||
|
||||
@@ -688,22 +687,17 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat
|
||||
return userStore.getUserByEmail(email);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserImpl createSimpleUser(final String username, final String email, String password, String fullName,
|
||||
String company, final String validationBaseURL) throws UserManagementException, MailException, UserGroupManagementException {
|
||||
return createSimpleUser(username, email, password, fullName, company, null, validationBaseURL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserImpl createSimpleUser(final String username, final String email, String password, String fullName,
|
||||
String company, Locale locale, final String validationBaseURL)
|
||||
throws UserManagementException, MailException, UserGroupManagementException {
|
||||
return createSimpleUser(username, email, password, fullName, company, /* locale */ null, validationBaseURL,
|
||||
return createSimpleUser(username, email, password, fullName, company, locale, validationBaseURL,
|
||||
getDefaultTenantForCurrentUser());
|
||||
}
|
||||
|
||||
|
||||
private UserImpl createSimpleUser(final String username, final String email, String password, String fullName,
|
||||
String company, Locale locale, final String validationBaseURL, UserGroup tenantOwner) throws UserManagementException, MailException, UserGroupManagementException {
|
||||
String company, Locale locale, final String validationBaseURL, UserGroup userOwner)
|
||||
throws UserManagementException, MailException, UserGroupManagementException {
|
||||
logger.info("Creating user "+username);
|
||||
if (userStore.getUserByName(username) != null) {
|
||||
logger.warning("User "+username+" already exists");
|
||||
@@ -733,7 +727,8 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat
|
||||
|
||||
// the new user becomes its owner to ensure the user role is correctly working
|
||||
// the default tenant is the owning tenant to allow users having admin role for a specific server tenant to also be able to delete users
|
||||
accessControlStore.setOwnership(SecuredSecurityTypes.USER.getQualifiedObjectIdentifier(username), result, getDefaultTenant(), username);
|
||||
accessControlStore.setOwnership(SecuredSecurityTypes.USER.getQualifiedObjectIdentifier(username), result,
|
||||
userOwner, username);
|
||||
// the new user becomes the owning user of its own specific tenant which initially only contains the new user
|
||||
accessControlStore.setOwnership(SecuredSecurityTypes.USER_GROUP.getQualifiedObjectIdentifier(tenant.getId().toString()), result, tenant, tenant.getName());
|
||||
|
||||
@@ -1541,7 +1536,7 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat
|
||||
}
|
||||
if (checkCreateObjectOnServer) {
|
||||
SecurityUtils.getSubject().checkPermission(SecuredSecurityTypes.SERVER
|
||||
.getStringPermissionForObjects(ServerActions.CREATE_OBJECT, ServerStartupConstants.SERVER_NAME));
|
||||
.getStringPermissionForObjects(ServerActions.CREATE_OBJECT, ServerInfo.getName()));
|
||||
}
|
||||
SecurityUtils.getSubject()
|
||||
.checkPermission(identifier.getStringPermission(DefaultActions.CREATE));
|
||||
@@ -1870,4 +1865,26 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat
|
||||
final List<String> alreadyMigrated = getMigrationInfoForKey(permissions);
|
||||
alreadyMigrated.add(typeName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasUserAllWildcardPermissionsForAlreadyRealizedQualifications(RoleDefinition role,
|
||||
Iterable<WildcardPermission> permissionsToCheck) {
|
||||
Pair<Boolean, Set<Ownership>> qualificationsToCheck = userStore.getExistingQualificationsForRoleDefinition(role);
|
||||
final Iterable<Ownership> effectiveQualificationsToCheck = Boolean.TRUE.equals(qualificationsToCheck.getA())
|
||||
? Collections.singletonList(null)
|
||||
: qualificationsToCheck.getB();
|
||||
for (WildcardPermission permission : permissionsToCheck) {
|
||||
for (Ownership ownership : effectiveQualificationsToCheck) {
|
||||
if (!hasCurrentUserMetaPermission(permission, ownership)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getPreferenceObject(String username, String key) {
|
||||
return userStore.getPreferenceObject(username, key);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.sap.sse.security.jaxrs.api;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.FormParam;
|
||||
import javax.ws.rs.GET;
|
||||
@@ -119,7 +121,8 @@ public class SecurityResource extends AbstractSecurityResource {
|
||||
username, () -> {
|
||||
try {
|
||||
final String validationBaseURL = getEmailValidationBaseURL(uriInfo);
|
||||
getService().createSimpleUser(username, email, password, fullName, company, validationBaseURL);
|
||||
getService().createSimpleUser(username, email, password, fullName, company, Locale.ENGLISH,
|
||||
validationBaseURL);
|
||||
SecurityUtils.getSubject().login(new UsernamePasswordToken(username, password));
|
||||
return respondWithAccessTokenForUser(username);
|
||||
} catch (UserManagementException | MailException | UserGroupManagementException e) {
|
||||
|
||||
@@ -15,7 +15,7 @@ public class ServerInfo {
|
||||
private static final Logger logger = Logger.getLogger(ServerInfo.class.getName());
|
||||
|
||||
public static String getBuildVersion() {
|
||||
String version = "Unknown or Development (" + ServerStartupConstants.SERVER_NAME + ")";
|
||||
String version = "Unknown or Development (" + getName() + ")";
|
||||
File versionfile = new File(ServerStartupConstants.JETTY_HOME + File.separator + "version.txt");
|
||||
if (versionfile.exists()) {
|
||||
try (BufferedReader bufferedReader = new BufferedReader(new FileReader(versionfile))) {
|
||||
|
||||
@@ -91,11 +91,13 @@ This role implies the "*" permission. It should ideally be used with a tenant qu
|
||||
|
||||
### User "admin"
|
||||
|
||||
### Role for Anonymous Users
|
||||
On fresh instances that do not use a shared UserStore, the user "admin" is automatically created if no users exist yet on this instance. This user has the unqualified "admin" permission associated, which means this user has the permission to do everything. On event servers, this user is typically used to give permissions to specific event admin user.
|
||||
|
||||
### User "<all>"
|
||||
|
||||
When a user has not yet been authenticated, certain actions still need to be allowed for such users, in particular viewing public events or creating a new user during a sign-up activity. One approach may be to simply not request permissions for such actions. This way, all sessions would be able to perform such actions. The downside: in no server configuration would it be possible to limit access to those actions. For example, if one wanted to set up a private server instance where user sign-up is to be allowed only for administrators of that server then this could not be solved by role and permission configuration.
|
||||
|
||||
It would be more flexible if there was a role that all anonymous users implicitly have. This role could then by default imply the permissions that we want anonymous users to have. If a special server set-up demands changes then this could easily be solved by modifying the permissions of this role.
|
||||
To allow specific permissions to get associated on specific servers, a user named "<all>" has been introduced. Any permissions/roles associated with this user are evaluated for any user (including non-authenticated ones).
|
||||
|
||||
## Administration of Authorization
|
||||
|
||||
@@ -118,25 +120,25 @@ Tenant ownership has implications only for the application of roles that are qua
|
||||
|
||||
### Implementation of Sharing Data Objects with Public
|
||||
|
||||
In general, sharing a data object with the public should just be granting the “view” permission to everyone. For this purpose, the ``<ALL>`` user can be assigned the corresponding ``READ`` permission. This in return allows everybody that knows the link where one can view the data object to view it.
|
||||
To make sharing as convenient as possible it was decided to use groups and their owned objects as basic unit of sharing. This means all domain objects of specific types owned by a common group (e.g. an event specific group) are visible to all users having a specific "viewer" role for that group.
|
||||
|
||||
Alternatively, an access control list (ACL) could be assigned to the object, using a ``null`` group assignment, thereby making this ACL applicable to all users, regardless of their group memberships. The ACL will also be checked for permission requests by not authenticated users.
|
||||
A domain specific "viewer" role needs to be defined, having the required "READ" permissions for all object types that are open to a broader audience (e.g. events, but no advanced data). For Sailing Analytics, this role is named "sailing_viewer". The definition of a generic "viewer" role (e.g. having the permission "*:READ") is impracticabledue to the fact that this also allows read access to internal configuration objects and other administrative data. This means the associated permissions need to be explicitly enumerated.
|
||||
|
||||
The remaining challenge is the application of the permissions necessary for *all* objects in the *scope* that is to be shared. A user it typically not interested in sharing only the bare *Event* object but would rather want to grant public access to everything *belonging to* the event, including all leaderboard groups, leaderboards, regattas and tracked races.
|
||||
To share the domain objects being owned by a group, you need to to associate the specific "viewer" role to a user with a qualification by the owning group. To make an event public, you can instead give the qualified "viewer" role to the "<all>" user.
|
||||
|
||||
The problem with this is that this object set is dynamic. It changes as users add object to or remove them from the scope of the event. For example, if another race is being added after the event has been shared, a reasonable behavior seems to be that the new race shall also be shared together with everything else belonging to the overarching event that has been shared before. This is similar to a "setgid" bit in a Unix file system.
|
||||
To make the role work as intended, the domain objects of an event to be shared need to be consistently owned by a specific group. To make this as convenient as possible and to be able to fix wrong ownerships, batch-changing the group owner of related domain objects needs to be able. For Sailing Analytics, this means batch-changing needs to be possible under the following conditions:
|
||||
* Batch-changing the ownership of a LeaderboardGroup in general includes Leaderboards/Regattas and their associated TrackedRaces.
|
||||
* For event series defined by a LeaderboardGroup having an overall leaderboard, a batch change will also catch all events associated to the series.
|
||||
* Batch-changing the ownership of an event means, all LeaderboardGroups not having an overall leaderboard are included with their included Leaderboards/Regattas and associated TrackedRaces.
|
||||
* Such a batch change in general does not include Competitors/Boats that are used by a regatta. This is explicitly intended because Competitors/Boats are typically used on different event/regatta contexts that are not directly related. In some cases this could in fact be useful which means a user needs to have the choice of optionally include Competitors/Boats.
|
||||
|
||||
For this to work we will need observable object relationships which can trigger rules for permission propagation along the relationships. While we agreed that the permission checking mechanism must not need to analyze object relationships, changes in relationships may lead to updates in permissions.
|
||||
In addition we need to detect object associations with inconsistent group ownerships. In most cases domain objects getting associated in one of the hierarchies mentioned above, also need to inherit the group ownership if the associated "higher level" object is the first. For Sailing analytics, this means a leaderboard being added to a leaderboard group will inherit the leaderboard group's group owner if the leaderboard is not yet associated to another leaderboard group (assumed the current user is permitted to change the leaderboard's ownership). This ensures that e.g. leaderboards being created in the personal group of a user will get "fixed" when adding them to an event-context. The user needs to be informed when auto-adjusting group ownerships on association as well as cases where such an auto update is not performed. It needs to be discussed how the REST APIs can do this properly.
|
||||
|
||||
Examples: when a user links a leaderboard group to an event that is visible to a larger set of users than the leaderboard group, the user may receive a hint suggesting to update the leaderboard group's permissions to match those of the event into which it is just being linked. Similarly, if a leaderboard is added to a leaderboard group, the user may want to propagate the leaderboard group's visibility to that of the leaderboard just added to the group.
|
||||
|
||||
Possible approach: make directed permission propagation a property on associations between domain types that users can configure. Such associations then need to be observed by the permission propagation mechanism, and when links are added (what if they are removed?) the permission propagation is triggered. Furthermore, permission propagation may need to be triggered when the permissions on a source object of a permission propagation change, in order to support transitive permission propagation, e.g., when a LeaderboardGroup is added to an Event, so that also all Leaderboards and Regattas and TrackedRaces reachable from that LeaderboardGroup receive the same permission update as the LeaderboardGroup itself.
|
||||
|
||||
Propagation rule candidates: Event---LeaderboardGroup; LeaderboardGroup---Leaderboard; Leaderboard---Regatta; Leaderboard---TrackedRace.
|
||||
|
||||
This is separated from promoting this public data object (e.g. event) on the official SAP site. This would have to be a separate list which can be edited by e.g. media admins. This would however only link to the source, because otherwise all promoted material would have to be imported to the archive.
|
||||
An additional requirement is the possibility to extract an event/series from a bigger sharing context (e.g. club group). This means a new group needs to be created and the domain objects's group owner need to be changed to this new group by following the structure explained above. In addition, all role associations that exist for the source group need to be copied as equivalent role associations for the newly created group to ensure that all users having specific permissions for the source group will keep their respective permissions. ACLs do not need to be adjusted due to the fact that those are directly associated to the respective objects that will keep their object IDs.
|
||||
|
||||
The downside of a role-based approach for sharing is that sharing domain objects requires every single user to have the specific role associated for the owning group. It is currently not possible to give roles to all users of a group. In contrast to this, sharing domain object with the public is easy.
|
||||
|
||||
In addition of sharing domain objects of a whole event, there is also the need to share single races. This can easily solved by using ACLs on the respective TrackedRace instance. Be aware that this approach is not conveniently usable for a hierarchy of domain objects due to the fact that this would require ACL rules to be implicitly batch-updated for all objects of a hierarchy. In addition, a consistent view of ACLs in a hierarchy can not easily created in contrast of just finding differences in the group ownerships of domain objects.
|
||||
|
||||
## Permissions in Frontend
|
||||
|
||||
|
||||
Reference in New Issue
Block a user