bug5569: synchronizing the initialization of a server as "public" with the ReplicationService and skip if becoming a replica

This commit is contained in:
Axel Uhl
2021-06-08 15:41:16 +02:00
parent 1887258ae9
commit edc1aeae0a
9 changed files with 92 additions and 72 deletions
@@ -17,7 +17,7 @@ import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.gwt.ui.adminconsole.places.AdminConsoleView.Presenter;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.SailingServiceWriteAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.ServerConfigurationDTO;
import com.sap.sse.gwt.client.ErrorReporter;
@@ -36,7 +36,7 @@ import com.sap.sse.security.ui.client.component.EditOwnershipDialog;
import com.sap.sse.security.ui.client.component.editacl.EditACLDialog;
public class LocalServerManagementPanel extends SimplePanel {
private final SailingServiceAsync sailingService;
private final SailingServiceWriteAsync sailingService;
private final ErrorReporter errorReporter;
private final StringMessages stringMessages;
@@ -240,8 +240,6 @@ public interface SailingService extends RemoteService, RemoteReplicationService
ServerConfigurationDTO getServerConfiguration() throws UnauthorizedException;
void updateServerConfiguration(ServerConfigurationDTO serverConfiguration) throws UnauthorizedException;
List<RemoteSailingServerReferenceDTO> getRemoteSailingServerReferences() throws UnauthorizedException;
List<UrlDTO> getResultImportUrls(String resultProviderName) throws UnauthorizedException;
@@ -263,8 +263,6 @@ public interface SailingServiceAsync extends RemoteReplicationServiceAsync {
void getServerConfiguration(AsyncCallback<ServerConfigurationDTO> callback);
void updateServerConfiguration(ServerConfigurationDTO serverConfiguration, AsyncCallback<Void> callback);
void getRemoteSailingServerReferences(AsyncCallback<List<RemoteSailingServerReferenceDTO>> callback);
void getResultImportUrls(String resultProviderName, AsyncCallback<List<UrlDTO>> callback);
@@ -72,6 +72,7 @@ import com.sap.sailing.gwt.ui.shared.RaceLogSetFinishingAndFinishTimeDTO;
import com.sap.sailing.gwt.ui.shared.RaceLogSetStartTimeAndProcedureDTO;
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
import com.sap.sailing.gwt.ui.shared.RemoteSailingServerReferenceDTO;
import com.sap.sailing.gwt.ui.shared.ServerConfigurationDTO;
import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTOWithSecurity;
import com.sap.sailing.gwt.ui.shared.SwissTimingArchiveConfigurationWithSecurityDTO;
import com.sap.sailing.gwt.ui.shared.SwissTimingConfigurationWithSecurityDTO;
@@ -694,4 +695,5 @@ public interface SailingServiceWrite extends FileStorageManagementGwtService, Sa
List<DeviceMappingDTO> getDeviceMappings(String leaderboardName)
throws UnauthorizedException, DoesNotHaveRegattaLogException, TransformationException, NotFoundException;
void updateServerConfiguration(ServerConfigurationDTO serverConfiguration) throws UnauthorizedException;
}
@@ -57,6 +57,7 @@ import com.sap.sailing.gwt.ui.shared.RaceLogSetFinishingAndFinishTimeDTO;
import com.sap.sailing.gwt.ui.shared.RaceLogSetStartTimeAndProcedureDTO;
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
import com.sap.sailing.gwt.ui.shared.RemoteSailingServerReferenceDTO;
import com.sap.sailing.gwt.ui.shared.ServerConfigurationDTO;
import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTOWithSecurity;
import com.sap.sailing.gwt.ui.shared.SwissTimingArchiveConfigurationWithSecurityDTO;
import com.sap.sailing.gwt.ui.shared.SwissTimingConfigurationWithSecurityDTO;
@@ -619,4 +620,6 @@ public interface SailingServiceWriteAsync extends FileStorageManagementGwtServic
AsyncCallback<MarkTrackDTO> callback);
void getDeviceMappings(String leaderboardName, AsyncCallback<List<DeviceMappingDTO>> asyncCallback);
void updateServerConfiguration(ServerConfigurationDTO serverConfiguration, AsyncCallback<Void> callback);
}
@@ -70,7 +70,6 @@ import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.UnauthorizedException;
import org.apache.shiro.subject.Subject;
import org.json.simple.JSONArray;
@@ -152,7 +151,6 @@ import com.sap.sailing.domain.base.configuration.impl.RegattaConfigurationImpl;
import com.sap.sailing.domain.base.configuration.impl.SWCStartConfigurationImpl;
import com.sap.sailing.domain.base.configuration.procedures.ConfigurableStartModeFlagRacingProcedureConfiguration;
import com.sap.sailing.domain.base.impl.CourseDataImpl;
import com.sap.sailing.domain.base.impl.SailingServerConfigurationImpl;
import com.sap.sailing.domain.common.CompetitorDescriptor;
import com.sap.sailing.domain.common.DetailType;
import com.sap.sailing.domain.common.DeviceIdentifier;
@@ -413,7 +411,6 @@ import com.sap.sailing.server.gateway.deserialization.racelog.impl.ORCCertificat
import com.sap.sailing.server.gateway.serialization.LeaderboardGroupConstants;
import com.sap.sailing.server.interfaces.RacingEventService;
import com.sap.sailing.server.interfaces.SimulationService;
import com.sap.sailing.server.operationaltransformation.UpdateServerConfiguration;
import com.sap.sailing.server.security.SailingViewerRole;
import com.sap.sailing.shared.server.SharedSailingData;
import com.sap.sailing.simulator.Path;
@@ -478,7 +475,6 @@ import com.sap.sse.security.shared.RoleDefinition;
import com.sap.sse.security.shared.TypeRelativeObjectIdentifier;
import com.sap.sse.security.shared.dto.StrippedUserGroupDTO;
import com.sap.sse.security.shared.impl.AccessControlList;
import com.sap.sse.security.shared.impl.Ownership;
import com.sap.sse.security.shared.impl.SecuredSecurityTypes;
import com.sap.sse.security.shared.impl.SecuredSecurityTypes.ServerActions;
import com.sap.sse.security.shared.impl.UserGroup;
@@ -3979,52 +3975,6 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
return result;
}
@Override
// FIXME See bug 5569: this shall move to SailingServiceWriteImpl
public void updateServerConfiguration(ServerConfigurationDTO serverConfiguration) {
getSecurityService().checkCurrentUserServerPermission(ServerActions.CONFIGURE_LOCAL_SERVER);
getService().apply(new UpdateServerConfiguration(
new SailingServerConfigurationImpl(serverConfiguration.isStandaloneServer())));
if (serverConfiguration.isSelfService() != null) {
final boolean isCurrentlySelfService = isSelfServiceServer();
final boolean shouldBeSelfService = serverConfiguration.isSelfService();
if (isCurrentlySelfService != shouldBeSelfService) {
SecurityUtils.getSubject().checkPermission(getServerInfo().getIdentifier().getStringPermission(DefaultActions.CHANGE_ACL));
if (shouldBeSelfService) {
getSecurityService().addToAccessControlList(getServerInfo().getIdentifier(), null, ServerActions.CREATE_OBJECT.name());
} else {
getSecurityService().removeFromAccessControlList(getServerInfo().getIdentifier(), null, ServerActions.CREATE_OBJECT.name());
}
}
}
if (serverConfiguration.isPublic() != null) {
final RoleDefinition viewerRole = getSecurityService()
.getRoleDefinition(SailingViewerRole.getInstance().getId());
final UserGroup serverGroup = getSecurityService().getServerGroup();
if (viewerRole != null && serverGroup != null) {
final boolean isCurrentlyPublic = Boolean.TRUE.equals(serverGroup.getRoleAssociation(viewerRole));
final boolean shouldBePublic = serverConfiguration.isPublic();
if (isCurrentlyPublic != shouldBePublic) {
// value changed
if (getSecurityService().hasCurrentUserUpdatePermission(serverGroup)
&& getSecurityService().hasCurrentUserMetaPermissionsOfRoleDefinitionWithQualification(
viewerRole, new Ownership(null, serverGroup))) {
if (serverConfiguration.isPublic()) {
getSecurityService().putRoleDefinitionToUserGroup(serverGroup, viewerRole, /* forAll */ true);
} else {
getSecurityService().removeRoleDefintionFromUserGroup(serverGroup, viewerRole);
}
} else {
throw new AuthorizationException("No permission to make the server public");
}
}
} else {
throw new IllegalArgumentException(
SailingViewerRole.getInstance().getName() + " role or default server tenant does not exist");
}
}
}
@Override
//??
public List<RemoteSailingServerReferenceDTO> getRemoteSailingServerReferences() {
@@ -5829,7 +5779,7 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
return result;
}
private Boolean isSelfServiceServer() {
protected Boolean isSelfServiceServer() {
final AccessControlListAnnotation serverAclOrNull = getSecurityService().getAccessControlList(getServerInfo().getIdentifier());
final Boolean result;
if (serverAclOrNull == null) {
@@ -153,6 +153,7 @@ import com.sap.sailing.domain.base.impl.DynamicPerson;
import com.sap.sailing.domain.base.impl.DynamicTeam;
import com.sap.sailing.domain.base.impl.EventBaseImpl;
import com.sap.sailing.domain.base.impl.PersonImpl;
import com.sap.sailing.domain.base.impl.SailingServerConfigurationImpl;
import com.sap.sailing.domain.base.impl.TeamImpl;
import com.sap.sailing.domain.common.CompetitorDescriptor;
import com.sap.sailing.domain.common.CompetitorRegistrationType;
@@ -274,6 +275,7 @@ import com.sap.sailing.gwt.ui.shared.RaceLogSetStartTimeAndProcedureDTO;
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
import com.sap.sailing.gwt.ui.shared.RemoteSailingServerReferenceDTO;
import com.sap.sailing.gwt.ui.shared.SeriesDTO;
import com.sap.sailing.gwt.ui.shared.ServerConfigurationDTO;
import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTOWithSecurity;
import com.sap.sailing.gwt.ui.shared.SwissTimingArchiveConfigurationWithSecurityDTO;
import com.sap.sailing.gwt.ui.shared.SwissTimingConfigurationWithSecurityDTO;
@@ -342,7 +344,9 @@ import com.sap.sailing.server.operationaltransformation.UpdateLeaderboardScoreCo
import com.sap.sailing.server.operationaltransformation.UpdateRaceDelayToLive;
import com.sap.sailing.server.operationaltransformation.UpdateSailingServerReference;
import com.sap.sailing.server.operationaltransformation.UpdateSeries;
import com.sap.sailing.server.operationaltransformation.UpdateServerConfiguration;
import com.sap.sailing.server.operationaltransformation.UpdateSpecificRegatta;
import com.sap.sailing.server.security.SailingViewerRole;
import com.sap.sailing.server.util.WaitForTrackedRaceUtil;
import com.sap.sailing.xrr.schema.RegattaResults;
import com.sap.sse.common.Duration;
@@ -374,7 +378,10 @@ import com.sap.sse.gwt.shared.filestorage.FileStorageServicePropertyErrorsDTO;
import com.sap.sse.security.Action;
import com.sap.sse.security.shared.HasPermissions.DefaultActions;
import com.sap.sse.security.shared.QualifiedObjectIdentifier;
import com.sap.sse.security.shared.RoleDefinition;
import com.sap.sse.security.shared.TypeRelativeObjectIdentifier;
import com.sap.sse.security.shared.impl.Ownership;
import com.sap.sse.security.shared.impl.UserGroup;
import com.sap.sse.security.shared.impl.SecuredSecurityTypes.ServerActions;
import com.sap.sse.security.ui.server.SecurityDTOUtil;
import com.sap.sse.security.ui.shared.SuccessInfo;
@@ -3778,4 +3785,49 @@ public class SailingServiceWriteImpl extends SailingServiceImpl implements Saili
RegattaLog regattaLog = getRegattaLogInternal(leaderboardName);
return getDeviceMappings(regattaLog);
}
@Override
public void updateServerConfiguration(ServerConfigurationDTO serverConfiguration) {
getSecurityService().checkCurrentUserServerPermission(ServerActions.CONFIGURE_LOCAL_SERVER);
getService().apply(new UpdateServerConfiguration(
new SailingServerConfigurationImpl(serverConfiguration.isStandaloneServer())));
if (serverConfiguration.isSelfService() != null) {
final boolean isCurrentlySelfService = isSelfServiceServer();
final boolean shouldBeSelfService = serverConfiguration.isSelfService();
if (isCurrentlySelfService != shouldBeSelfService) {
SecurityUtils.getSubject().checkPermission(getServerInfo().getIdentifier().getStringPermission(DefaultActions.CHANGE_ACL));
if (shouldBeSelfService) {
getSecurityService().addToAccessControlList(getServerInfo().getIdentifier(), null, ServerActions.CREATE_OBJECT.name());
} else {
getSecurityService().removeFromAccessControlList(getServerInfo().getIdentifier(), null, ServerActions.CREATE_OBJECT.name());
}
}
}
if (serverConfiguration.isPublic() != null) {
final RoleDefinition viewerRole = getSecurityService()
.getRoleDefinition(SailingViewerRole.getInstance().getId());
final UserGroup serverGroup = getSecurityService().getServerGroup();
if (viewerRole != null && serverGroup != null) {
final boolean isCurrentlyPublic = Boolean.TRUE.equals(serverGroup.getRoleAssociation(viewerRole));
final boolean shouldBePublic = serverConfiguration.isPublic();
if (isCurrentlyPublic != shouldBePublic) {
// value changed
if (getSecurityService().hasCurrentUserUpdatePermission(serverGroup)
&& getSecurityService().hasCurrentUserMetaPermissionsOfRoleDefinitionWithQualification(
viewerRole, new Ownership(null, serverGroup))) {
if (serverConfiguration.isPublic()) {
getSecurityService().putRoleDefinitionToUserGroup(serverGroup, viewerRole, /* forAll */ true);
} else {
getSecurityService().removeRoleDefintionFromUserGroup(serverGroup, viewerRole);
}
} else {
throw new AuthorizationException("No permission to make the server public");
}
}
} else {
throw new IllegalArgumentException(
SailingViewerRole.getInstance().getName() + " role or default server tenant does not exist");
}
}
}
}
@@ -71,9 +71,9 @@ import com.sap.sse.security.SecurityService;
import com.sap.sse.security.SecurityUrlPathProvider;
import com.sap.sse.security.interfaces.PreferenceConverter;
import com.sap.sse.security.shared.HasPermissions.DefaultActions;
import com.sap.sse.security.util.GenericJSONPreferenceConverter;
import com.sap.sse.security.shared.HasPermissionsProvider;
import com.sap.sse.security.shared.RoleDefinition;
import com.sap.sse.security.util.GenericJSONPreferenceConverter;
import com.sap.sse.util.ClearStateTestSupport;
import com.sap.sse.util.ServiceTrackerFactory;
@@ -229,19 +229,33 @@ public class Activator implements BundleActivator {
registrations.add(context.registerService(HasPermissionsProvider.class, SecuredDomainType::getAllInstances, null));
registrations.add(context.registerService(SecurityInitializationCustomizer.class,
(SecurityInitializationCustomizer) securityService -> {
final RoleDefinition sailingViewerRoleDefinition = securityService
.getOrCreateRoleDefinitionFromPrototype(SailingViewerRole.getInstance());
if (securityService.isNewServer()) {
// The server is initially set to be public by adding sailing_viewer role to the server group
// with forAll=true
securityService.putRoleDefinitionToUserGroup(securityService.getServerGroup(),
sailingViewerRoleDefinition, true);
}
if (securityService.isInitialOrMigration()) {
// sailing_viewer role is publicly readable
securityService.addToAccessControlList(sailingViewerRoleDefinition.getIdentifier(),
null, DefaultActions.READ.name());
}
final Thread backgroundThread = new Thread(()->{
ReplicationService replicationService;
try {
replicationService = ServiceTrackerFactory.createAndOpen(context, ReplicationService.class).waitForService(0);
if (!replicationService.isReplicationStarting() && securityService.getMasterDescriptor() == null) {
// see also bug 5569: this must only be done if it is clear that this instance is not to become a replica
final RoleDefinition sailingViewerRoleDefinition = securityService
.getOrCreateRoleDefinitionFromPrototype(SailingViewerRole.getInstance());
if (securityService.isNewServer()) {
// The server is initially set to be public by adding sailing_viewer role to the server group
// with forAll=true
securityService.putRoleDefinitionToUserGroup(securityService.getServerGroup(),
sailingViewerRoleDefinition, true);
}
if (securityService.isInitialOrMigration()) {
// sailing_viewer role is publicly readable
securityService.addToAccessControlList(sailingViewerRoleDefinition.getIdentifier(),
null, DefaultActions.READ.name());
}
}
} catch (InterruptedException e) {
logger.log(Level.SEVERE, "Couldn't get a hold of the ReplicationService to tell whether this SecurityService is to become a replica; "+
"not setting server to public, not enforcing READability of sailing_viewer role", e);
}
}, "Waiting for replication service to tell whether this SecurityService will become a replica");
backgroundThread.setDaemon(true);
backgroundThread.start();
}, null));
final TrackedRaceStatisticsCache trackedRaceStatisticsCache = new TrackedRaceStatisticsCacheImpl();
registrations.add(context.registerService(TrackedRaceStatisticsCache.class.getName(),
@@ -128,11 +128,14 @@ public interface ReplicationService {
void removeReplicationStartingListener(ReplicationStartingListener listener);
/**
* When this service has been registered in the OSGi service registry and auto-replication has been configured,
* this method will return {@code true} already as soon as the service can be discovered in the registry.
*
* @return {@code true} if replication is starting; during this phase it is clear that the replicables managed by
* this service instance are about to be {@link Replicable#clearReplicaState() cleared} and to be initialized
* with an {@link Replicable#initiallyFillFrom(java.io.InputStream) initial load} obtained from the master
* instance. However, this initial load process may not yet have started and hence the {@link #getReplicator} method
*
* may return nothing.
*/
boolean isReplicationStarting();