Merge branch 'permission-vertical' into bug4895

This commit is contained in:
Steffen Jacobs committed 2019-03-08 16:37:42 +01:00
commit e44d6a9f5a
45 files changed
+361 -144

No files matched your search

@@ -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");
@@ -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"),
@@ -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<SimpleCompetitorWithId
Competitor competitor = competitorStore.getExistingCompetitorByIdAsString(id.toString());
if (competitor != null) {
ctx.getSecurityService().checkCurrentUserAnyExplicitPermissions(competitor,
CompetitorAndBoatActions.READ_AND_READ_PUBLIC_ACTIONS);
SecuredSecurityTypes.PublicReadableActions.READ_AND_READ_PUBLIC_ACTIONS);
}
return (competitor == null ? new SimpleCompetitorWithIdDTO(id.toString(), id.toString(), "", null, null)
: new SimpleCompetitorWithIdDTO(competitor));
@@ -13,7 +13,6 @@ import com.google.gwt.user.client.ui.HeaderPanel;
import com.google.gwt.user.client.ui.RootLayoutPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.domain.common.security.SecuredDomainType;
import com.sap.sailing.domain.common.security.SecuredDomainType.CompetitorAndBoatActions;
import com.sap.sailing.domain.common.security.SecuredDomainType.TrackedRaceActions;
import com.sap.sailing.gwt.common.authentication.FixedSailingAuthentication;
import com.sap.sailing.gwt.common.authentication.SAPSailingHeaderWithAuthentication;
@@ -234,7 +233,7 @@ public class AdminConsoleEntryPoint extends AbstractSailingEntryPoint
getWidget().refreshCompetitorList();
}
}, getStringMessages().competitors(),
SecuredDomainType.COMPETITOR.getPermission(CompetitorAndBoatActions.MUTATION_ACTIONS));
SecuredDomainType.COMPETITOR.getPermission(DefaultActions.MUTATION_ACTIONS_FOR_NON_DELETABLE_TYPES));
final BoatPanel boatPanel = new BoatPanel(getSailingService(), getUserService(), getStringMessages(), this);
boatPanel.ensureDebugId("BoatPanel");
@@ -243,7 +242,8 @@ public class AdminConsoleEntryPoint extends AbstractSailingEntryPoint
public void refreshAfterBecomingVisible() {
getWidget().refreshBoatList();
}
}, getStringMessages().boats(), SecuredDomainType.BOAT.getPermission(CompetitorAndBoatActions.MUTATION_ACTIONS));
}, getStringMessages().boats(),
SecuredDomainType.BOAT.getPermission(DefaultActions.MUTATION_ACTIONS_FOR_NON_DELETABLE_TYPES));
RaceCourseManagementPanel raceCourseManagementPanel = new RaceCourseManagementPanel(getSailingService(), this,
this, getStringMessages(), getUserService());
@@ -317,7 +317,7 @@ public class LeaderboardConfigPanel extends AbstractLeaderboardConfigPanel
removeLeaderboard(leaderboardDTO);
}
});
leaderboardActionColumn.addAction(LeaderboardConfigImagesBarCell.ACTION_EDIT_SCORES, leaderboardDTO -> {
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<Iterable<DetailType>>() {
@@ -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<String, String> dashboardURLParameters = new HashMap<String, String>();
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<StrippedLeaderboardDTOWithSecurity> config = EditOwnershipDialog.create(
@@ -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);
}
}
}
@@ -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<RegattasDisplayer> 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<CaptionPanel, ExpeditionAllInOneImportPanel> 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<RaceDTO> 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();
@@ -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:
@@ -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 :
@@ -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 つ以上選択してください:
@@ -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:
@@ -236,7 +236,7 @@ raceIsKnownToStartUpwind=Гонка начинается против ветра
events=События
pairingList=Список пар
pairingLists=Списки пар
pairingListCreationInfo=Первым будет рассчитан шаблон, который содержит только номера участников и может быть применен к другим таблицам лидеров. Если число участников не делится на число лодок без остатка, в списке пар будут пустые места. \ Если число участников не равно числу зарегистрированных участников в фактической таблице лидеров, список пар не может быть применен к журналам гонки. \ Для генерирования списка пар используется число всех флайтов таблицы лидеров, не являющихся гонками на медали. \ Если используются повторы флайтов, то присвоения флайтов могут повторяться (например, 15 флайтов с 3 повторами: 5 уникальных присвоений флайтов, каждый из которых повторяется 3 раза). \ Результатом нулевого коэффициента смены лодки является хорошее распределение присвоений участников лодкам, то есть максимум (число флайтов) ведет к меньшему числу смен лодок, поскольку участники плывут на одной и той же лодке двух флотов друг за другом в разных флайтах.
pairingListCreationInfo=Первым будет рассчитан шаблон, который содержит только номера участников и может быть применен к другим таблицам лидеров. Если число участников не делится на число лодок без остатка, в списке пар будут пустые места. \ Если число участников не равно числу зарегистрированных участников в фактической таблице лидеров, список пар не может быть применен к журналам гонки. \ Для генерирования списка пар используется число всех флайтов таблицы лидеров, не являющихся гонками на медали. \ Если используются повторы флайтов, то присвоения флайтов могут повторяться (например, 15 флайтов с 3 повторами: 5 уникальных присвоений флайтов, каждый из которых повторяется 3 раза). \ Результатом нулевого коэффициента смены лодки является хорошее распределение участников по лодкам, то есть максимум (число флайтов) ведет к меньшему числу смен лодок, и это увеличивает шансы, что участник будет плыть на одной и той же лодке в двух смежных гонках до окончания флайта.
setCompetitors=Укажите число участников:
setBoatChangeFactor=Укажите коэффициент смены лодки:
seriesHint=Выберите не менее одной серии для списка пар:
@@ -236,7 +236,7 @@ raceIsKnownToStartUpwind=比赛轮次从迎风航段开始
events=活动
pairingList=配对清单
pairingLists=配对清单
pairingListCreationInfo=首先,将计算仅包含参赛队数并且可以应用到其他积分榜的模板。如果参赛队数量无法被船只数量除尽,则在配对清单中输入空占位符。\ 此外,如果参赛队数量不等于实际积分榜中注册的参赛队数量,配对清单无法应用到比赛轮次日志。\ 用于生成配对清单的航程数由积分榜中不是奖牌轮的航程数定义。\ 使用航程重复时,可以重复航程分配(例如,有 3 个航程重复的 15 个航程:5 个独特的航程分配,每个重复 3 次)。\ 船只更换系数等于 0 导致向船只平均分配参赛队,而最大(航程数)导致船只更换次数减少,因为不同航程中背后两个船队的参赛队在同一船只上航行。
pairingListCreationInfo=首先,将计算仅包含参赛队数并且可以应用到其他积分榜的模板。如果参赛队数量无法被船只数量除尽,则在配对清单中输入空占位符。\ 此外,如果参赛队数量不等于实际积分榜中注册的参赛队数量,配对清单无法应用到比赛轮次日志。\ 用于生成配对清单的航程数由积分榜中不是奖牌轮的航程数定义。\ 使用航程重复时,可以重复航程分配(例如,有 3 个航程重复的 15 个航程:5 个独特的航程分配,每个重复 3 次)。\ 船只更换系数等于 0 导致向船只平均分配参赛队,而最大值(航程数)通过提高参赛队在两个连续的比赛轮次驾驶同一船只驶过航程边界的机会来减少换船次数。
setCompetitors=请设置参赛队数量:
setBoatChangeFactor=请设置船只更换系数:
seriesHint=请选择一场或多场应位于配对清单的系列赛:
@@ -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<RegattaLogEventDTO> entries = new ArrayList<>();
@@ -5750,7 +5753,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
boolean filterCompetitorsWithoutBoat) {
Iterable<CompetitorDTO> 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<Competitor> competitors = (Iterable<Competitor>) competitorStore.getAllCompetitors();
@@ -5921,7 +5924,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
@Override
public Iterable<BoatDTO> getAllBoats() {
final HasPermissions.Action[] requiredActionsForRead = SecuredDomainType.CompetitorAndBoatActions.READ_AND_READ_PUBLIC_ACTIONS;
final HasPermissions.Action[] requiredActionsForRead = SecuredSecurityTypes.PublicReadableActions.READ_AND_READ_PUBLIC_ACTIONS;
Iterable<BoatDTO> result = getSecurityService().mapAndFilterByAnyExplicitPermissionForCurrentUser(
SecuredDomainType.BOAT, requiredActionsForRead,
getService().getBaseDomainFactory().getCompetitorAndBoatStore().getBoats(),
@@ -5933,7 +5936,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
public Iterable<BoatDTO> getStandaloneBoats() {
List<BoatDTO> 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<DynamicTrackedRace>) trackedRaces).add(race);
}
}
}
Map<RegattaAndRaceIdentifier, Integer> numberOfWindFixesImportedPerRace = new HashMap<RegattaAndRaceIdentifier, Integer>();
for (Account account : igtimiConnectionFactory.getAllAccounts()) {
@@ -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) {
@@ -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<Regatta> {
public static final String FIELD_NAME = "name";
@@ -65,7 +65,7 @@ public class RegattaJsonSerializer implements JsonSerializer<Regatta> {
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<Regatta> {
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));
}
}
@@ -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) {
@@ -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>(competitor, boat));
@@ -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.");
@@ -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(
@@ -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);
@@ -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<Regatta> 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)));
}
@@ -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 \""
@@ -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();
@@ -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;
}
@@ -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
@@ -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<CompetitorDTO> 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);
@@ -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<Competitor, Boat> 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<Competitor, Boat> 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<Competitor, Boat> competitorsRegisteredInRaceLog = new HashMap<>();
for (final Entry<Competitor, Boat> e : raceColumn.getCompetitorsRegisteredInRacelog(fleet).entrySet()) {
competitorsRegisteredInRaceLog.put((CompetitorWithBoat) e.getKey(), e.getValue());
}
final Iterable<Competitor> 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<Competitor, Boat> 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<Competitor, Boat> competitorsRegisteredInRaceLog = new HashMap<>();
for (final Entry<Competitor, Boat> e : raceColumn.getCompetitorsRegisteredInRacelog(fleet).entrySet()) {
competitorsRegisteredInRaceLog.put((CompetitorWithBoat) e.getKey(), e.getValue());
}
final Iterable<Competitor> 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<Competitor, Boat> 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<Competitor> filterCompetitorDuplicates(Map<Competitor, Boat> competitorToBoatMappingsToRegister,
@@ -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<Maneuver> maneuversForCompetitor = trackedRace.getManeuvers(competitor, startTime, endTime, false);
data.add(new Pair<Competitor, Iterable<Maneuver>>(competitor, maneuversForCompetitor));
@@ -1601,7 +1602,7 @@ public class RegattasResource extends AbstractSailingServerResource {
Map<Competitor, Integer> 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) {
@@ -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) {
@@ -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<TrackedRace> 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);
@@ -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
@@ -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<DynamicTrackedRace> trackedRaces, final Map<InputStream, String> streamsWithFilenames)
throws IOException, InterruptedException, FormatNotSupportedException {
for (DynamicTrackedRace trackedRace : trackedRaces) {
service.getSecurityService().checkCurrentUserUpdatePermission(trackedRace);
}
Iterable<Wind> windFixes = importWind(streamsWithFilenames);
if (!Util.isEmpty(windFixes)) {
for (DynamicTrackedRace trackedRace : trackedRaces) {
@@ -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() {
@@ -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
@@ -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.");
}
@@ -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.");
}
@@ -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<RaceColumn, List<Competitor>> 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<>();
}
@@ -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;
}
@@ -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
@@ -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() {
@@ -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
@@ -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<UserDTO> getUserList() throws UnauthorizedException {
List<UserDTO> 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);
}
@@ -1789,7 +1789,7 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat
HasPermissions permittedObject, HasPermissions.Action[] actions, Iterable<T> objectsToFilter,
Consumer<T> 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))) {
@@ -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();
}
}