diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/security/SecuredDomainType.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/security/SecuredDomainType.java index 360a855e0f2..828c56e6e09 100644 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/security/SecuredDomainType.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/security/SecuredDomainType.java @@ -6,6 +6,7 @@ import java.util.Set; import com.sap.sse.security.shared.HasPermissions; import com.sap.sse.security.shared.impl.HasPermissionsImpl; +import com.sap.sse.security.shared.impl.SecuredSecurityTypes; /** * Logical domain types in the "sailing" domain that require the user to have certain permissions @@ -49,32 +50,27 @@ public class SecuredDomainType extends HasPermissionsImpl { TrackedRaceActions.ALL_ACTIONS); public static enum TrackedRaceActions implements Action { - CAN_REPLAY_DURING_LIVE_RACES, DETAIL_TIMER; + CAN_REPLAY_DURING_LIVE_RACES, + DETAIL_TIMER, + EXPORT; - private static final Action[] ALL_ACTIONS = new Action[] { CAN_REPLAY_DURING_LIVE_RACES, DefaultActions.READ, + private static final Action[] ALL_ACTIONS = DefaultActions.plus(CAN_REPLAY_DURING_LIVE_RACES, DETAIL_TIMER, + EXPORT); + + public static final Action[] MUTATION_ACTIONS = new Action[] { EXPORT, DefaultActions.DELETE, DefaultActions.CREATE, DefaultActions.UPDATE, DefaultActions.CHANGE_OWNERSHIP, DefaultActions.CHANGE_ACL }; - - public static final Action[] MUTATION_ACTIONS = new Action[] { DefaultActions.CREATE, DefaultActions.UPDATE, - DefaultActions.CHANGE_OWNERSHIP, DefaultActions.CHANGE_ACL }; } - public static enum CompetitorAndBoatActions implements Action { - READ_PUBLIC; - - public static final Action[] READ_AND_READ_PUBLIC_ACTIONS = new Action[] { READ_PUBLIC, DefaultActions.READ }; - - private static final Action[] ALL_ACTIONS = new Action[] { READ_PUBLIC, DefaultActions.READ, + private static final Action[] ALL_ACTIONS_FOR_COMPETITOR_AND_BOAT = new Action[] { + SecuredSecurityTypes.PublicReadableActions.READ_PUBLIC, DefaultActions.READ, DefaultActions.CREATE, DefaultActions.UPDATE, DefaultActions.CHANGE_OWNERSHIP, DefaultActions.CHANGE_ACL }; - public static final Action[] MUTATION_ACTIONS = new Action[] { DefaultActions.CREATE, DefaultActions.UPDATE, - DefaultActions.CHANGE_OWNERSHIP, DefaultActions.CHANGE_ACL }; - }; + public static final HasPermissions COMPETITOR = new SecuredDomainType("COMPETITOR", + ALL_ACTIONS_FOR_COMPETITOR_AND_BOAT); - public static final HasPermissions COMPETITOR = new SecuredDomainType("COMPETITOR", CompetitorAndBoatActions.ALL_ACTIONS); - - public static final HasPermissions BOAT = new SecuredDomainType("BOAT", CompetitorAndBoatActions.ALL_ACTIONS); + public static final HasPermissions BOAT = new SecuredDomainType("BOAT", ALL_ACTIONS_FOR_COMPETITOR_AND_BOAT); public static final HasPermissions MEDIA_TRACK = new SecuredDomainType("MEDIA_TRACK"); diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/JSONServiceImpl.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/JSONServiceImpl.java index 8ca0f20829c..5530e121910 100755 --- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/JSONServiceImpl.java +++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/JSONServiceImpl.java @@ -35,7 +35,7 @@ public class JSONServiceImpl implements JSONService { private RaceRecord createRaceRecord(URL jsonURL, boolean loadLiveAndStoredURI, JSONObject jsonRaceEntry) throws URISyntaxException, IOException { RaceRecord raceRecord = new RaceRecord(jsonURL, regattaName, - (String) jsonRaceEntry.get("name"), (String) jsonRaceEntry.get("url"), + (String) jsonRaceEntry.get("name"), (String) jsonRaceEntry.get("url_html"), (String) jsonRaceEntry.get("params_url"), (String) jsonRaceEntry.get("id"), (String) jsonRaceEntry.get("tracking_starttime"), diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/communication/user/profile/GetCompetitorAction.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/communication/user/profile/GetCompetitorAction.java index 4b844f3532c..393aa032963 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/communication/user/profile/GetCompetitorAction.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/communication/user/profile/GetCompetitorAction.java @@ -5,11 +5,11 @@ import java.util.UUID; import com.google.gwt.core.shared.GwtIncompatible; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.CompetitorAndBoatStore; -import com.sap.sailing.domain.common.security.SecuredDomainType.CompetitorAndBoatActions; import com.sap.sailing.gwt.home.communication.SailingAction; import com.sap.sailing.gwt.home.communication.SailingDispatchContext; import com.sap.sailing.gwt.home.communication.event.SimpleCompetitorWithIdDTO; import com.sap.sse.gwt.dispatch.shared.exceptions.DispatchException; +import com.sap.sse.security.shared.impl.SecuredSecurityTypes; /** * {@link SailingAction} implementation to load competitor data to be shown on the qr code page for the @@ -40,7 +40,7 @@ public class GetCompetitorAction implements SailingAction { + leaderboardActionColumn.addAction(LeaderboardConfigImagesBarCell.ACTION_EDIT_SCORES, UPDATE, leaderboardDTO -> { String leaderboardEditingUrl = EntryPointWithSettingsLinkFactory .createLeaderboardEditingLink(leaderboardDTO.getName()); Window.open(leaderboardEditingUrl, "_blank", null); @@ -337,7 +337,8 @@ public class LeaderboardConfigPanel extends AbstractLeaderboardConfigPanel }); editCompetitorsDialog.show(); }); - leaderboardActionColumn.addAction(LeaderboardConfigImagesBarCell.ACTION_CONFIGURE_URL, leaderboardDTO -> { + leaderboardActionColumn.addAction(LeaderboardConfigImagesBarCell.ACTION_CONFIGURE_URL, READ, + leaderboardDTO -> { sailingService.getAvailableDetailTypesForLeaderboard(leaderboardDTO.getName(), null, new AsyncCallback>() { @@ -352,21 +353,21 @@ public class LeaderboardConfigPanel extends AbstractLeaderboardConfigPanel } }); }); - leaderboardActionColumn.addAction(LeaderboardConfigImagesBarCell.ACTION_EXPORT_XML, + leaderboardActionColumn.addAction(LeaderboardConfigImagesBarCell.ACTION_EXPORT_XML, READ, leaderboardDTO -> Window.open(UriUtils .fromString("/export/xml?domain=leaderboard&name=" + leaderboardDTO.getName()).asString(), "", null)); - leaderboardActionColumn.addAction(LeaderboardConfigImagesBarCell.ACTION_OPEN_COACH_DASHBOARD, + leaderboardActionColumn.addAction(LeaderboardConfigImagesBarCell.ACTION_OPEN_COACH_DASHBOARD, READ, leaderboardDTO -> { Map dashboardURLParameters = new HashMap(); dashboardURLParameters.put("leaderboardName", leaderboardDTO.getName()); Window.open(EntryPointLinkFactory.createDashboardLink(dashboardURLParameters), "", null); }); - leaderboardActionColumn.addAction(LeaderboardConfigImagesBarCell.ACTION_SHOW_REGATTA_LOG, + leaderboardActionColumn.addAction(LeaderboardConfigImagesBarCell.ACTION_SHOW_REGATTA_LOG, READ, leaderboardDTO -> showRegattaLog()); - leaderboardActionColumn.addAction(LeaderboardConfigImagesBarCell.ACTION_CREATE_PAIRINGLIST, + leaderboardActionColumn.addAction(LeaderboardConfigImagesBarCell.ACTION_CREATE_PAIRINGLIST, UPDATE, this::createPairingListTemplate); - leaderboardActionColumn.addAction(LeaderboardConfigImagesBarCell.ACTION_PRINT_PAIRINGLIST, + leaderboardActionColumn.addAction(LeaderboardConfigImagesBarCell.ACTION_PRINT_PAIRINGLIST, READ, this::openPairingListEntryPoint); final DialogConfig config = EditOwnershipDialog.create( diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TrackedRacesListComposite.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TrackedRacesListComposite.java index 6c030f4c85b..f22bc8814ca 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TrackedRacesListComposite.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TrackedRacesListComposite.java @@ -15,6 +15,7 @@ import com.google.gwt.user.client.ui.HorizontalPanel; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.RegattaNameAndRaceName; import com.sap.sailing.domain.common.dto.RaceDTO; +import com.sap.sailing.domain.common.security.SecuredDomainType; import com.sap.sailing.gwt.ui.client.RegattaRefresher; import com.sap.sailing.gwt.ui.client.RegattasDisplayer; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; @@ -184,6 +185,7 @@ public class TrackedRacesListComposite extends AbstractTrackedRacesListComposite boolean canUpdateAll = true; boolean canDeleteAll = true; + boolean canExportAll = true; for (RaceDTO race : selectedRaces) { if (!userService.hasPermission(race, DefaultActions.UPDATE)) { canUpdateAll = false; @@ -191,11 +193,15 @@ public class TrackedRacesListComposite extends AbstractTrackedRacesListComposite if (!userService.hasPermission(race, DefaultActions.DELETE)) { canDeleteAll = false; } + if (!userService.hasPermission(race, SecuredDomainType.TrackedRaceActions.EXPORT)) { + canExportAll = false; + } + } btnSetDelayToLive.setEnabled(canUpdateAll); btnRemoveRace.setEnabled(canDeleteAll); btnUntrack.setEnabled(canUpdateAll); - btnExport.setEnabled(numberOfItemsSelected > 0); + btnExport.setEnabled(canExportAll); } } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindPanel.java index 8cdd644b67c..fafb5435cc4 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindPanel.java @@ -55,6 +55,7 @@ import com.sap.sailing.domain.common.WindSource; import com.sap.sailing.domain.common.WindSourceType; import com.sap.sailing.domain.common.dto.RaceDTO; import com.sap.sailing.domain.common.impl.WindSourceImpl; +import com.sap.sailing.domain.common.security.SecuredDomainType; import com.sap.sailing.gwt.ui.adminconsole.WindImportResult.RaceEntry; import com.sap.sailing.gwt.ui.client.RegattaRefresher; import com.sap.sailing.gwt.ui.client.RegattasDisplayer; @@ -75,7 +76,10 @@ import com.sap.sse.gwt.client.async.MarkedAsyncCallback; import com.sap.sse.gwt.client.celltable.BaseCelltable; import com.sap.sse.gwt.client.celltable.RefreshableMultiSelectionModel; import com.sap.sse.gwt.client.dialog.DataEntryDialog.DialogCallback; +import com.sap.sse.security.shared.HasPermissions.DefaultActions; +import com.sap.sse.security.shared.dto.UserDTO; import com.sap.sse.security.ui.client.UserService; +import com.sap.sse.security.ui.client.UserStatusEventHandler; /** * Displays a table of currently tracked races. The user can configure whether a race @@ -120,6 +124,18 @@ public class WindPanel extends FormPanel implements RegattasDisplayer, WindShowe * will be forwarded to those objects contained in this collection. */ private final Set containedRegattaDisplayers; + + private CaptionPanel expeditionImportPanel; + + private CaptionPanel gribImportPanel; + + private CaptionPanel nmeaImportPanel; + + private CaptionPanel bravoImportPanel; + + private CaptionPanel igtimiImportPanel; + + private CaptionPanel expeditionAllInOneImporterPanel; public WindPanel(final SailingServiceAsync sailingService, UserService userService, AsyncActionsExecutor asyncActionsExecutor, ErrorReporter errorReporter, final StringMessages stringMessages) { @@ -163,6 +179,7 @@ public class WindPanel extends FormPanel implements RegattasDisplayer, WindShowe @Override public void onSelectionChange(SelectionChangeEvent event) { updateWindDisplay(); + updateVisibilityStateForPanels(); } }); windCaptionPanel = new CaptionPanel(stringMessages.wind()); @@ -278,19 +295,73 @@ public class WindPanel extends FormPanel implements RegattasDisplayer, WindShowe rawWindFixesTable.addColumnSortHandler(columnSortHandler); rawWindFixesTable.getColumnSortList().push(timeColumn); windFixesDisplayPanel.add(rawWindFixesTable); - mainPanel.add(createExpeditionWindImportPanel()); - mainPanel.add(createGribWindImportPanel()); - mainPanel.add(createNmeaWindImportPanel()); - mainPanel.add(createBravoWindImportPanel()); - mainPanel.add(createIgtimiWindImportPanel(mainPanel)); + + expeditionImportPanel = createExpeditionWindImportPanel(); + gribImportPanel = createGribWindImportPanel(); + nmeaImportPanel = createNmeaWindImportPanel(); + bravoImportPanel = createBravoWindImportPanel(); + igtimiImportPanel = igtimiImportPanel(mainPanel); + mainPanel.add(expeditionImportPanel); + mainPanel.add(gribImportPanel); + mainPanel.add(nmeaImportPanel); + mainPanel.add(bravoImportPanel); + mainPanel.add(igtimiImportPanel); + final Pair expeditionAllInOneRootAndImportPanel = createExpeditionAllInOneImportPanel(regattaRefresher); - mainPanel.add(expeditionAllInOneRootAndImportPanel.getA()); + expeditionAllInOneImporterPanel = expeditionAllInOneRootAndImportPanel.getA(); + mainPanel.add(expeditionAllInOneImporterPanel); containedRegattaDisplayers.add(expeditionAllInOneRootAndImportPanel.getB()); + updateVisibilityStateForPanels(); + + this.userService.addUserStatusEventHandler(new UserStatusEventHandler() { + @Override + public void onUserStatusChange(UserDTO user, boolean preAuthenticated) { + updateVisibilityStateForPanels(); + } + }); + // refresh regattas for this panel manually regattaRefresher.fillRegattas(); } + private void updateVisibilityStateForPanels() { + Set selectedRaces = refreshableRaceSelectionModel.getSelectedSet(); + if (selectedRaces.isEmpty()) { + // they could potentially hit all races, we don't have enough information to check here + expeditionImportPanel.setVisible(true); + gribImportPanel.setVisible(true); + nmeaImportPanel.setVisible(true); + bravoImportPanel.setVisible(true); + igtimiImportPanel.setVisible(true); + } else { + boolean canUpdateAll = true; + for (RaceDTO race : refreshableRaceSelectionModel.getSelectedSet()) { + if (!userService.hasPermission(race, DefaultActions.UPDATE)) { + canUpdateAll = false; + } + } + expeditionImportPanel.setVisible(canUpdateAll); + gribImportPanel.setVisible(canUpdateAll); + nmeaImportPanel.setVisible(canUpdateAll); + bravoImportPanel.setVisible(canUpdateAll); + igtimiImportPanel.setVisible(canUpdateAll); + } + + boolean canCreateEvent = userService.hasCurrentUserPermissionToCreateObjectOfType(SecuredDomainType.EVENT); + boolean canCreateRegatta = userService.hasCurrentUserPermissionToCreateObjectOfType(SecuredDomainType.REGATTA); + boolean canCreateRace = userService + .hasCurrentUserPermissionToCreateObjectOfType(SecuredDomainType.TRACKED_RACE); + boolean canCreateLeaderboard = userService + .hasCurrentUserPermissionToCreateObjectOfType(SecuredDomainType.LEADERBOARD); + expeditionAllInOneImporterPanel + .setVisible(canCreateEvent && canCreateLeaderboard && canCreateRace && canCreateRegatta); + } + + private CaptionPanel igtimiImportPanel(VerticalPanel mainPanel) { + return createIgtimiWindImportPanel(mainPanel); + } + private CaptionPanel createIgtimiWindImportPanel(VerticalPanel mainPanel) { CaptionPanel igtimiWindImportRootPanel = new CaptionPanel(stringMessages.igtimiWindImport()); VerticalPanel contentPanel = new VerticalPanel(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_es.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_es.properties index 274af8a509d..e5cda5da0a7 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_es.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_es.properties @@ -236,7 +236,7 @@ raceIsKnownToStartUpwind=La carrera empieza con ceñida events=Eventos pairingList=Lista de emparejamientos pairingLists=Listas de emparejamientos -pairingListCreationInfo=Inicialmente se calculará una plantilla que contenga solo los números de los competidores y que pueda aplicarse a otras clasificaciones. Si el recuento de competidores no es divisible por recuento de embarcaciones sin remanente, se introducirán marcadores de posición en blanco en la lista de emparejamientos. \ Además, si el recuento de competidores difiere del recuento de competidores registrado en la clasificación real, la lista de emparejamientos no podrá aplicarse a los registros de la carrera. \ El recuento de flights, que se utiliza para generar una lista de emparejamientos, está definido por el número de flights de la clasificación que no son carreras de medalla. \ Cuando se utilizan repeticiones de flight, se pueden repetir asignaciones de flights (por ejemplo, 15 flights con 3 repeticiones de flight: 5 asignaciones de flight únicas que se repetirán 3 veces cada una de ellas). \ Un factor de cambio de embarcación equivalente a 0 da como resultado una asignación bien distribuida de competidores y embarcaciones, considerando que el máximo (número de flights) provoca menos cambios de embarcación, dado que los competidores navegan en la misma embarcación para dos flotas, uno detrás de otro, en diferentes flights. +pairingListCreationInfo=Inicialmente se calculará una plantilla que contenga solo los números de los competidores y que pueda aplicarse a otras clasificaciones. Si el recuento de competidores no es divisible por recuento de embarcaciones sin remanente, se introducirán marcadores de posición en blanco en la lista de emparejamientos. \ Además, si el recuento de competidores difiere del recuento de competidores registrado en la clasificación real, la lista de emparejamientos no podrá aplicarse a los registros de la carrera. \ El recuento de flights, que se utiliza para generar una lista de emparejamientos, está definido por el número de flights de la clasificación que no son carreras de medalla. \ Cuando se utilizan repeticiones de flight, se pueden repetir asignaciones de flights (por ejemplo, 15 flights con 3 repeticiones de flight: 5 asignaciones de flight únicas que se repetirán 3 veces cada una de ellas). \ Un factor de cambio de embarcación equivalente a 0 da como resultado una asignación bien distribuida de competidores y embarcaciones, considerando que el máximo (número de flights) provoca menos cambios de embarcación aumentando las posibilidades de que un competidor navegue con la misma embarcación en dos carreras contiguas a través de un límite de flights. setCompetitors=Fije el recuento de competidores: setBoatChangeFactor=Fije el factor de cambio de embarcación: seriesHint=Seleccione una o más series que deberían estar en una lista de emparejamientos: diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_fr.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_fr.properties index 8b1e5b8fc5e..a17a3d18ace 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_fr.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_fr.properties @@ -236,7 +236,7 @@ raceIsKnownToStartUpwind=La course a commencé avec une portion de parcours au v events=Manifestations pairingList=Liste d''appariement pairingLists=Listes d''appariement -pairingListCreationInfo=Tout d''abord, un modèle contenant uniquement le nombre de concurrents sera calculé. Il pourra être appliqué à d''autres palmarès. Si la division du nombre de concurrents par le nombre de bateaux ne permet pas d''obtenir un nombre entier, des espaces réservés vides seront ajoutés à la liste d''appariement. \ En outre, si le nombre de concurrents ne correspond pas au nombre de concurrents inscrits au palmarès actuel, la liste d''appariement ne pourra pas être appliquée aux journaux de course. \ Le nombre de flights (matchs) utilisé pour générer une liste d''appariement est défini par tous les flights du palmarès, hors courses médaillées. \ En cas de répétition des flights, les affectations de flights peuvent être répétées (par exemple, 15 flights avec 3 répétitions : 5 affectations de flights uniques, et chacun sera répété 3 fois). \ Un facteur de changement de bateau équivalent à 0 permet une répartition correcte des concurrents sur les bateaux, alors que le nombre de flights maximum implique moins de changements de bateaux car les concurrents naviguent sur un même bateau pour deux flottes l''une après l''autre sur des flights différents. +pairingListCreationInfo=Tout d''abord, un modèle contenant uniquement le nombre de concurrents sera calculé. Il pourra être appliqué à d''autres palmarès. Si la division du nombre de concurrents par le nombre de bateaux ne permet pas d''obtenir un nombre entier, des espaces réservés vides seront ajoutés à la liste d''appariement. \ En outre, si le nombre de concurrents ne correspond pas au nombre de concurrents inscrits au palmarès actuel, la liste d''appariement ne pourra pas être appliquée aux journaux de course. \ Le nombre de flights (matchs) utilisé pour générer une liste d''appariement est défini par tous les flights du palmarès, hors courses médaillées. \ En cas de répétition des flights, les affectations de flights peuvent être répétées (par exemple, 15 flights avec 3 répétitions : 5 affectations de flights uniques, et chacun sera répété 3 fois). \ Un facteur de changement de bateau équivalent à 0 permet une répartition correcte des concurrents sur les bateaux, alors que le nombre de flights maximum implique moins de changements de bateaux en augmentant les chances qu''un concurrent navigue sur un même bateau pour deux courses consécutives trans-flight. setCompetitors=Veuillez définir le nombre de concurrents : setBoatChangeFactor=Veuillez définir le facteur de changement de bateau : seriesHint=Veuillez sélectionner une ou plusieurs séries à ajouter à une liste de paires : diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties index 3a7a972ccdb..3f33426b419 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties @@ -236,7 +236,7 @@ raceIsKnownToStartUpwind=アップウィンドレグでレーススタート events=イベント pairingList=対戦表 pairingLists=対戦表 -pairingListCreationInfo=まず最初に、競技者番号のみが含まれていて、他のリーダーボードに適用できるテンプレートがすべて計算されます。競技者カウントが艇カウントで割り切れない場合、空のプレースホルダーが対戦表に入力されます。 \ また、競技者カウントが実際のリーダーボードにある登録済の競技者カウントと等しくない場合、対戦表をレースログに適用することはできません。 \ 対戦表を生成するために使用されるフライトのカウントは、リーダーボードにあるメダルレースでないフライトの回数によって設定されます。 \ フライト繰返の使用時に、フライト割当を繰り返すことができます (例: 3 つのフライト繰返がある 15 回のフライトでは、別々の 5 つのフライト割当でそれぞれが 3 回繰り返されます)。 \ 艇変更係数が 0 に等しければ、艇への競技者の割当がうまく配分されることにつながり、一方、最大 (フライト回数) になると、別のフライトだが互いに前後している 2 つのフリートで競技者が同一艇で帆走するため艇変更が少なくなることにつながります。 +pairingListCreationInfo=まず最初に、競技者番号のみが含まれていて、他のリーダーボードに適用できるテンプレートがすべて計算されます。競技者カウントが艇カウントで割り切れない場合、空のプレースホルダーが対戦表に入力されます。 \ また、競技者カウントが実際のリーダーボードにある登録済の競技者カウントと等しくない場合、対戦表をレースログに適用することはできません。 \ 対戦表を生成するために使用されるフライトのカウントは、リーダーボードにあるメダルレースでないフライトの回数によって設定されます。 \ フライト繰返の使用時に、フライト割当を繰り返すことができます (例: 3 つのフライト繰返がある 15 回のフライトでは、別々の 5 つのフライト割当でそれぞれが 3 回繰り返されます)。 \ 艇変更係数が 0 に等しければ、艇への競技者の割当がうまく配分されることにつながり、一方、最大 (フライト回数) になると、フライトの境界を超えて隣接している 2 つのレースで競技者が同一艇で帆走する可能性が高くなることで艇変更がより少なくなります。 setCompetitors=競技者カウントを設定してください: setBoatChangeFactor=艇変更係数を設定してください: seriesHint=対戦表にあることが必要なシリーズを 1 つ以上選択してください: diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_pt.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_pt.properties index 59029a09c5c..cf86eb72a0d 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_pt.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_pt.properties @@ -236,7 +236,7 @@ raceIsKnownToStartUpwind=A corrida começa com perna de contravento events=Eventos pairingList=Lista de pares pairingLists=Listas de pares -pairingListCreationInfo=Primeiro será calculado um modelo que contém somente os números dos competidores e pode ser aplicado a outros painéis de classificação. Se contagem de competidores não for divisível pela contagem de barcos sem restante, os caracteres de preenchimento em branco serão inseridos na lista de pares. \ Além disso, se a contagem de competidores for diferente da contagem de competidores registrado no painel de classificação real, a lista de pares não pode ser aplicada aos registros das corridas. \ A contagem de voos, que é utilizado para gerar uma lista de pares, é definida pelo número de voos no painel de classificação que não sejam corridas para medalha. \ Ao utilizar repetições de voo, as atribuições de voo podem ser repetidas (por exemplo, 15 voos com 3 repetições de voo: 5 atribuições de voo únicas e cada uma será repetida 3 vezes). \ Um fator de mudança de barco igual a 0 leva a uma atribuição bem distribuída de competidores a barcos, sendo que o (número de voos) máximo leva a menos mudanças de barco devido a competidores que navegam no mesmo barco para duas flotilhas atrás uma da outra em diferentes voos. +pairingListCreationInfo=Primeiro será calculado um modelo que contém somente os números dos competidores e pode ser aplicado a outros painéis de classificação. Se contagem de competidores não for divisível pela contagem de barcos sem restante, os caracteres de preenchimento em branco serão inseridos na lista de pares. \ Além disso, se a contagem de competidores for diferente da contagem de competidores registrados no painel de classificação real, a lista de pares não pode ser aplicada aos registros das corridas. \ A contagem de voos, que é utilizada para gerar uma lista de pares, é definida pelo número de voos no painel de classificação que não sejam corridas para medalha. \ Ao utilizar repetições de voo, as atribuições de voo podem ser repetidas (por exemplo, 15 voos com 3 repetições de voo: 5 atribuições de voo únicas e cada uma será repetida 3 vezes). \ Um fator de mudança de barco igual a 0 leva a uma atribuição bem distribuída de competidores a barcos, sendo que o (número de voos) máximo leva a menos mudanças de barco aumentando as hipóteses de um competidor navegar no mesmo barco em duas corridas contínuas além do limite do voo. setCompetitors=Defina a contagem dos competidores: setBoatChangeFactor=Defina o fator de mudança de barco: seriesHint=Selecione uma ou mais séries que devam estar em uma lista de pares: diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties index 499e3e885da..9c35cd1fbe6 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties @@ -236,7 +236,7 @@ raceIsKnownToStartUpwind=Гонка начинается против ветра events=События pairingList=Список пар pairingLists=Списки пар -pairingListCreationInfo=Первым будет рассчитан шаблон, который содержит только номера участников и может быть применен к другим таблицам лидеров. Если число участников не делится на число лодок без остатка, в списке пар будут пустые места. \ Если число участников не равно числу зарегистрированных участников в фактической таблице лидеров, список пар не может быть применен к журналам гонки. \ Для генерирования списка пар используется число всех флайтов таблицы лидеров, не являющихся гонками на медали. \ Если используются повторы флайтов, то присвоения флайтов могут повторяться (например, 15 флайтов с 3 повторами: 5 уникальных присвоений флайтов, каждый из которых повторяется 3 раза). \ Результатом нулевого коэффициента смены лодки является хорошее распределение присвоений участников лодкам, то есть максимум (число флайтов) ведет к меньшему числу смен лодок, поскольку участники плывут на одной и той же лодке двух флотов друг за другом в разных флайтах. +pairingListCreationInfo=Первым будет рассчитан шаблон, который содержит только номера участников и может быть применен к другим таблицам лидеров. Если число участников не делится на число лодок без остатка, в списке пар будут пустые места. \ Если число участников не равно числу зарегистрированных участников в фактической таблице лидеров, список пар не может быть применен к журналам гонки. \ Для генерирования списка пар используется число всех флайтов таблицы лидеров, не являющихся гонками на медали. \ Если используются повторы флайтов, то присвоения флайтов могут повторяться (например, 15 флайтов с 3 повторами: 5 уникальных присвоений флайтов, каждый из которых повторяется 3 раза). \ Результатом нулевого коэффициента смены лодки является хорошее распределение участников по лодкам, то есть максимум (число флайтов) ведет к меньшему числу смен лодок, и это увеличивает шансы, что участник будет плыть на одной и той же лодке в двух смежных гонках до окончания флайта. setCompetitors=Укажите число участников: setBoatChangeFactor=Укажите коэффициент смены лодки: seriesHint=Выберите не менее одной серии для списка пар: diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties index ae1a34fcdb3..5f49c7daaf4 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties @@ -236,7 +236,7 @@ raceIsKnownToStartUpwind=比赛轮次从迎风航段开始 events=活动 pairingList=配对清单 pairingLists=配对清单 -pairingListCreationInfo=首先,将计算仅包含参赛队数并且可以应用到其他积分榜的模板。如果参赛队数量无法被船只数量除尽,则在配对清单中输入空占位符。\ 此外,如果参赛队数量不等于实际积分榜中注册的参赛队数量,配对清单无法应用到比赛轮次日志。\ 用于生成配对清单的航程数由积分榜中不是奖牌轮的航程数定义。\ 使用航程重复时,可以重复航程分配(例如,有 3 个航程重复的 15 个航程:5 个独特的航程分配,每个重复 3 次)。\ 船只更换系数等于 0 导致向船只平均分配参赛队,而最大(航程数)导致船只更换次数减少,因为不同航程中背后两个船队的参赛队在同一船只上航行。 +pairingListCreationInfo=首先,将计算仅包含参赛队数并且可以应用到其他积分榜的模板。如果参赛队数量无法被船只数量除尽,则在配对清单中输入空占位符。\ 此外,如果参赛队数量不等于实际积分榜中注册的参赛队数量,配对清单无法应用到比赛轮次日志。\ 用于生成配对清单的航程数由积分榜中不是奖牌轮的航程数定义。\ 使用航程重复时,可以重复航程分配(例如,有 3 个航程重复的 15 个航程:5 个独特的航程分配,每个重复 3 次)。\ 船只更换系数等于 0 导致向船只平均分配参赛队,而最大值(航程数)通过提高参赛队在两个连续的比赛轮次驾驶同一船只驶过航程边界的机会来减少换船次数。 setCompetitors=请设置参赛队数量: setBoatChangeFactor=请设置船只更换系数: seriesHint=请选择一场或多场应位于配对清单的系列赛: diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index 39175fc5a56..4951a759925 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -5511,6 +5511,9 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S @Override public RegattaLogDTO getRegattaLog(String leaderboardName) throws DoesNotHaveRegattaLogException { RegattaLogDTO result = null; + Leaderboard l = getService().getLeaderboardByName(leaderboardName); + getSecurityService().checkCurrentUserReadPermission(l); + RegattaLog regattaLog = getRegattaLogInternal(leaderboardName); if (regattaLog != null) { List entries = new ArrayList<>(); @@ -5750,7 +5753,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S boolean filterCompetitorsWithoutBoat) { Iterable result; CompetitorAndBoatStore competitorStore = getService().getBaseDomainFactory().getCompetitorAndBoatStore(); - final HasPermissions.Action[] requiredActionsForRead = SecuredDomainType.CompetitorAndBoatActions.READ_AND_READ_PUBLIC_ACTIONS; + final HasPermissions.Action[] requiredActionsForRead = SecuredSecurityTypes.PublicReadableActions.READ_AND_READ_PUBLIC_ACTIONS; if (filterCompetitorsWithBoat == false && filterCompetitorsWithoutBoat == false) { @SuppressWarnings("unchecked") Iterable competitors = (Iterable) competitorStore.getAllCompetitors(); @@ -5921,7 +5924,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S @Override public Iterable getAllBoats() { - final HasPermissions.Action[] requiredActionsForRead = SecuredDomainType.CompetitorAndBoatActions.READ_AND_READ_PUBLIC_ACTIONS; + final HasPermissions.Action[] requiredActionsForRead = SecuredSecurityTypes.PublicReadableActions.READ_AND_READ_PUBLIC_ACTIONS; Iterable result = getSecurityService().mapAndFilterByAnyExplicitPermissionForCurrentUser( SecuredDomainType.BOAT, requiredActionsForRead, getService().getBaseDomainFactory().getCompetitorAndBoatStore().getBoats(), @@ -5933,7 +5936,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S public Iterable getStandaloneBoats() { List result = new ArrayList<>(); getSecurityService().filterObjectsWithAnyPermissionForCurrentUser(SecuredDomainType.BOAT, - SecuredDomainType.CompetitorAndBoatActions.READ_AND_READ_PUBLIC_ACTIONS, + SecuredSecurityTypes.PublicReadableActions.READ_AND_READ_PUBLIC_ACTIONS, getService().getBaseDomainFactory().getCompetitorAndBoatStore().getStandaloneBoats(), filteredObject -> result.add(convertToBoatDTO(filteredObject))); return result; @@ -6401,7 +6404,12 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S myTrackedRaces.add(trackedRace); } } else { - trackedRaces = getAllTrackedRaces(); + trackedRaces = new ArrayList<>(); + for (DynamicTrackedRace race : getAllTrackedRaces()) { + if (getSecurityService().hasCurrentUserUpdatePermission(race)) { + ((List) trackedRaces).add(race); + } + } } Map numberOfWindFixesImportedPerRace = new HashMap(); for (Account account : igtimiConnectionFactory.getAllAccounts()) { diff --git a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/CompetitorTrackWithEstimationDataJsonSerializer.java b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/CompetitorTrackWithEstimationDataJsonSerializer.java index 4c6bd073996..30a2695b5d8 100644 --- a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/CompetitorTrackWithEstimationDataJsonSerializer.java +++ b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/CompetitorTrackWithEstimationDataJsonSerializer.java @@ -10,7 +10,6 @@ import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.Waypoint; import com.sap.sailing.domain.common.WindSource; import com.sap.sailing.domain.common.WindSourceType; -import com.sap.sailing.domain.common.security.SecuredDomainType; import com.sap.sailing.domain.common.tracking.GPSFixMoving; import com.sap.sailing.domain.maneuverdetection.impl.ManeuverDetectorImpl; import com.sap.sailing.domain.maneuverdetection.impl.TrackTimeInfo; @@ -23,6 +22,7 @@ import com.sap.sse.common.TimePoint; import com.sap.sse.common.Util; import com.sap.sse.common.impl.MillisecondsDurationImpl; import com.sap.sse.security.SecurityService; +import com.sap.sse.security.shared.impl.SecuredSecurityTypes; /** * @@ -84,7 +84,7 @@ public class CompetitorTrackWithEstimationDataJsonSerializer extends AbstractTra result.put(BYCOMPETITOR, byCompetitorJson); for (Competitor competitor : trackedRace.getRace().getCompetitors()) { if (securityService.hasCurrentUserAnyExplicitPermissions(competitor, - SecuredDomainType.CompetitorAndBoatActions.READ_AND_READ_PUBLIC_ACTIONS)) { + SecuredSecurityTypes.PublicReadableActions.READ_AND_READ_PUBLIC_ACTIONS)) { ManeuverDetectorImpl maneuverDetector = new ManeuverDetectorImpl(trackedRace, competitor); TrackTimeInfo trackTimeInfo = maneuverDetector.getTrackTimeInfo(); if (trackTimeInfo != null) { diff --git a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/RegattaJsonSerializer.java b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/RegattaJsonSerializer.java index 87c1a7a6a0b..2c9eba81b05 100644 --- a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/RegattaJsonSerializer.java +++ b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/RegattaJsonSerializer.java @@ -7,9 +7,9 @@ import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.base.Series; -import com.sap.sailing.domain.common.security.SecuredDomainType; import com.sap.sailing.server.gateway.serialization.JsonSerializer; import com.sap.sse.security.SecurityService; +import com.sap.sse.security.shared.impl.SecuredSecurityTypes; public class RegattaJsonSerializer implements JsonSerializer { public static final String FIELD_NAME = "name"; @@ -65,7 +65,7 @@ public class RegattaJsonSerializer implements JsonSerializer { JSONArray competitorsJson = new JSONArray(); for (Competitor competitor : regatta.getAllCompetitors()) { if (securityService.hasCurrentUserAnyExplicitPermissions(competitor, - SecuredDomainType.CompetitorAndBoatActions.READ_AND_READ_PUBLIC_ACTIONS)) { + SecuredSecurityTypes.PublicReadableActions.READ_AND_READ_PUBLIC_ACTIONS)) { competitorsJson.add(competitorSerializer.serialize(competitor)); } } @@ -75,7 +75,7 @@ public class RegattaJsonSerializer implements JsonSerializer { JSONArray boatsJson = new JSONArray(); for (Boat boat : regatta.getAllBoats()) { if (securityService.hasCurrentUserAnyExplicitPermissions(boat, - SecuredDomainType.CompetitorAndBoatActions.READ_AND_READ_PUBLIC_ACTIONS)) { + SecuredSecurityTypes.PublicReadableActions.READ_AND_READ_PUBLIC_ACTIONS)) { boatsJson.add(boatSerializer.serialize(boat)); } } diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/AddEntryToRaceLogJsonPostServlet.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/AddEntryToRaceLogJsonPostServlet.java index 2734a8abd8f..4f734d7facd 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/AddEntryToRaceLogJsonPostServlet.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/AddEntryToRaceLogJsonPostServlet.java @@ -13,6 +13,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang.StringEscapeUtils; +import org.apache.shiro.SecurityUtils; import org.json.simple.JSONObject; import org.json.simple.JSONValue; import org.json.simple.parser.ParseException; @@ -36,6 +37,7 @@ import com.sap.sailing.server.gateway.serialization.racelog.tracking.DeviceIdent import com.sap.sailing.server.gateway.serialization.racelog.tracking.impl.PlaceHolderDeviceIdentifierJsonHandler; import com.sap.sailing.server.interfaces.RacingEventService; import com.sap.sse.common.TypeBasedServiceFinder; +import com.sap.sse.security.shared.HasPermissions.DefaultActions; public class AddEntryToRaceLogJsonPostServlet extends AbstractJsonHttpServlet { private static final long serialVersionUID = 7704668926551060433L; @@ -69,7 +71,6 @@ public class AddEntryToRaceLogJsonPostServlet extends AbstractJsonHttpServlet { String.format("Missing parameter '%s'.", RaceLogServletConstants.PARAMS_LEADERBOARD_NAME)); return; } - //SecurityUtils.getSubject().checkPermission(SecuredDomainType.LEADERBOARD.getStringPermissionForObjects(DefaultActions.UPDATE, leaderboardName)); String raceColumnName = request.getParameter(RaceLogServletConstants.PARAMS_RACE_COLUMN_NAME); if (raceColumnName == null) { @@ -93,6 +94,8 @@ public class AddEntryToRaceLogJsonPostServlet extends AbstractJsonHttpServlet { response.sendError(HttpServletResponse.SC_NOT_FOUND, "Leaderboard "+StringEscapeUtils.escapeHtml(leaderboardName)+" not found."); return; } + SecurityUtils.getSubject() + .checkPermission(leaderboard.getIdentifier().getStringPermission(DefaultActions.UPDATE)); RaceColumn raceColumn = leaderboard.getRaceColumnByName(raceColumnName); if (raceColumn == null) { diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/CompetitorsAndBoatsJsonExportServlet.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/CompetitorsAndBoatsJsonExportServlet.java index d20f911b5ba..6ce508ee463 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/CompetitorsAndBoatsJsonExportServlet.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/CompetitorsAndBoatsJsonExportServlet.java @@ -6,6 +6,7 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.apache.shiro.SecurityUtils; import org.json.simple.JSONArray; import org.json.simple.JSONObject; @@ -15,10 +16,12 @@ import com.sap.sailing.domain.base.Fleet; import com.sap.sailing.domain.base.RaceColumn; import com.sap.sailing.domain.common.racelog.RaceLogServletConstants; import com.sap.sailing.domain.leaderboard.Leaderboard; +import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.server.gateway.AbstractJsonHttpServlet; import com.sap.sailing.server.gateway.serialization.impl.CompetitorAndBoatJsonSerializer; import com.sap.sailing.server.interfaces.RacingEventService; import com.sap.sse.common.Util.Pair; +import com.sap.sse.security.shared.HasPermissions.DefaultActions; public class CompetitorsAndBoatsJsonExportServlet extends AbstractJsonHttpServlet { private static final long serialVersionUID = 4510175441769759252L; @@ -49,6 +52,10 @@ public class CompetitorsAndBoatsJsonExportServlet extends AbstractJsonHttpServle response.sendError(HttpServletResponse.SC_NOT_FOUND, "No such leaderboard found."); return; } + + SecurityUtils.getSubject() + .checkPermission(leaderboard.getIdentifier().getStringPermission(DefaultActions.READ)); + RaceColumn raceColumn = leaderboard.getRaceColumnByName(raceColumnName); if (raceColumn == null) { response.sendError(HttpServletResponse.SC_NOT_FOUND, "No such race column found."); @@ -61,6 +68,13 @@ public class CompetitorsAndBoatsJsonExportServlet extends AbstractJsonHttpServle } CompetitorAndBoatJsonSerializer competitorsAndBoatsSerializer = CompetitorAndBoatJsonSerializer.create(); JSONArray result = new JSONArray(); + final TrackedRace trackedRace = raceColumn.getTrackedRace(fleet); + + if (trackedRace != null) { + SecurityUtils.getSubject() + .checkPermission(trackedRace.getIdentifier().getStringPermission(DefaultActions.READ)); + } + for (Competitor competitor : leaderboard.getCompetitors(raceColumn, fleet)) { Boat boat = leaderboard.getBoatOfCompetitor(competitor, raceColumn, fleet); JSONObject serializeCompetitorAndBoat = competitorsAndBoatsSerializer.serialize(new Pair(competitor, boat)); diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/CompetitorsJsonExportServlet.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/CompetitorsJsonExportServlet.java index 3328fc56ecc..3b5ddc7808c 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/CompetitorsJsonExportServlet.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/CompetitorsJsonExportServlet.java @@ -6,6 +6,7 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.apache.shiro.SecurityUtils; import org.json.simple.JSONArray; import com.sap.sailing.domain.base.Competitor; @@ -16,6 +17,7 @@ import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.server.gateway.AbstractJsonHttpServlet; import com.sap.sailing.server.gateway.serialization.impl.CompetitorJsonSerializer; import com.sap.sailing.server.interfaces.RacingEventService; +import com.sap.sse.security.shared.HasPermissions.DefaultActions; public class CompetitorsJsonExportServlet extends AbstractJsonHttpServlet { private static final long serialVersionUID = 4510175441769759252L; @@ -46,6 +48,9 @@ public class CompetitorsJsonExportServlet extends AbstractJsonHttpServlet { response.sendError(HttpServletResponse.SC_NOT_FOUND, "No such leaderboard found."); return; } + SecurityUtils.getSubject() + .checkPermission(leaderboard.getIdentifier().getStringPermission(DefaultActions.READ)); + RaceColumn raceColumn = leaderboard.getRaceColumnByName(raceColumnName); if (raceColumn == null) { response.sendError(HttpServletResponse.SC_NOT_FOUND, "No such race column found."); diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/CourseJsonExportServlet.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/CourseJsonExportServlet.java index 50800b23917..00bb8874819 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/CourseJsonExportServlet.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/CourseJsonExportServlet.java @@ -6,6 +6,7 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.apache.shiro.SecurityUtils; import org.json.simple.JSONObject; import com.sap.sailing.domain.base.Course; @@ -21,6 +22,7 @@ import com.sap.sailing.server.gateway.serialization.coursedata.impl.GateJsonSeri import com.sap.sailing.server.gateway.serialization.coursedata.impl.MarkJsonSerializer; import com.sap.sailing.server.gateway.serialization.coursedata.impl.WaypointJsonSerializer; import com.sap.sailing.server.interfaces.RacingEventService; +import com.sap.sse.security.shared.HasPermissions.DefaultActions; public class CourseJsonExportServlet extends AbstractJsonHttpServlet { @@ -52,6 +54,9 @@ public class CourseJsonExportServlet extends AbstractJsonHttpServlet { response.sendError(HttpServletResponse.SC_NOT_FOUND, "No such leaderboard found."); return; } + SecurityUtils.getSubject() + .checkPermission(leaderboard.getIdentifier().getStringPermission(DefaultActions.READ)); + RaceColumn raceColumn = leaderboard.getRaceColumnByName(raceColumnName); if (raceColumn == null) { response.sendError(HttpServletResponse.SC_NOT_FOUND, "No such race column found."); @@ -65,6 +70,8 @@ public class CourseJsonExportServlet extends AbstractJsonHttpServlet { TrackedRace trackedRace = raceColumn.getTrackedRace(fleet); JSONObject result; if (trackedRace != null) { + SecurityUtils.getSubject() + .checkPermission(trackedRace.getIdentifier().getStringPermission(DefaultActions.READ)); Course course = raceColumn.getRaceDefinition(fleet).getCourse(); CourseBaseJsonSerializer serializer = new CourseBaseJsonSerializer( new WaypointJsonSerializer( diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/MarkPositionsJsonExportServlet.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/MarkPositionsJsonExportServlet.java index 9c341d8d40f..4074843293e 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/MarkPositionsJsonExportServlet.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/MarkPositionsJsonExportServlet.java @@ -7,6 +7,7 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.apache.shiro.SecurityUtils; import org.json.simple.JSONArray; import com.sap.sailing.domain.base.Fleet; @@ -24,6 +25,7 @@ import com.sap.sailing.server.interfaces.RacingEventService; import com.sap.sse.common.Distance; import com.sap.sse.common.Util; import com.sap.sse.common.impl.MillisecondsTimePoint; +import com.sap.sse.security.shared.HasPermissions.DefaultActions; public class MarkPositionsJsonExportServlet extends AbstractJsonHttpServlet { @@ -31,7 +33,6 @@ public class MarkPositionsJsonExportServlet extends AbstractJsonHttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - String leaderboardName = request.getParameter(RaceLogServletConstants.PARAMS_LEADERBOARD_NAME); if (leaderboardName == null) { response.sendError(HttpServletResponse.SC_BAD_REQUEST, @@ -60,6 +61,8 @@ public class MarkPositionsJsonExportServlet extends AbstractJsonHttpServlet { response.sendError(HttpServletResponse.SC_NOT_FOUND, "No such leaderboard found."); return; } + SecurityUtils.getSubject() + .checkPermission(leaderboard.getIdentifier().getStringPermission(DefaultActions.READ)); RaceColumn raceColumn = leaderboard.getRaceColumnByName(raceColumnName); if (raceColumn == null) { @@ -78,6 +81,9 @@ public class MarkPositionsJsonExportServlet extends AbstractJsonHttpServlet { JSONArray result = new JSONArray(); if (trackedRace != null) { + SecurityUtils.getSubject() + .checkPermission(trackedRace.getIdentifier().getStringPermission(DefaultActions.READ)); + MarkJsonSerializer markSerializer = new MarkJsonSerializer(); PositionJsonSerializer positionSerializer = new PositionJsonSerializer(); PositionedMarkJsonSerializer positionedMarkSerializer = new PositionedMarkJsonSerializer(markSerializer, positionSerializer); diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/RaceGroupJsonExportServlet.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/RaceGroupJsonExportServlet.java index 0e859d03356..da6d0625456 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/RaceGroupJsonExportServlet.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/RaceGroupJsonExportServlet.java @@ -9,6 +9,7 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.apache.shiro.SecurityUtils; import org.json.simple.JSONArray; import com.sap.sailing.domain.abstractlog.race.RaceLog; @@ -39,6 +40,7 @@ import com.sap.sailing.server.gateway.serialization.racegroup.impl.SeriesWithRow import com.sap.sailing.server.gateway.serialization.racegroup.impl.SeriesWithRowsOfRaceGroupSerializer; import com.sap.sailing.server.gateway.serialization.racelog.impl.RaceLogEventSerializer; import com.sap.sailing.server.gateway.serialization.racelog.impl.RaceLogSerializer; +import com.sap.sse.security.shared.HasPermissions.DefaultActions; public class RaceGroupJsonExportServlet extends AbstractJsonHttpServlet { private static final long serialVersionUID = 4510175441769759252L; @@ -73,9 +75,16 @@ public class RaceGroupJsonExportServlet extends AbstractJsonHttpServlet { final Set regattasForWhichRegattaLeaderboardsWereAdded = new HashSet<>(); for (Leaderboard leaderboard : getService().getLeaderboards().values()) { if (filterCourseArea.equals(leaderboard.getDefaultCourseArea())) { + SecurityUtils.getSubject() + .checkPermission(leaderboard.getIdentifier().getStringPermission(DefaultActions.READ)); if (leaderboard instanceof RegattaLeaderboard && !(leaderboard instanceof RegattaLeaderboardWithEliminations)) { result.add(serializer.serialize(raceGroupFactory.convert((RegattaLeaderboard) leaderboard))); - regattasForWhichRegattaLeaderboardsWereAdded.add(((RegattaLeaderboard) leaderboard).getRegatta()); + + final Regatta regatta = ((RegattaLeaderboard) leaderboard).getRegatta(); + SecurityUtils.getSubject() + .checkPermission(regatta.getIdentifier().getStringPermission(DefaultActions.READ)); + + regattasForWhichRegattaLeaderboardsWereAdded.add(regatta); } else if (leaderboard instanceof FlexibleLeaderboard) { result.add(serializer.serialize(raceGroupFactory.convert((FlexibleLeaderboard) leaderboard))); } diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/AbstractSailingServerResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/AbstractSailingServerResource.java index a50a475505c..56e4a5ccbd5 100755 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/AbstractSailingServerResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/AbstractSailingServerResource.java @@ -118,6 +118,9 @@ public abstract class AbstractSailingServerResource { * If the leaderboard can be resolved to a regatta, and the given secret is correct, return true */ protected boolean skipChecksDueToCorrectSecret(String leaderboardName, String secret) { + if (leaderboardName == null && secret == null) { + return false; + } Regatta regatta = getService().getRegattaByName(leaderboardName); if (regatta == null && secret != null) { logger.warning("Attempt to skip security checks using regatta secret \"" + secret + "\" for leaderboard \"" diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/BoatsResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/BoatsResource.java index e56537f0b42..11cf40fc78c 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/BoatsResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/BoatsResource.java @@ -14,6 +14,7 @@ import org.apache.commons.lang.StringEscapeUtils; import com.sap.sailing.domain.base.Boat; import com.sap.sailing.server.gateway.jaxrs.AbstractSailingServerResource; import com.sap.sailing.server.gateway.serialization.impl.BoatJsonSerializer; +import com.sap.sse.security.shared.impl.SecuredSecurityTypes; @Path("/v1/boats") public class BoatsResource extends AbstractSailingServerResource { @@ -32,7 +33,8 @@ public class BoatsResource extends AbstractSailingServerResource { } else { boolean skip = skipChecksDueToCorrectSecret(leaderboardName, regattaSecret); if (!skip) { - getSecurityService().checkCurrentUserReadPermission(boat); + getSecurityService().checkCurrentUserAnyExplicitPermissions(boat, + SecuredSecurityTypes.PublicReadableActions.READ_AND_READ_PUBLIC_ACTIONS); } BoatJsonSerializer boatJsonSerializer = BoatJsonSerializer.create(); String jsonString = boatJsonSerializer.serialize(boat).toJSONString(); diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/CompetitorsResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/CompetitorsResource.java index 83b40b5f716..7532f4523ec 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/CompetitorsResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/CompetitorsResource.java @@ -39,6 +39,7 @@ import com.sap.sailing.server.gateway.serialization.impl.TeamJsonSerializer; import com.sap.sailing.server.interfaces.RacingEventService; import com.sap.sse.filestorage.InvalidPropertiesException; import com.sap.sse.filestorage.OperationFailedException; +import com.sap.sse.security.shared.impl.SecuredSecurityTypes; @Path("/v1/competitors") public class CompetitorsResource extends AbstractSailingServerResource { @@ -77,19 +78,22 @@ public class CompetitorsResource extends AbstractSailingServerResource { public Response getCompetitor(@PathParam("competitorId") String competitorIdAsString, @QueryParam("secret") String secret, @QueryParam("leaderboardname") String leaderboardName) { Response response; - Competitor competitor = getService().getCompetitorAndBoatStore().getExistingCompetitorByIdAsString( - competitorIdAsString); + Competitor competitor = getService().getCompetitorAndBoatStore() + .getExistingCompetitorByIdAsString(competitorIdAsString); if (competitor == null) { response = Response.status(Status.NOT_FOUND) - .entity("Could not find a competitor with id '" + StringEscapeUtils.escapeHtml(competitorIdAsString) + "'.") + .entity("Could not find a competitor with id '" + StringEscapeUtils.escapeHtml(competitorIdAsString) + + "'.") .type(MediaType.TEXT_PLAIN).build(); } else { boolean skip = skipChecksDueToCorrectSecret(leaderboardName, secret); if (!skip) { - getSecurityService().checkCurrentUserReadPermission(competitor); + getSecurityService().checkCurrentUserAnyExplicitPermissions(competitor, + SecuredSecurityTypes.PublicReadableActions.READ_AND_READ_PUBLIC_ACTIONS); } String jsonString = getCompetitorJSON(competitor).toJSONString(); - response = Response.ok(jsonString).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build(); + response = Response.ok(jsonString).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8") + .build(); } return response; } diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResource.java index 3aa05d18ad0..de45a7784e6 100755 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResource.java @@ -975,9 +975,17 @@ public class LeaderboardsResource extends AbstractLeaderboardsResource { @QueryParam(RaceLogServletConstants.PARAMS_RACE_COLUMN_NAME) String raceColumnName, @QueryParam(RaceLogServletConstants.PARAMS_RACE_FLEET_NAME) String fleetName) throws MalformedURLException, IOException, InterruptedException { - SecurityUtils.getSubject().checkPermission(SecuredDomainType.LEADERBOARD.getStringPermissionForObject( - DefaultActions.UPDATE, getService().getLeaderboardByName(leaderboardName))); - return stopOrRemoveTrackedRace(leaderboardName, raceColumnName, fleetName, true); + Leaderboard leaderBoard = getService().getLeaderboardByName(leaderboardName); + Response result = null; + if(leaderBoard == null) { + result = Response.status(Status.NOT_FOUND) + .entity("Could not find a leaderboard with name '" + StringEscapeUtils.escapeHtml(leaderboardName) + "'.") + .type(MediaType.TEXT_PLAIN).build(); + } else { + getSecurityService().checkCurrentUserUpdatePermission(leaderBoard); + result = stopOrRemoveTrackedRace(leaderboardName, raceColumnName, fleetName, true); + } + return result; } @POST diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResourceV2.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResourceV2.java index d160b5df2c4..517e0929e1a 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResourceV2.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResourceV2.java @@ -42,7 +42,6 @@ import com.sap.sailing.domain.common.dto.LeaderboardDTO; import com.sap.sailing.domain.common.dto.LeaderboardEntryDTO; import com.sap.sailing.domain.common.dto.LeaderboardRowDTO; import com.sap.sailing.domain.common.dto.LegEntryDTO; -import com.sap.sailing.domain.common.security.SecuredDomainType; import com.sap.sailing.domain.common.sharding.ShardingType; import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.sharding.ShardingContext; @@ -54,6 +53,7 @@ import com.sap.sse.common.Duration; import com.sap.sse.common.TimePoint; import com.sap.sse.common.Util.Pair; import com.sap.sse.security.shared.HasPermissions.DefaultActions; +import com.sap.sse.security.shared.impl.SecuredSecurityTypes; @Path("/v2/leaderboards") public class LeaderboardsResourceV2 extends AbstractLeaderboardsResource { @@ -148,7 +148,7 @@ public class LeaderboardsResourceV2 extends AbstractLeaderboardsResource { List filteredCompetitorsFromBestToWorst = new ArrayList<>(); competitorsFromBestToWorst.forEach(competitor -> { if (SecurityUtils.getSubject().isPermitted(competitor.getIdentifier() - .getStringPermission(SecuredDomainType.CompetitorAndBoatActions.READ_PUBLIC)) + .getStringPermission(SecuredSecurityTypes.PublicReadableActions.READ_PUBLIC)) || SecurityUtils.getSubject() .isPermitted(competitor.getIdentifier().getStringPermission(DefaultActions.READ))) { filteredCompetitorsFromBestToWorst.add(competitor); @@ -178,7 +178,7 @@ public class LeaderboardsResourceV2 extends AbstractLeaderboardsResource { leaderboardDTO.competitors.forEach(competitor -> { if (SecurityUtils.getSubject() .isPermitted(competitor.getIdentifier() - .getStringPermission(SecuredDomainType.CompetitorAndBoatActions.READ_PUBLIC)) + .getStringPermission(SecuredSecurityTypes.PublicReadableActions.READ_PUBLIC)) || SecurityUtils.getSubject() .isPermitted(competitor.getIdentifier().getStringPermission(DefaultActions.READ))) { filteredCompetitors.add(competitor); diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/MarkRessource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/MarkRessource.java index fbd7dfecc41..be29c756b49 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/MarkRessource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/MarkRessource.java @@ -15,7 +15,9 @@ import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import org.apache.commons.lang.StringEscapeUtils; import org.apache.shiro.SecurityUtils; import org.json.simple.JSONArray; import org.json.simple.JSONObject; @@ -82,15 +84,23 @@ public class MarkRessource extends AbstractSailingServerResource { Mark mark = getService().getBaseDomainFactory().getOrCreateMark(markId, markName); String regattaName = (String) requestObject.get("regattaName"); Regatta regatta = getService().getRegattaByName(regattaName); - SecurityUtils.getSubject().checkPermission(regatta.getIdentifier().getStringPermission(DefaultActions.UPDATE)); - RegattaLog regattaLog = getRegattaLogInternal(regattaName); - RegattaLogDefineMarkEventImpl event = new RegattaLogDefineMarkEventImpl(MillisecondsTimePoint.now(), - getService().getServerAuthor(), MillisecondsTimePoint.now(), UUID.randomUUID(), mark); - regattaLog.add(event); - JSONObject answer = new JSONObject(); - answer.put("markId", markId.toString()); - return Response.ok(answer.toJSONString()).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8") - .build(); + Response response; + if (regatta == null) { + response = Response.status(Status.NOT_FOUND) + .entity("Could not find a regatta with name '" + StringEscapeUtils.escapeHtml(regattaName) + "'.") + .type(MediaType.TEXT_PLAIN).build(); + } else { + getSecurityService().checkCurrentUserUpdatePermission(regatta); + RegattaLog regattaLog = getRegattaLogInternal(regattaName); + RegattaLogDefineMarkEventImpl event = new RegattaLogDefineMarkEventImpl(MillisecondsTimePoint.now(), + getService().getServerAuthor(), MillisecondsTimePoint.now(), UUID.randomUUID(), mark); + regattaLog.add(event); + JSONObject answer = new JSONObject(); + answer.put("markId", markId.toString()); + response = Response.ok(answer.toJSONString()) + .header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build(); + } + return response; } @POST @@ -221,36 +231,46 @@ public class MarkRessource extends AbstractSailingServerResource { JSONObject requestObject = Helpers.toJSONObjectSafe(requestBody); String leaderboardName = (String) requestObject.get("leaderboardName"); - SecurityUtils.getSubject().checkPermission(SecuredDomainType.LEADERBOARD.getStringPermissionForTypeRelativeIdentifier( - DefaultActions.UPDATE, Leaderboard.getTypeRelativeObjectIdentifier(leaderboardName))); - String raceColumnName = (String) requestObject.get("raceColumnName"); - String fleetName = (String) requestObject.get("fleetName"); + Leaderboard leaderBoard = getService().getLeaderboardByName(leaderboardName); + Response response; + if (leaderBoard == null) { + response = Response.status(Status.NOT_FOUND).entity( + "Could not find a leaderboard with name '" + StringEscapeUtils.escapeHtml(leaderboardName) + "'.") + .type(MediaType.TEXT_PLAIN).build(); + } else { + SecurityUtils.getSubject().checkPermission( + SecuredDomainType.LEADERBOARD.getStringPermissionForTypeRelativeIdentifier(DefaultActions.UPDATE, + Leaderboard.getTypeRelativeObjectIdentifier(leaderboardName))); + String raceColumnName = (String) requestObject.get("raceColumnName"); + String fleetName = (String) requestObject.get("fleetName"); - JSONArray competitorsRaw = (JSONArray) requestObject.get("competitors"); - Map competitorsToRegister = new HashMap<>(); - for (int i = 0; i < competitorsRaw.size(); i++) { - JSONObject competitorRaw = (JSONObject) competitorsRaw.get(i); - String competitorId = (String) competitorRaw.get("competitorId"); - String boatId = (String) competitorRaw.get("boatId"); - Competitor competitor = getService().getCompetitorAndBoatStore() - .getExistingCompetitorByIdAsString(competitorId); - Boat boat = getService().getCompetitorAndBoatStore().getExistingBoatByIdAsString(boatId); - competitorsToRegister.put(competitor, boat); + JSONArray competitorsRaw = (JSONArray) requestObject.get("competitors"); + Map competitorsToRegister = new HashMap<>(); + for (int i = 0; i < competitorsRaw.size(); i++) { + JSONObject competitorRaw = (JSONObject) competitorsRaw.get(i); + String competitorId = (String) competitorRaw.get("competitorId"); + String boatId = (String) competitorRaw.get("boatId"); + Competitor competitor = getService().getCompetitorAndBoatStore() + .getExistingCompetitorByIdAsString(competitorId); + Boat boat = getService().getCompetitorAndBoatStore().getExistingBoatByIdAsString(boatId); + competitorsToRegister.put(competitor, boat); + } + RaceColumn raceColumn = getRaceColumn(leaderboardName, raceColumnName); + Fleet fleet = getFleetByName(raceColumn, fleetName); + Map competitorsRegisteredInRaceLog = new HashMap<>(); + for (final Entry e : raceColumn.getCompetitorsRegisteredInRacelog(fleet).entrySet()) { + competitorsRegisteredInRaceLog.put((CompetitorWithBoat) e.getKey(), e.getValue()); + } + final Iterable competitorSetToRemove = filterCompetitorDuplicates(competitorsToRegister, + competitorsRegisteredInRaceLog); + raceColumn.deregisterCompetitors(competitorSetToRemove, fleet); + // we assume that the competitors id of type Competitor here, so we need to find the corresponding boat + for (Entry competitorToRegister : competitorsToRegister.entrySet()) { + raceColumn.registerCompetitor(competitorToRegister.getKey(), competitorToRegister.getValue(), fleet); + } + response = Response.ok().header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build(); } - RaceColumn raceColumn = getRaceColumn(leaderboardName, raceColumnName); - Fleet fleet = getFleetByName(raceColumn, fleetName); - Map competitorsRegisteredInRaceLog = new HashMap<>(); - for (final Entry e : raceColumn.getCompetitorsRegisteredInRacelog(fleet).entrySet()) { - competitorsRegisteredInRaceLog.put((CompetitorWithBoat) e.getKey(), e.getValue()); - } - final Iterable competitorSetToRemove = filterCompetitorDuplicates(competitorsToRegister, - competitorsRegisteredInRaceLog); - raceColumn.deregisterCompetitors(competitorSetToRemove, fleet); - // we assume that the competitors id of type Competitor here, so we need to find the corresponding boat - for (Entry competitorToRegister : competitorsToRegister.entrySet()) { - raceColumn.registerCompetitor(competitorToRegister.getKey(), competitorToRegister.getValue(), fleet); - } - return Response.ok().header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build(); + return response; } private Iterable filterCompetitorDuplicates(Map competitorToBoatMappingsToRegister, diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java index 25a2b2cda40..768e179b413 100755 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java @@ -142,6 +142,7 @@ import com.sap.sse.common.util.RoundingUtil; import com.sap.sse.datamining.shared.impl.PredefinedQueryIdentifier; import com.sap.sse.security.shared.HasPermissions.DefaultActions; import com.sap.sse.security.shared.OwnershipAnnotation; +import com.sap.sse.security.shared.impl.SecuredSecurityTypes; import com.sap.sse.security.shared.impl.User; @Path("/v1/regattas") @@ -331,7 +332,7 @@ public class RegattasResource extends AbstractSailingServerResource { final JSONArray result = new JSONArray(); for (final Competitor competitor : regatta.getAllCompetitors()) { if (getSecurityService().hasCurrentUserAnyExplicitPermissions(competitor, - SecuredDomainType.CompetitorAndBoatActions.READ_AND_READ_PUBLIC_ACTIONS)) { + SecuredSecurityTypes.PublicReadableActions.READ_AND_READ_PUBLIC_ACTIONS)) { result.add(competitorSerializer.serialize(competitor)); } } @@ -364,7 +365,7 @@ public class RegattasResource extends AbstractSailingServerResource { response = getBadCompetitorIdResponse(competitorId); } else { getSecurityService().checkCurrentUserAnyExplicitPermissions(competitor, - SecuredDomainType.CompetitorAndBoatActions.READ_AND_READ_PUBLIC_ACTIONS); + SecuredSecurityTypes.PublicReadableActions.READ_AND_READ_PUBLIC_ACTIONS); regatta.registerCompetitor(competitor); response = Response.ok().build(); } @@ -609,7 +610,7 @@ public class RegattasResource extends AbstractSailingServerResource { response = Response.status(Status.NOT_FOUND).entity("Boat is not valid").type(MediaType.TEXT_PLAIN).build(); } else { getSecurityService().checkCurrentUserAnyExplicitPermissions(boat, - SecuredDomainType.CompetitorAndBoatActions.READ_AND_READ_PUBLIC_ACTIONS); + SecuredSecurityTypes.PublicReadableActions.READ_AND_READ_PUBLIC_ACTIONS); response = createAndAddCompetitor(regattaName, nationalityThreeLetterIOCCode, displayColor, timeOnTimeFactor, timeOnDistanceAllowancePerNauticalMileAsMillis, searchTag, competitorName, competitorShortName, competitorEmail, flagImageURI, teamImageURI, t -> boat, deviceUuid, @@ -665,7 +666,7 @@ public class RegattasResource extends AbstractSailingServerResource { response = getBadCompetitorIdResponse(competitorId); } else { getSecurityService().checkCurrentUserAnyExplicitPermissions(competitor, - SecuredDomainType.CompetitorAndBoatActions.READ_AND_READ_PUBLIC_ACTIONS); + SecuredSecurityTypes.PublicReadableActions.READ_AND_READ_PUBLIC_ACTIONS); regatta.deregisterCompetitor(competitor); response = Response.ok().build(); } @@ -728,7 +729,7 @@ public class RegattasResource extends AbstractSailingServerResource { JSONArray jsonCompetitors = new JSONArray(); for (Competitor competitor : trackedRace.getRace().getCompetitors()) { if (getSecurityService().hasCurrentUserAnyExplicitPermissions(competitor, - SecuredDomainType.CompetitorAndBoatActions.READ_AND_READ_PUBLIC_ACTIONS)) { + SecuredSecurityTypes.PublicReadableActions.READ_AND_READ_PUBLIC_ACTIONS)) { if (competitorIds == null || competitorIds.isEmpty() || competitorIds.contains(competitor.getId().toString())) { JSONObject jsonCompetitor = new JSONObject(); @@ -1405,7 +1406,7 @@ public class RegattasResource extends AbstractSailingServerResource { for (Competitor competitor : competitors) { if (getSecurityService().hasCurrentUserAnyExplicitPermissions(competitor, - SecuredDomainType.CompetitorAndBoatActions.READ_AND_READ_PUBLIC_ACTIONS)) { + SecuredSecurityTypes.PublicReadableActions.READ_AND_READ_PUBLIC_ACTIONS)) { if (competitorFilter == null || competitor.getId().equals(competitorFilter)) { Iterable maneuversForCompetitor = trackedRace.getManeuvers(competitor, startTime, endTime, false); data.add(new Pair>(competitor, maneuversForCompetitor)); @@ -1601,7 +1602,7 @@ public class RegattasResource extends AbstractSailingServerResource { Map ranks = leg.getRanks(timePoint); for (Competitor competitor : trackedRace.getRace().getCompetitors()) { if (getSecurityService().hasCurrentUserAnyExplicitPermissions(competitor, - SecuredDomainType.CompetitorAndBoatActions.READ_AND_READ_PUBLIC_ACTIONS)) { + SecuredSecurityTypes.PublicReadableActions.READ_AND_READ_PUBLIC_ACTIONS)) { JSONObject jsonCompetitorInLeg = new JSONObject(); TrackedLegOfCompetitor trackedLegOfCompetitor = leg.getTrackedLeg(competitor); if (trackedLegOfCompetitor != null) { @@ -1769,7 +1770,7 @@ public class RegattasResource extends AbstractSailingServerResource { Integer overallRank = 1; for (Competitor competitor : overallRanking) { if (getSecurityService().hasCurrentUserAnyExplicitPermissions(competitor, - SecuredDomainType.CompetitorAndBoatActions.READ_AND_READ_PUBLIC_ACTIONS)) { + SecuredSecurityTypes.PublicReadableActions.READ_AND_READ_PUBLIC_ACTIONS)) { overallRankPerCompetitor.put(competitor, overallRank++); } } @@ -1777,7 +1778,7 @@ public class RegattasResource extends AbstractSailingServerResource { Integer rank = 1; for (Competitor competitor : competitorsFromBestToWorst) { if (getSecurityService().hasCurrentUserAnyExplicitPermissions(competitor, - SecuredDomainType.CompetitorAndBoatActions.READ_AND_READ_PUBLIC_ACTIONS)) { + SecuredSecurityTypes.PublicReadableActions.READ_AND_READ_PUBLIC_ACTIONS)) { JSONObject jsonCompetitorInLeg = new JSONObject(); if (topN != null && topN > 0 && rank > topN) { diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/WindResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/WindResource.java index 28925cdaa7e..f59d700ec49 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/WindResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/WindResource.java @@ -6,7 +6,6 @@ import javax.ws.rs.Path; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; -import org.apache.shiro.SecurityUtils; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.JSONValue; @@ -24,7 +23,6 @@ import com.sap.sailing.server.gateway.deserialization.impl.Helpers; import com.sap.sailing.server.gateway.deserialization.impl.PositionJsonDeserializer; import com.sap.sailing.server.gateway.deserialization.impl.WindJsonDeserializer; import com.sap.sailing.server.gateway.jaxrs.AbstractSailingServerResource; -import com.sap.sse.security.shared.HasPermissions.DefaultActions; @Path("/v1/wind") public class WindResource extends AbstractSailingServerResource { @@ -47,7 +45,7 @@ public class WindResource extends AbstractSailingServerResource { String raceName = (String) regattaNameAndRaceNameObject.get("raceName"); RegattaNameAndRaceName identifier = new RegattaNameAndRaceName(regattaName, raceName); // add wind only to those races the subject is permitted to update - if (SecurityUtils.getSubject().isPermitted(identifier.getIdentifier().getStringPermission(DefaultActions.UPDATE))) { + if (getSecurityService().hasCurrentUserUpdatePermission(identifier)) { JSONObject answerForRace = new JSONObject(); answerForRace.put("regattaNameAndRaceName", regattaNameAndRaceName); if (windSourceType == WindSourceType.EXPEDITION || windSourceType == WindSourceType.WEB) { diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/trackfiles/impl/TrackFilesExportPostServlet.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/trackfiles/impl/TrackFilesExportPostServlet.java index 6f96b3270ef..2dcab62e67a 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/trackfiles/impl/TrackFilesExportPostServlet.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/trackfiles/impl/TrackFilesExportPostServlet.java @@ -11,8 +11,11 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.apache.shiro.SecurityUtils; + import com.sap.sailing.domain.base.RaceDefinition; import com.sap.sailing.domain.base.Regatta; +import com.sap.sailing.domain.common.security.SecuredDomainType.TrackedRaceActions; import com.sap.sailing.domain.common.trackfiles.TrackFilesDataSource; import com.sap.sailing.domain.common.trackfiles.TrackFilesExportParameters; import com.sap.sailing.domain.common.trackfiles.TrackFilesFormat; @@ -81,8 +84,12 @@ public class TrackFilesExportPostServlet extends SailingServerHttpServlet { resp.setHeader("Content-Disposition", "attachment; filename=\"tracked-races.zip\""); resp.setContentType("application/zip"); - ZipOutputStream out = new ZipOutputStream(resp.getOutputStream()); List trackedRaces = getTrackedRaces(regattaRaces); + for (TrackedRace trackedRace : trackedRaces) { + SecurityUtils.getSubject() + .checkPermission(trackedRace.getIdentifier().getStringPermission(TrackedRaceActions.EXPORT)); + } + ZipOutputStream out = new ZipOutputStream(resp.getOutputStream()); boolean beforeAfter = beforeAfterString == null ? false : true; boolean rawFixes = rawFixesString == null ? false : true; TrackFilesFormat format = TrackFilesFormat.valueOf(formatString); diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/windimport/AbstractWindImportServlet.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/windimport/AbstractWindImportServlet.java index 61a3426e140..e02264c730c 100755 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/windimport/AbstractWindImportServlet.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/windimport/AbstractWindImportServlet.java @@ -19,6 +19,7 @@ import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; +import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.RegattaNameAndRaceName; import com.sap.sailing.server.gateway.SailingServerHttpServlet; import com.sap.sailing.server.gateway.windimport.AbstractWindImporter.UploadRequest; @@ -44,6 +45,9 @@ public abstract class AbstractWindImportServlet extends SailingServerHttpServlet WindImportResult windImportResult = new WindImportResult(); try { UploadRequest uploadRequest = readRequest(request); + for (RegattaAndRaceIdentifier trackedRace : uploadRequest.races) { + getSecurityService().checkCurrentUserUpdatePermission(trackedRace); + } importer.importWindForUploadRequest(getService(), windImportResult, uploadRequest); // Use text/html to prevent browsers from wrapping the response body, // see "Handling File Upload Responses in GWT" at http://www.artofsolving.com/node/50 diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/windimport/AbstractWindImporter.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/windimport/AbstractWindImporter.java index d0a773f147a..5e4ec432352 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/windimport/AbstractWindImporter.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/windimport/AbstractWindImporter.java @@ -9,6 +9,7 @@ import java.util.List; import java.util.Map; import org.apache.commons.fileupload.FileItem; +import org.apache.shiro.SecurityUtils; import org.json.simple.JSONArray; import org.json.simple.JSONObject; @@ -22,6 +23,7 @@ import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.server.gateway.windimport.AbstractWindImporter.WindImportResult.RaceEntry; import com.sap.sailing.server.interfaces.RacingEventService; import com.sap.sse.common.Util; +import com.sap.sse.security.shared.HasPermissions.DefaultActions; public abstract class AbstractWindImporter { public static class UploadRequest { @@ -138,6 +140,8 @@ public abstract class AbstractWindImporter { if (uploadRequest.races.size() > 0) { for (RegattaAndRaceIdentifier raceEntry : uploadRequest.races) { DynamicTrackedRace trackedRace = service.getTrackedRace(raceEntry); + SecurityUtils.getSubject() + .checkPermission(trackedRace.getIdentifier().getStringPermission(DefaultActions.UPDATE)); if (trackedRace != null) { trackedRaces.add(trackedRace); } @@ -145,7 +149,11 @@ public abstract class AbstractWindImporter { } else { for (Regatta regatta : service.getAllRegattas()) { for (RaceDefinition raceDefinition : regatta.getAllRaces()) { - trackedRaces.add(service.getTrackedRegatta(regatta).getTrackedRace(raceDefinition)); + final DynamicTrackedRace trackedRace = service.getTrackedRegatta(regatta).getTrackedRace(raceDefinition); + if (SecurityUtils.getSubject() + .isPermitted(trackedRace.getIdentifier().getStringPermission(DefaultActions.UPDATE))) { + trackedRaces.add(trackedRace); + } } } } @@ -159,9 +167,6 @@ public abstract class AbstractWindImporter { public void importWindToWindSourceAndTrackedRaces(RacingEventService service, WindImportResult windImportResult, WindSource windSource, List trackedRaces, final Map streamsWithFilenames) throws IOException, InterruptedException, FormatNotSupportedException { - for (DynamicTrackedRace trackedRace : trackedRaces) { - service.getSecurityService().checkCurrentUserUpdatePermission(trackedRace); - } Iterable windFixes = importWind(streamsWithFilenames); if (!Util.isEmpty(windFixes)) { for (DynamicTrackedRace trackedRace : trackedRaces) { diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/security/SailingViewerRole.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/security/SailingViewerRole.java index 45a183b059b..3873b0738c4 100644 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/security/SailingViewerRole.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/security/SailingViewerRole.java @@ -1,10 +1,10 @@ package com.sap.sailing.server.security; import com.sap.sailing.domain.common.security.SecuredDomainType; -import com.sap.sailing.domain.common.security.SecuredDomainType.CompetitorAndBoatActions; import com.sap.sse.security.shared.HasPermissions.DefaultActions; import com.sap.sse.security.shared.RolePrototype; import com.sap.sse.security.shared.WildcardPermission; +import com.sap.sse.security.shared.impl.SecuredSecurityTypes; /** * Specifies a role that when associated to a user gives read access to the sailing domain parts needed for the home @@ -20,9 +20,7 @@ public class SailingViewerRole extends RolePrototype { WildcardPermission.builder().withTypes(SecuredDomainType.EVENT, SecuredDomainType.LEADERBOARD_GROUP, SecuredDomainType.LEADERBOARD, SecuredDomainType.REGATTA, SecuredDomainType.TRACKED_RACE) .withActions(DefaultActions.READ).build(), WildcardPermission.builder().withTypes(SecuredDomainType.COMPETITOR, SecuredDomainType.BOAT) - .withActions(CompetitorAndBoatActions.READ_PUBLIC).build(), - WildcardPermission.builder().withTypes(SecuredDomainType.COMPETITOR, SecuredDomainType.BOAT) - .withActions(DefaultActions.READ).build()); + .withActions(SecuredSecurityTypes.PublicReadableActions.READ_PUBLIC).build()); } public static SailingViewerRole getInstance() { diff --git a/java/com.sap.sailing.xmlexport/META-INF/MANIFEST.MF b/java/com.sap.sailing.xmlexport/META-INF/MANIFEST.MF index c3b14e2e719..b3f3cdb6dac 100644 --- a/java/com.sap.sailing.xmlexport/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.xmlexport/META-INF/MANIFEST.MF @@ -18,7 +18,8 @@ Require-Bundle: com.sap.sailing.server, com.sap.sse.common, com.sap.sse.replication, org.jdom;bundle-version="1.1.1", - com.sap.sailing.server.interface + com.sap.sailing.server.interface, + com.sap.sse.security Bundle-ClassPath: . Web-ContextPath: /export Automatic-Module-Name: com.sap.sailing.xmlexport diff --git a/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/ExportAction.java b/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/ExportAction.java index 3156508c51b..3073cd77c58 100644 --- a/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/ExportAction.java +++ b/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/ExportAction.java @@ -35,6 +35,7 @@ import com.sap.sse.common.Speed; import com.sap.sse.common.TimePoint; import com.sap.sse.common.Util; import com.sap.sse.common.Util.Triple; +import com.sap.sse.security.SecurityService; public abstract class ExportAction { private HttpServletRequest req; @@ -44,11 +45,14 @@ public abstract class ExportAction { private Leaderboard leaderboard; protected final boolean useProvidedLeaderboard; private String resultingXMLData; + private final SecurityService securityService; - public ExportAction(HttpServletRequest req, HttpServletResponse res, RacingEventService service) { + public ExportAction(HttpServletRequest req, HttpServletResponse res, RacingEventService service, + final SecurityService securityService) { this.req = req; this.res = res; this.service = service; + this.securityService = securityService; this.leaderboard = null; this.useProvidedLeaderboard = false; } @@ -56,6 +60,8 @@ public abstract class ExportAction { public ExportAction(Leaderboard leaderboard) { this.leaderboard = leaderboard; this.useProvidedLeaderboard = true; + // no permission check needed since we already got a leaderboard + this.securityService = null; } public String getAttribute(String name) { @@ -76,7 +82,8 @@ public abstract class ExportAction { if (leaderboardName == null) { throw new ServletException("Use the name= parameter to specify the leaderboard"); } - leaderboard = getService().getLeaderboardByName(leaderboardName); + leaderboard = getService().getLeaderboardByName(leaderboardName); + securityService.checkCurrentUserReadPermission(leaderboard); if (leaderboard == null) { throw new ServletException("Leaderboard " + leaderboardName + " not found."); } diff --git a/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/FoilingData.java b/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/FoilingData.java index 1ee8614f952..b4a7e2778d9 100644 --- a/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/FoilingData.java +++ b/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/FoilingData.java @@ -33,6 +33,7 @@ import com.sap.sailing.server.interfaces.RacingEventService; import com.sap.sse.common.Bearing; import com.sap.sse.common.Duration; import com.sap.sse.common.TimePoint; +import com.sap.sse.security.SecurityService; public class FoilingData { @@ -40,12 +41,15 @@ public class FoilingData { private final HttpServletResponse res; private final RacingEventService service; private final boolean useProvidedLeaderboard; + private SecurityService securityService; - public FoilingData(HttpServletRequest req, HttpServletResponse res, RacingEventService service) { + public FoilingData(HttpServletRequest req, HttpServletResponse res, RacingEventService service, + final SecurityService securityService) { this.req = req; this.res = res; this.service = service; this.useProvidedLeaderboard = false; + this.securityService = securityService; } public String getAttribute(String name) { @@ -64,6 +68,7 @@ public class FoilingData { throw new ServletException("Use the name= parameter to specify the leaderboard"); } leaderboard = getService().getLeaderboardByName(leaderboardName); + securityService.checkCurrentUserReadPermission(leaderboard); if (leaderboard == null) { throw new ServletException("Leaderboard " + leaderboardName + " not found."); } diff --git a/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/LeaderboardData.java b/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/LeaderboardData.java index 0e6e6014b08..c4ca9278d47 100644 --- a/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/LeaderboardData.java +++ b/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/LeaderboardData.java @@ -61,6 +61,7 @@ import com.sap.sse.common.Util; import com.sap.sse.common.Util.Pair; import com.sap.sse.common.impl.MillisecondsDurationImpl; import com.sap.sse.common.impl.MillisecondsTimePoint; +import com.sap.sse.security.SecurityService; /** * Exports all data from a leaderboard into XML format. @@ -81,9 +82,10 @@ public class LeaderboardData extends ExportAction { private static final String VERY_STRONG_WIND_DESCRIPTION = "Very Strong"; private final Map> raceColumnToCompetitors; - - public LeaderboardData(HttpServletRequest req, HttpServletResponse res, RacingEventService service) { - super(req, res, service); + + public LeaderboardData(HttpServletRequest req, HttpServletResponse res, RacingEventService service, + final SecurityService securityService) { + super(req, res, service, securityService); this.raceColumnToCompetitors = new HashMap<>(); } diff --git a/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/XmlExportServlet.java b/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/XmlExportServlet.java index 9ecec4bd6ee..e58bf9d62fc 100755 --- a/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/XmlExportServlet.java +++ b/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/XmlExportServlet.java @@ -17,11 +17,12 @@ public class XmlExportServlet extends SailingServerHttpServlet { try { if ((action != null) && !"".equals(action)) { if ("leaderboard".equalsIgnoreCase(action)) { - final LeaderboardData leaderboardData = new LeaderboardData(req, res, getService()); + final LeaderboardData leaderboardData = new LeaderboardData(req, res, getService(), + getSecurityService()); leaderboardData.perform(); return; } else if ("foiling".equalsIgnoreCase(action)) { - final FoilingData foilingData = new FoilingData(req, res, getService()); + final FoilingData foilingData = new FoilingData(req, res, getService(), getSecurityService()); foilingData.perform(); return; } diff --git a/java/com.sap.sse.security.common/src/com/sap/sse/security/shared/HasPermissions.java b/java/com.sap.sse.security.common/src/com/sap/sse/security/shared/HasPermissions.java index 54f147bf397..9e900166a5c 100644 --- a/java/com.sap.sse.security.common/src/com/sap/sse/security/shared/HasPermissions.java +++ b/java/com.sap.sse.security.common/src/com/sap/sse/security/shared/HasPermissions.java @@ -119,6 +119,9 @@ public interface HasPermissions { public static final Action[] MUTATION_ACTIONS = new Action[] { CREATE, UPDATE, DELETE, CHANGE_OWNERSHIP, CHANGE_ACL }; + + public static final Action[] MUTATION_ACTIONS_FOR_NON_DELETABLE_TYPES = new Action[] { DefaultActions.CREATE, + DefaultActions.UPDATE, DefaultActions.CHANGE_OWNERSHIP, DefaultActions.CHANGE_ACL }; /** * Returns all {@link DefaultActions} plus the {@code actions} passed, combined in one new array diff --git a/java/com.sap.sse.security.common/src/com/sap/sse/security/shared/UserRole.java b/java/com.sap.sse.security.common/src/com/sap/sse/security/shared/UserRole.java index 2a5adb21d05..050f7481d55 100755 --- a/java/com.sap.sse.security.common/src/com/sap/sse/security/shared/UserRole.java +++ b/java/com.sap.sse.security.common/src/com/sap/sse/security/shared/UserRole.java @@ -1,6 +1,7 @@ package com.sap.sse.security.shared; import com.sap.sse.security.shared.HasPermissions.DefaultActions; +import com.sap.sse.security.shared.impl.SecuredSecurityTypes; public class UserRole extends RolePrototype { private static final long serialVersionUID = 3291793984984443193L; @@ -9,7 +10,9 @@ public class UserRole extends RolePrototype { UserRole() { super("user", "ad1d5148-b13d-4464-90c4-7c396e4d4e2e", - WildcardPermission.builder().withActions(DefaultActions.values()).build()); + WildcardPermission.builder() + .withActions(DefaultActions.plus(SecuredSecurityTypes.PublicReadableActions.READ_PUBLIC)) + .build()); } public static UserRole getInstance() { diff --git a/java/com.sap.sse.security.common/src/com/sap/sse/security/shared/impl/SecuredSecurityTypes.java b/java/com.sap.sse.security.common/src/com/sap/sse/security/shared/impl/SecuredSecurityTypes.java index 2f588c4761d..c6276e9362c 100755 --- a/java/com.sap.sse.security.common/src/com/sap/sse/security/shared/impl/SecuredSecurityTypes.java +++ b/java/com.sap.sse.security.common/src/com/sap/sse/security/shared/impl/SecuredSecurityTypes.java @@ -32,6 +32,13 @@ public class SecuredSecurityTypes extends HasPermissionsImpl { return Collections.unmodifiableSet(allInstances); } + public static enum PublicReadableActions implements Action { + READ_PUBLIC; + + public static final Action[] READ_AND_READ_PUBLIC_ACTIONS = new Action[] { READ_PUBLIC, DefaultActions.READ }; + }; + + public static enum UserActions implements Action { /** Update a user's password without knowing the old password. */ FORCE_OVERWRITE_PASSWORD @@ -40,7 +47,7 @@ public class SecuredSecurityTypes extends HasPermissionsImpl { * type-relative identifier is the {@link User#getName() username}. */ public static final HasPermissions USER = new SecuredSecurityTypes("USER", DefaultActions - .plus(UserActions.FORCE_OVERWRITE_PASSWORD)); + .plus(UserActions.FORCE_OVERWRITE_PASSWORD, PublicReadableActions.READ_PUBLIC)); /** * type-relative identifier is the {@link RoleDefinition#getId() role ID's} string representation diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/server/UserManagementServiceImpl.java b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/server/UserManagementServiceImpl.java index 0daf0e1a1a3..be698effce8 100644 --- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/server/UserManagementServiceImpl.java +++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/server/UserManagementServiceImpl.java @@ -322,10 +322,15 @@ public class UserManagementServiceImpl extends RemoteServiceServlet implements U @Override public UserDTO getUserByName(String username) throws UnauthorizedException { final User user = getSecurityService().getUserByName(username); - if (user == null || SecurityUtils.getSubject().isPermitted(SecuredSecurityTypes.USER.getStringPermissionForObject(DefaultActions.READ, user))) { - return user==null?null:securityDTOFactory.createUserDTOFromUser(user, getSecurityService()); + if (user == null + || SecurityUtils.getSubject() + .isPermitted(SecuredSecurityTypes.USER.getStringPermissionForObject(DefaultActions.READ, user)) + || SecurityUtils.getSubject().isPermitted(SecuredSecurityTypes.USER + .getStringPermissionForObject(SecuredSecurityTypes.PublicReadableActions.READ_PUBLIC, user))) { + // TODO: pruning when current user only has READ_PUBLIC permission + return user == null ? null : securityDTOFactory.createUserDTOFromUser(user, getSecurityService()); } else { - throw new UnauthorizedException("Not permitted to read user "+username); + throw new UnauthorizedException("Not permitted to read user " + username); } } @@ -438,7 +443,11 @@ public class UserManagementServiceImpl extends RemoteServiceServlet implements U public Collection getUserList() throws UnauthorizedException { List users = new ArrayList<>(); for (User u : getSecurityService().getUserList()) { - if (SecurityUtils.getSubject().isPermitted(SecuredSecurityTypes.USER.getStringPermissionForObject(DefaultActions.READ, u))) { + if (SecurityUtils.getSubject() + .isPermitted(SecuredSecurityTypes.USER.getStringPermissionForObject(DefaultActions.READ, u)) + || SecurityUtils.getSubject().isPermitted(SecuredSecurityTypes.USER + .getStringPermissionForObject(SecuredSecurityTypes.PublicReadableActions.READ_PUBLIC, u))) { + // TODO: pruning if subject only has READ_PUBLIC permission final UserDTO userDTO = getUserDTOWithFilteredRolesAndPermissions(u); users.add(userDTO); } diff --git a/java/com.sap.sse.security/src/com/sap/sse/security/impl/SecurityServiceImpl.java b/java/com.sap.sse.security/src/com/sap/sse/security/impl/SecurityServiceImpl.java index c9931f42f98..7745e0a8460 100644 --- a/java/com.sap.sse.security/src/com/sap/sse/security/impl/SecurityServiceImpl.java +++ b/java/com.sap.sse.security/src/com/sap/sse/security/impl/SecurityServiceImpl.java @@ -1789,7 +1789,7 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat HasPermissions permittedObject, HasPermissions.Action[] actions, Iterable objectsToFilter, Consumer filteredObjectsConsumer) { objectsToFilter.forEach(objectToCheck -> { - boolean isPermitted = actions.length > 0; + boolean isPermitted = false; for (int i = 0; i < actions.length; i++) { if (SecurityUtils.getSubject() .isPermitted(permittedObject.getStringPermissionForObject(actions[i], objectToCheck))) { diff --git a/java/com.sap.sse.security/src/com/sap/sse/security/jaxrs/api/SecurityResource.java b/java/com.sap.sse.security/src/com/sap/sse/security/jaxrs/api/SecurityResource.java index 27e03e180f0..bfc6f28e0ec 100644 --- a/java/com.sap.sse.security/src/com/sap/sse/security/jaxrs/api/SecurityResource.java +++ b/java/com.sap.sse.security/src/com/sap/sse/security/jaxrs/api/SecurityResource.java @@ -26,6 +26,7 @@ import com.sap.sse.common.Util; import com.sap.sse.common.mail.MailException; import com.sap.sse.security.jaxrs.AbstractSecurityResource; import com.sap.sse.security.shared.UserManagementException; +import com.sap.sse.security.shared.impl.SecuredSecurityTypes; import com.sap.sse.security.shared.impl.User; import com.sun.jersey.api.client.ClientResponse.Status; @@ -156,15 +157,17 @@ public class SecurityResource extends AbstractSecurityResource { final User user = getService().getUserByName(username == null ? subject.getPrincipal().toString() : username); if (user == null) { return Response.status(Status.PRECONDITION_FAILED).entity("User "+username+" not known").build(); - } else if (!getService().hasCurrentUserReadPermission(user)) { - return Response.status(Status.UNAUTHORIZED).build(); - } else { + } else if (getService().hasCurrentUserReadPermission(user) || getService() + .hasCurrentUserAnyExplicitPermissions(user, SecuredSecurityTypes.PublicReadableActions.READ_PUBLIC)) { + // TODO: pruning when current user only has READ_PUBLIC JSONObject result = new JSONObject(); result.put("username", user.getName()); result.put("fullName", user.getFullName()); result.put("email", user.getEmail()); result.put("company", user.getCompany()); return Response.ok(result.toJSONString()).build(); + } else { + return Response.status(Status.UNAUTHORIZED).build(); } }