195b78e63f28182c49ac3e69423d78c31b5a40a2
java/com.sap.sailing.aiagent.gateway/resources/shiro.ini
| ... | ... | @@ -26,7 +26,7 @@ securityManager.subjectDAO = $subjectDAO |
| 26 | 26 | securityManager.sessionManager.globalSessionTimeout = 31536000000 |
| 27 | 27 | cacheManager = com.sap.sse.security.SessionCacheManager |
| 28 | 28 | securityManager.cacheManager = $cacheManager |
| 29 | -authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithLockingAndBanning |
|
| 29 | +authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithTimedLocks |
|
| 30 | 30 | securityManager.authenticator.authenticationStrategy = $authenticationStrategy |
| 31 | 31 | |
| 32 | 32 | # Support for anonymous user permissions |
java/com.sap.sailing.dashboards.gwt/.settings/com.gwtplugins.gwt.eclipse.core.prefs
| ... | ... | @@ -1,5 +1,5 @@ |
| 1 | 1 | //gwtVersion_/com.google.gwt.servlet/lib= |
| 2 | -//gwtVersion_/com.google.gwt.user/lib=2.11.1 |
|
| 2 | +//gwtVersion_/com.google.gwt.user/lib= |
|
| 3 | 3 | //gwtVersion_/opt/gwt-2.11.0=2.11.0 |
| 4 | 4 | //gwtVersion_/opt/gwt-2.11.1=2.11.1 |
| 5 | 5 | //gwtVersion_/opt/gwt-2.12.2=2.12.2 |
java/com.sap.sailing.dashboards.gwt/src/main/resources/shiro.ini
| ... | ... | @@ -22,7 +22,7 @@ securityManager.sessionManager.sessionDAO = $sessionDAO |
| 22 | 22 | securityManager.sessionManager.globalSessionTimeout = 31536000000 |
| 23 | 23 | cacheManager = com.sap.sse.security.SessionCacheManager |
| 24 | 24 | securityManager.cacheManager = $cacheManager |
| 25 | -authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithLockingAndBanning |
|
| 25 | +authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithTimedLocks |
|
| 26 | 26 | securityManager.authenticator.authenticationStrategy = $authenticationStrategy |
| 27 | 27 | |
| 28 | 28 | subjectDAO = com.sap.sse.security.NoSessionStorageForUnauthenticatedSessionsSessionDAO |
java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/BoatClassMasterdata.java
| ... | ... | @@ -56,6 +56,7 @@ public enum BoatClassMasterdata { |
| 56 | 56 | ELLIOTT_6M ("Elliott 6m", true, 6.0, 2.35, BoatHullType.MONOHULL, true, "Elliott6m"), |
| 57 | 57 | EUROPE_INT ("Europe Int.", true, 3.35, 1.35, BoatHullType.MONOHULL, false, "Europe"), |
| 58 | 58 | F_18 ("Formula 18", true, 6.85, 2.25, BoatHullType.CATAMARAN, true, "F18", "F-18"), |
| 59 | + FAREAST28R ("Fareast 28R", true, 9.07, 2.75, BoatHullType.MONOHULL, true, "FE28R", "F28R"), |
|
| 59 | 60 | FARR_30 ("Farr 30", true, 9.42, 3.08, BoatHullType.MONOHULL, true, "F30", "F-30", "Farr-30"), |
| 60 | 61 | FARR_280 ("Farr 280", true, 8.72, 2.87, BoatHullType.MONOHULL, true, "F280", "F-280", "Farr-280"), |
| 61 | 62 | FINN ("Finn", true, 4.50, 1.51, BoatHullType.MONOHULL, false), |
java/com.sap.sailing.domain.igtimiadapter.gateway/resources/shiro.ini
| ... | ... | @@ -26,7 +26,7 @@ securityManager.subjectDAO = $subjectDAO |
| 26 | 26 | securityManager.sessionManager.globalSessionTimeout = 31536000000 |
| 27 | 27 | cacheManager = com.sap.sse.security.SessionCacheManager |
| 28 | 28 | securityManager.cacheManager = $cacheManager |
| 29 | -authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithLockingAndBanning |
|
| 29 | +authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithTimedLocks |
|
| 30 | 30 | securityManager.authenticator.authenticationStrategy = $authenticationStrategy |
| 31 | 31 | |
| 32 | 32 | # Support for anonymous user permissions |
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/OfflineSerializationTest.java
| ... | ... | @@ -47,11 +47,11 @@ import com.sap.sse.common.Color; |
| 47 | 47 | import com.sap.sse.common.Duration; |
| 48 | 48 | import com.sap.sse.common.Util; |
| 49 | 49 | import com.sap.sse.common.impl.MillisecondsTimePoint; |
| 50 | +import com.sap.sse.common.impl.TimedLockImpl; |
|
| 50 | 51 | import com.sap.sse.security.SecurityService; |
| 51 | 52 | import com.sap.sse.security.interfaces.UserStore; |
| 52 | 53 | import com.sap.sse.security.shared.UserGroupManagementException; |
| 53 | 54 | import com.sap.sse.security.shared.UserManagementException; |
| 54 | -import com.sap.sse.security.shared.impl.LockingAndBanningImpl; |
|
| 55 | 55 | import com.sap.sse.security.shared.impl.User; |
| 56 | 56 | import com.sap.sse.security.shared.impl.UserGroup; |
| 57 | 57 | import com.sap.sse.security.userstore.mongodb.UserStoreImpl; |
| ... | ... | @@ -111,7 +111,7 @@ public class OfflineSerializationTest extends AbstractSerializationTest { |
| 111 | 111 | UserStore userStore = new UserStoreImpl("defaultTenant"); |
| 112 | 112 | userStore.clear(); |
| 113 | 113 | UserGroup defaultTenant = userStore.createUserGroup(UUID.randomUUID(), "admin"+SecurityService.TENANT_SUFFIX); |
| 114 | - User user = userStore.createUser("admin", "", new LockingAndBanningImpl()); |
|
| 114 | + User user = userStore.createUser("admin", "", new TimedLockImpl()); |
|
| 115 | 115 | defaultTenant.add(user); |
| 116 | 116 | userStore.updateUserGroup(defaultTenant); |
| 117 | 117 | user.getDefaultTenantMap().put("testserver", defaultTenant); |
java/com.sap.sailing.gwt.ui/GWT Sailing SDM ManagementConsole.launch
| ... | ... | @@ -3,7 +3,7 @@ |
| 3 | 3 | <booleanAttribute key="com.gwtplugins.gdt.eclipse.core.RUN_SERVER" value="false"/> |
| 4 | 4 | <stringAttribute key="com.gwtplugins.gdt.eclipse.suiteMainTypeProcessor.PREVIOUSLY_SET_MAIN_TYPE_NAME" value="com.google.gwt.dev.DevMode"/> |
| 5 | 5 | <booleanAttribute key="com.gwtplugins.gdt.eclipse.suiteWarArgumentProcessor.IS_WAR_FROM_PROJECT_PROPERTIES" value="true"/> |
| 6 | - <stringAttribute key="com.gwtplugins.gwt.eclipse.core.CLASSIC_DEVMODE_CODE_SERVER_PORT" value="9876"/> |
|
| 6 | + <stringAttribute key="com.gwtplugins.gwt.eclipse.core.CLASSIC_DEVMODE_CODE_SERVER_PORT" value="9879"/> |
|
| 7 | 7 | <listAttribute key="com.gwtplugins.gwt.eclipse.core.ENTRY_POINT_MODULES"> |
| 8 | 8 | <listEntry value="com.sap.sailing.gwt.ui.AdminConsole"/> |
| 9 | 9 | <listEntry value="com.sap.sailing.gwt.home.Home"/> |
| ... | ... | @@ -104,7 +104,7 @@ |
| 104 | 104 | <booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/> |
| 105 | 105 | <stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.DevMode"/> |
| 106 | 106 | <stringAttribute key="org.eclipse.jdt.launching.MODULE_NAME" value="com.sap.sailing.gwt.ui"/> |
| 107 | - <stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-style PRETTY -incremental -war "${project_loc:com.sap.sailing.gwt.ui}" -noserver -remoteUI "${gwt_remote_ui_server_port}:${unique_id}" -logLevel INFO -codeServerPort 9876 -startupUrl /gwt/Home.html -startupUrl /gwt/AdminConsole.html com.sap.sailing.gwt.home.Home com.sap.sailing.gwt.ui.AdminConsole"/> |
|
| 107 | + <stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-style PRETTY -incremental -war "${project_loc:com.sap.sailing.gwt.ui}" -noserver -remoteUI "${gwt_remote_ui_server_port}:${unique_id}" -logLevel INFO -codeServerPort 9879 -startupUrl /gwt/Home.html -startupUrl /gwt/AdminConsole.html com.sap.sailing.gwt.home.Home com.sap.sailing.gwt.ui.AdminConsole"/> |
|
| 108 | 108 | <stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.gwt.ui"/> |
| 109 | 109 | <stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-XX:+UseG1GC -XX:+UseStringDeduplication -Dgwt.watchFileChanges=false -Xmx2048m -Dgwt-usearchives=false -Dgwt.persistentunitcache=false"/> |
| 110 | 110 | <stringAttribute key="org.eclipse.jdt.launching.WORKING_DIRECTORY" value="${project_loc:com.sap.sailing.gwt.ui}/.tmp/gwt-work"/> |
java/com.sap.sailing.gwt.ui/pom.xml
| ... | ... | @@ -12,9 +12,9 @@ |
| 12 | 12 | <packaging>eclipse-plugin</packaging> |
| 13 | 13 | |
| 14 | 14 | <dependencies> |
| 15 | - <!-- The following dependency needs re-declaration with the desired version |
|
| 16 | - because gwt-maps-api declares it with no version specifier which may be resolved |
|
| 17 | - to an incorrect version. --> |
|
| 15 | + <!-- The following dependency needs re-declaration with the desired version |
|
| 16 | + because gwt-maps-api declares it with no version specifier which may be resolved |
|
| 17 | + to an incorrect version. --> |
|
| 18 | 18 | <dependency> |
| 19 | 19 | <groupId>org.gwtproject</groupId> |
| 20 | 20 | <artifactId>gwt-user</artifactId> |
| ... | ... | @@ -165,7 +165,13 @@ |
| 165 | 165 | <execution> |
| 166 | 166 | <configuration> |
| 167 | 167 | <!-- <userAgents>chrome</userAgents> --> |
| 168 | + <!-- Uncomment this if you'd like to attach a debugger to the GWT build: |
|
| 169 | + <extraJvmArgs> |
|
| 170 | + -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:8003 |
|
| 171 | + </extraJvmArgs> |
|
| 172 | + --> |
|
| 168 | 173 | <modules> |
| 174 | + <!-- Comment all modules but DataMining when chasing GWT issue-10181: --> |
|
| 169 | 175 | <module>com.sap.sailing.gwt.home.Home</module> |
| 170 | 176 | <module>com.sap.sailing.gwt.autoplay.AutoPlay</module> |
| 171 | 177 | <module>com.sap.sailing.gwt.ui.AdminConsole</module> |
| ... | ... | @@ -175,20 +181,26 @@ |
| 175 | 181 | <module>com.sap.sailing.gwt.ui.RaceBoard</module> |
| 176 | 182 | <module>com.sap.sailing.gwt.ui.EmbeddedMapAndWindChart</module> |
| 177 | 183 | <module>com.sap.sailing.gwt.ui.Spectator</module> |
| 184 | + <!-- --> |
|
| 178 | 185 | <module>com.sap.sailing.gwt.ui.DataMining</module> |
| 186 | + <!-- Comment all modules but DataMining when chasing GWT issue-10181: --> |
|
| 179 | 187 | <module>com.sap.sailing.gwt.ui.VideoPopup</module> |
| 180 | 188 | <module>com.sap.sailing.gwt.ui.YoutubePopup</module> |
| 181 | 189 | <module>com.sap.sailing.gwt.ui.Simulator</module> |
| 182 | 190 | <module>com.sap.sailing.gwt.ui.PairingList</module> |
| 183 | 191 | <module>com.sap.sailing.gwt.regattaoverview.RegattaOverview</module> |
| 192 | + <!-- --> |
|
| 184 | 193 | </modules> |
| 185 | - <!-- uncomment the following for PRETTY-printed, non-obfuscated output |
|
| 186 | - format <style>PRETTY</style> --> |
|
| 194 | + <!-- uncomment the following for PRETTY-printed, non-obfuscated output format |
|
| 195 | + <style>PRETTY</style> |
|
| 196 | + --> |
|
| 187 | 197 | <gen>${basedir}/.generated</gen> |
| 188 | 198 | <webappDirectory>${basedir}</webappDirectory> |
| 189 | 199 | <warSourceDirectory>${basedir}/src/main/resources</warSourceDirectory> |
| 190 | 200 | <!-- Uncomment the following to obtain details split-point reports |
| 191 | - <compileReport>true</compileReport> <detailedSoyc>true</detailedSoyc> --> |
|
| 201 | + <compileReport>true</compileReport> |
|
| 202 | + <detailedSoyc>true</detailedSoyc> |
|
| 203 | + --> |
|
| 192 | 204 | </configuration> |
| 193 | 205 | <goals> |
| 194 | 206 | <goal>compile</goal> |
java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sse/security/ui/shared/IpToTimedLockDTO_CustomFieldSerializer.java
| ... | ... | @@ -0,0 +1,48 @@ |
| 1 | +package com.google.gwt.user.client.rpc.core.com.sap.sse.security.ui.shared; |
|
| 2 | + |
|
| 3 | +import com.google.gwt.user.client.rpc.CustomFieldSerializer; |
|
| 4 | +import com.google.gwt.user.client.rpc.SerializationException; |
|
| 5 | +import com.google.gwt.user.client.rpc.SerializationStreamReader; |
|
| 6 | +import com.google.gwt.user.client.rpc.SerializationStreamWriter; |
|
| 7 | +import com.sap.sse.common.TimedLock; |
|
| 8 | +import com.sap.sse.security.ui.shared.IpToTimedLockDTO; |
|
| 9 | + |
|
| 10 | +public class IpToTimedLockDTO_CustomFieldSerializer extends CustomFieldSerializer<IpToTimedLockDTO> { |
|
| 11 | + @Override |
|
| 12 | + public void serializeInstance(SerializationStreamWriter streamWriter, IpToTimedLockDTO instance) |
|
| 13 | + throws SerializationException { |
|
| 14 | + serialize(streamWriter, instance); |
|
| 15 | + } |
|
| 16 | + |
|
| 17 | + public static void serialize(SerializationStreamWriter streamWriter, IpToTimedLockDTO instance) |
|
| 18 | + throws SerializationException { |
|
| 19 | + streamWriter.writeString(instance.getIp()); |
|
| 20 | + streamWriter.writeObject(instance.getTimedLock()); |
|
| 21 | + } |
|
| 22 | + |
|
| 23 | + @Override |
|
| 24 | + public boolean hasCustomInstantiateInstance() { |
|
| 25 | + return true; |
|
| 26 | + } |
|
| 27 | + |
|
| 28 | + @Override |
|
| 29 | + public IpToTimedLockDTO instantiateInstance(SerializationStreamReader streamReader) |
|
| 30 | + throws SerializationException { |
|
| 31 | + return instantiate(streamReader); |
|
| 32 | + } |
|
| 33 | + |
|
| 34 | + public static IpToTimedLockDTO instantiate(SerializationStreamReader streamReader) |
|
| 35 | + throws SerializationException { |
|
| 36 | + return new IpToTimedLockDTO(streamReader.readString(), (TimedLock) streamReader.readObject()); |
|
| 37 | + } |
|
| 38 | + |
|
| 39 | + @Override |
|
| 40 | + public void deserializeInstance(SerializationStreamReader streamReader, IpToTimedLockDTO instance) |
|
| 41 | + throws SerializationException { |
|
| 42 | + deserialize(streamReader, instance); |
|
| 43 | + } |
|
| 44 | + |
|
| 45 | + public static void deserialize(SerializationStreamReader streamReader, IpToTimedLockDTO instance) { |
|
| 46 | + // Done by instantiateInstance |
|
| 47 | + } |
|
| 48 | +} |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/BoatClassImageResolver.java
| ... | ... | @@ -72,6 +72,7 @@ public class BoatClassImageResolver { |
| 72 | 72 | boatClassIconsMap.put(BoatClassMasterdata.D_35.getDisplayName(), imageResources.D35Icon()); |
| 73 | 73 | boatClassIconsMap.put(BoatClassMasterdata.F_16.getDisplayName(), imageResources.F16Icon()); |
| 74 | 74 | boatClassIconsMap.put(BoatClassMasterdata.F_18.getDisplayName(), imageResources.F18Icon()); |
| 75 | + boatClassIconsMap.put(BoatClassMasterdata.FAREAST28R.getDisplayName(), imageResources.Fareast28RIcon()); |
|
| 75 | 76 | boatClassIconsMap.put(BoatClassMasterdata.FARR_30.getDisplayName(), imageResources.Farr30Icon()); |
| 76 | 77 | boatClassIconsMap.put(BoatClassMasterdata.FARR_280.getDisplayName(), imageResources.Farr280Icon()); |
| 77 | 78 | boatClassIconsMap.put(BoatClassMasterdata.FINN.getDisplayName(), imageResources.FinnIcon()); |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/BoatClassImageResources.java
| ... | ... | @@ -188,6 +188,10 @@ public interface BoatClassImageResources extends ClientBundle { |
| 188 | 188 | @ImageOptions(preventInlining = true) |
| 189 | 189 | ImageResource F18Icon(); |
| 190 | 190 | |
| 191 | + @Source("com/sap/sailing/gwt/ui/client/images/boatclass/FAREAST28R.png") |
|
| 192 | + @ImageOptions(preventInlining = true) |
|
| 193 | + ImageResource Fareast28RIcon(); |
|
| 194 | + |
|
| 191 | 195 | @Source("com/sap/sailing/gwt/ui/client/images/boatclass/FARR_30.png") |
| 192 | 196 | @ImageOptions(preventInlining = true) |
| 193 | 197 | ImageResource Farr30Icon(); |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/whatsnew/resources/SailingAnalyticsNotes.html
| ... | ... | @@ -5,6 +5,10 @@ |
| 5 | 5 | <div id="mainContent"> |
| 6 | 6 | <h4 class="articleHeadline" id="sailingAnalyticsHeadline">What's New - Sailing Analytics</h4> |
| 7 | 7 | <div class="innerContent"> |
| 8 | + <h5 class="articleSubheadline">December 2025</h5> |
|
| 9 | + <ul class="bulletList"> |
|
| 10 | + <li>Added boat class Fareast 28R.</li> |
|
| 11 | + </ul> |
|
| 8 | 12 | <h5 class="articleSubheadline">October 2025</h5> |
| 9 | 13 | <ul class="bulletList"> |
| 10 | 14 | <li>Bug fix: the "Edit Mark Passings" panel showed only up to 15 waypoints.</li> |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/IPBlocklistTableWrapper.java
| ... | ... | @@ -0,0 +1,172 @@ |
| 1 | +package com.sap.sailing.gwt.ui.adminconsole; |
|
| 2 | + |
|
| 3 | +import java.util.ArrayList; |
|
| 4 | +import java.util.Comparator; |
|
| 5 | +import java.util.List; |
|
| 6 | + |
|
| 7 | +import com.google.gwt.event.dom.client.ClickEvent; |
|
| 8 | +import com.google.gwt.event.dom.client.ClickHandler; |
|
| 9 | +import com.google.gwt.user.cellview.client.AbstractCellTable; |
|
| 10 | +import com.google.gwt.user.cellview.client.ColumnSortEvent.ListHandler; |
|
| 11 | +import com.google.gwt.user.client.rpc.AsyncCallback; |
|
| 12 | +import com.google.gwt.user.client.ui.Button; |
|
| 13 | +import com.google.gwt.user.client.ui.HasVerticalAlignment; |
|
| 14 | +import com.google.gwt.user.client.ui.HorizontalPanel; |
|
| 15 | +import com.google.gwt.user.client.ui.Label; |
|
| 16 | +import com.google.gwt.user.client.ui.Widget; |
|
| 17 | +import com.sap.sailing.gwt.ui.client.SailingServiceWriteAsync; |
|
| 18 | +import com.sap.sailing.gwt.ui.client.StringMessages; |
|
| 19 | +import com.sap.sse.gwt.client.ErrorReporter; |
|
| 20 | +import com.sap.sse.gwt.client.celltable.EntityIdentityComparator; |
|
| 21 | +import com.sap.sse.gwt.client.celltable.RefreshableSelectionModel; |
|
| 22 | +import com.sap.sse.gwt.client.panels.LabeledAbstractFilterablePanel; |
|
| 23 | +import com.sap.sse.security.shared.HasPermissions.DefaultActions; |
|
| 24 | +import com.sap.sse.security.shared.WildcardPermission; |
|
| 25 | +import com.sap.sse.security.shared.impl.SecuredSecurityTypes; |
|
| 26 | +import com.sap.sse.security.ui.client.UserService; |
|
| 27 | +import com.sap.sse.security.ui.client.component.SelectedElementsCountingButton; |
|
| 28 | +import com.sap.sse.security.ui.shared.IpToTimedLockDTO; |
|
| 29 | + |
|
| 30 | +abstract class IPBlocklistTableWrapper |
|
| 31 | + extends TableWrapper<IpToTimedLockDTO, RefreshableSelectionModel<IpToTimedLockDTO>> { |
|
| 32 | + private final UserService userService; |
|
| 33 | + private final LabeledAbstractFilterablePanel<IpToTimedLockDTO> filterField; |
|
| 34 | + private final String errorMessageOnDataFailureString; |
|
| 35 | + |
|
| 36 | + protected abstract void fetchData(AsyncCallback<ArrayList<IpToTimedLockDTO>> callback); |
|
| 37 | + |
|
| 38 | + protected abstract void unlockIP(String ip, AsyncCallback<Void> asyncCallback); |
|
| 39 | + |
|
| 40 | + public IPBlocklistTableWrapper(final SailingServiceWriteAsync sailingServiceWrite, final UserService userService, |
|
| 41 | + final String errorMessageOnDataFailureString, final StringMessages stringMessages, |
|
| 42 | + final ErrorReporter errorReporter) { |
|
| 43 | + super(sailingServiceWrite, stringMessages, errorReporter, true, true, |
|
| 44 | + new EntityIdentityComparator<IpToTimedLockDTO>() { |
|
| 45 | + @Override |
|
| 46 | + public boolean representSameEntity(IpToTimedLockDTO dto1, IpToTimedLockDTO dto2) { |
|
| 47 | + return dto1.getIp().equals(dto2.getIp()); |
|
| 48 | + } |
|
| 49 | + |
|
| 50 | + @Override |
|
| 51 | + public int hashCode(IpToTimedLockDTO t) { |
|
| 52 | + return t.getIp().hashCode(); |
|
| 53 | + } |
|
| 54 | + }); |
|
| 55 | + this.userService = userService; |
|
| 56 | + this.errorMessageOnDataFailureString = errorMessageOnDataFailureString; |
|
| 57 | + this.filterField = composeFilterField(); |
|
| 58 | + this.asWidget().ensureDebugId("wrappedTable"); |
|
| 59 | + this.table.ensureDebugId("cellTable"); |
|
| 60 | + configureDataColumns(); |
|
| 61 | + setButtonsAndFilterOnMainPanel(); |
|
| 62 | + loadDataAndPopulateTable(); |
|
| 63 | + } |
|
| 64 | + |
|
| 65 | + private void setButtonsAndFilterOnMainPanel() { |
|
| 66 | + final HorizontalPanel searchPanel = new HorizontalPanel(); |
|
| 67 | + searchPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE); |
|
| 68 | + searchPanel.setSpacing(5); |
|
| 69 | + final Label label = new Label(getStringMessages().filterIpAddresses() + ": "); |
|
| 70 | + searchPanel.add(label); |
|
| 71 | + searchPanel.add(filterField.getTextBox()); |
|
| 72 | + // inserted with indices to put them above the table |
|
| 73 | + mainPanel.insert(searchPanel, 0); |
|
| 74 | + mainPanel.insert(composeButtonPanel(), 1); |
|
| 75 | + mainPanel.setSpacing(5); |
|
| 76 | + } |
|
| 77 | + |
|
| 78 | + private Widget composeButtonPanel() { |
|
| 79 | + final HorizontalPanel buttonPanel = new HorizontalPanel(); |
|
| 80 | + buttonPanel.setSpacing(5); |
|
| 81 | + final Button refreshButton = new Button(getStringMessages().refresh(), new ClickHandler() { |
|
| 82 | + @Override |
|
| 83 | + public void onClick(ClickEvent event) { |
|
| 84 | + loadDataAndPopulateTable(); |
|
| 85 | + } |
|
| 86 | + }); |
|
| 87 | + refreshButton.ensureDebugId("refreshButton"); |
|
| 88 | + buttonPanel.add(refreshButton); |
|
| 89 | + if (hasUnlockPermission()) { |
|
| 90 | + final Button unlockButton = composeUnlockButton(); |
|
| 91 | + unlockButton.ensureDebugId("unlockButton"); |
|
| 92 | + buttonPanel.add(unlockButton); |
|
| 93 | + } |
|
| 94 | + return buttonPanel; |
|
| 95 | + } |
|
| 96 | + |
|
| 97 | + private boolean hasUnlockPermission() { |
|
| 98 | + final WildcardPermission unlockIpPermission = SecuredSecurityTypes.LOCKED_IP |
|
| 99 | + .getPermission(DefaultActions.DELETE); |
|
| 100 | + return userService.hasPermission(unlockIpPermission, userService.getServerInfo().getOwnership()); |
|
| 101 | + } |
|
| 102 | + |
|
| 103 | + private SelectedElementsCountingButton<IpToTimedLockDTO> composeUnlockButton() { |
|
| 104 | + return new SelectedElementsCountingButton<IpToTimedLockDTO>(getStringMessages().unlock(), getSelectionModel(), |
|
| 105 | + new ClickHandler() { |
|
| 106 | + @Override |
|
| 107 | + public void onClick(ClickEvent event) { |
|
| 108 | + for (IpToTimedLockDTO e : getSelectionModel().getSelectedSet()) { |
|
| 109 | + unlockIP(e.getIp(), new AsyncCallback<Void>() { |
|
| 110 | + @Override |
|
| 111 | + public void onFailure(Throwable caught) { |
|
| 112 | + errorReporter.reportError(errorMessageOnDataFailureString); |
|
| 113 | + } |
|
| 114 | + |
|
| 115 | + @Override |
|
| 116 | + public void onSuccess(Void result) { |
|
| 117 | + filterField.remove(e); |
|
| 118 | + } |
|
| 119 | + }); |
|
| 120 | + } |
|
| 121 | + } |
|
| 122 | + }); |
|
| 123 | + } |
|
| 124 | + |
|
| 125 | + private void loadDataAndPopulateTable() { |
|
| 126 | + final AsyncCallback<ArrayList<IpToTimedLockDTO>> dataInitializationCallback = new AsyncCallback<ArrayList<IpToTimedLockDTO>>() { |
|
| 127 | + @Override |
|
| 128 | + public void onFailure(Throwable caught) { |
|
| 129 | + errorReporter.reportError(errorMessageOnDataFailureString); |
|
| 130 | + } |
|
| 131 | + |
|
| 132 | + @Override |
|
| 133 | + public void onSuccess(ArrayList<IpToTimedLockDTO> result) { |
|
| 134 | + filterField.clear(); |
|
| 135 | + clear(); |
|
| 136 | + filterField.addAll(result); |
|
| 137 | + } |
|
| 138 | + }; |
|
| 139 | + fetchData(dataInitializationCallback); |
|
| 140 | + } |
|
| 141 | + |
|
| 142 | + private void configureDataColumns() { |
|
| 143 | + final ListHandler<IpToTimedLockDTO> columnListHandler = getColumnSortHandler(); |
|
| 144 | + addColumn(record -> record.getIp(), getStringMessages().ipAddress()); |
|
| 145 | + final Comparator<IpToTimedLockDTO> expiryComparator = (o1, o2) -> { |
|
| 146 | + return o1.getTimedLock().getLockedUntil().compareTo(o2.getTimedLock().getLockedUntil()); |
|
| 147 | + }; |
|
| 148 | + addColumn(record -> record.getTimedLock().getLockedUntil().toString(), getStringMessages().lockedUntil(), |
|
| 149 | + expiryComparator); |
|
| 150 | + table.addColumnSortHandler(columnListHandler); |
|
| 151 | + } |
|
| 152 | + |
|
| 153 | + private LabeledAbstractFilterablePanel<IpToTimedLockDTO> composeFilterField() { |
|
| 154 | + final LabeledAbstractFilterablePanel<IpToTimedLockDTO> filterField = new LabeledAbstractFilterablePanel<IpToTimedLockDTO>( |
|
| 155 | + new Label(getStringMessages().filterIpAddresses()), new ArrayList<>(), getDataProvider(), |
|
| 156 | + getStringMessages()) { |
|
| 157 | + @Override |
|
| 158 | + public Iterable<String> getSearchableStrings(IpToTimedLockDTO dto) { |
|
| 159 | + final List<String> string = new ArrayList<String>(); |
|
| 160 | + string.add(dto.getIp()); |
|
| 161 | + return string; |
|
| 162 | + } |
|
| 163 | + |
|
| 164 | + @Override |
|
| 165 | + public AbstractCellTable<IpToTimedLockDTO> getCellTable() { |
|
| 166 | + return table; |
|
| 167 | + } |
|
| 168 | + }; |
|
| 169 | + registerSelectionModelOnNewDataProvider(filterField.getAllListDataProvider()); |
|
| 170 | + return filterField; |
|
| 171 | + } |
|
| 172 | +} |
|
| ... | ... | \ No newline at end of file |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LocalServerManagementPanel.java
| ... | ... | @@ -52,6 +52,7 @@ import com.sap.sse.security.ui.client.UserStatusEventHandler; |
| 52 | 52 | import com.sap.sse.security.ui.client.component.AccessControlledButtonPanel; |
| 53 | 53 | import com.sap.sse.security.ui.client.component.EditOwnershipDialog; |
| 54 | 54 | import com.sap.sse.security.ui.client.component.editacl.EditACLDialog; |
| 55 | +import com.sap.sse.security.ui.shared.IpToTimedLockDTO; |
|
| 55 | 56 | |
| 56 | 57 | public class LocalServerManagementPanel extends SimplePanel { |
| 57 | 58 | private final SailingServiceWriteAsync sailingService; |
| ... | ... | @@ -87,6 +88,8 @@ public class LocalServerManagementPanel extends SimplePanel { |
| 87 | 88 | mainPanel.add(this.buttonPanel = createServerActionsUi(userService)); |
| 88 | 89 | mainPanel.add(createServerInfoUI()); |
| 89 | 90 | mainPanel.add(createServerConfigurationUI()); |
| 91 | + mainPanel.add(createBearerTokenAbusePanel()); |
|
| 92 | + mainPanel.add(createUserCreationAbusePanel()); |
|
| 90 | 93 | refreshServerConfiguration(); |
| 91 | 94 | if (userService.hasServerPermission(ServerActions.CONFIGURE_CORS_FILTER)) { |
| 92 | 95 | mainPanel.add(createCORSFilterConfigurationUI()); |
| ... | ... | @@ -157,6 +160,44 @@ public class LocalServerManagementPanel extends SimplePanel { |
| 157 | 160 | return captionPanel; |
| 158 | 161 | } |
| 159 | 162 | |
| 163 | + private Widget createBearerTokenAbusePanel() { |
|
| 164 | + final ServerDataCaptionPanel panel = new ServerDataCaptionPanel(stringMessages.ipsLockedForBearerTokenAbuse(), 3); |
|
| 165 | + panel.ensureDebugId("bearerTokenAbusePanel"); |
|
| 166 | + final IPBlocklistTableWrapper table = new IPBlocklistTableWrapper(sailingService, userService, |
|
| 167 | + stringMessages.unableToLoadIpsBlockedForBearerTokenAbuse(), stringMessages, errorReporter) { |
|
| 168 | + @Override |
|
| 169 | + protected void fetchData(AsyncCallback<ArrayList<IpToTimedLockDTO>> callback) { |
|
| 170 | + userService.getUserManagementService().getClientIPBasedTimedLocksForBearerTokenAbuse(callback); |
|
| 171 | + } |
|
| 172 | + |
|
| 173 | + @Override |
|
| 174 | + protected void unlockIP(String ip, AsyncCallback<Void> asyncCallback) { |
|
| 175 | + userService.getUserManagementWriteService().releaseBearerTokenLockOnIp(ip, asyncCallback); |
|
| 176 | + } |
|
| 177 | + }; |
|
| 178 | + panel.setContentWidget(table.asWidget()); |
|
| 179 | + return panel; |
|
| 180 | + } |
|
| 181 | + |
|
| 182 | + private Widget createUserCreationAbusePanel() { |
|
| 183 | + final ServerDataCaptionPanel panel = new ServerDataCaptionPanel(stringMessages.ipsLockedForUserCreationAbuse(), 3); |
|
| 184 | + panel.ensureDebugId("userCreationAbusePanel"); |
|
| 185 | + final IPBlocklistTableWrapper table = new IPBlocklistTableWrapper(sailingService, userService, |
|
| 186 | + stringMessages.unableToLoadIpsBlockedForUserCreationAbuse(), stringMessages, errorReporter) { |
|
| 187 | + @Override |
|
| 188 | + protected void fetchData(AsyncCallback<ArrayList<IpToTimedLockDTO>> callback) { |
|
| 189 | + userService.getUserManagementService().getClientIPBasedTimedLocksForUserCreation(callback); |
|
| 190 | + } |
|
| 191 | + |
|
| 192 | + @Override |
|
| 193 | + protected void unlockIP(String ip, AsyncCallback<Void> asyncCallback) { |
|
| 194 | + userService.getUserManagementWriteService().releaseUserCreationLockOnIp(ip, asyncCallback); |
|
| 195 | + } |
|
| 196 | + }; |
|
| 197 | + panel.setContentWidget(table.asWidget()); |
|
| 198 | + return panel; |
|
| 199 | + } |
|
| 200 | + |
|
| 160 | 201 | private Widget createCORSFilterConfigurationUI() { |
| 161 | 202 | final ServerDataCaptionPanel captionPanel = new ServerDataCaptionPanel(stringMessages.corsAndCSPFilterConfiguration(), 4); |
| 162 | 203 | captionPanel.addWidget("", new Label(stringMessages.corsAndCSPFilterConfigurationHint())); |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java
| ... | ... | @@ -2547,4 +2547,11 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, |
| 2547 | 2547 | String strategySimulatorReadMore(); |
| 2548 | 2548 | String testConnection(); |
| 2549 | 2549 | String tracTracConnectionTestFailed(String message); |
| 2550 | + String ipsLockedForBearerTokenAbuse(); |
|
| 2551 | + String unableToLoadIpsBlockedForBearerTokenAbuse(); |
|
| 2552 | + String ipAddress(); |
|
| 2553 | + String filterIpAddresses(); |
|
| 2554 | + String unlock(); |
|
| 2555 | + String ipsLockedForUserCreationAbuse(); |
|
| 2556 | + String unableToLoadIpsBlockedForUserCreationAbuse(); |
|
| 2550 | 2557 | } |
| ... | ... | \ No newline at end of file |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties
| ... | ... | @@ -2582,4 +2582,11 @@ exportTWAHistogramToCsv=Export True Wind Angle histogram to CSV |
| 2582 | 2582 | exportWindSpeedHistogramToCsv=Export Wind Speed histogram to CSV |
| 2583 | 2583 | optionalBearerTokenForWindImport=Optional bearer token for wind import |
| 2584 | 2584 | testConnection=Test Connection |
| 2585 | -tracTracConnectionTestFailed=TracTrac connection test failed: {0} |
|
| ... | ... | \ No newline at end of file |
| 0 | +tracTracConnectionTestFailed=TracTrac connection test failed: {0} |
|
| 1 | +ipsLockedForBearerTokenAbuse=IPs locked for Bearer Token abuse |
|
| 2 | +unableToLoadIpsBlockedForBearerTokenAbuse=Unable to load IPs blocked for bearer token abuse |
|
| 3 | +ipAddress=IP Address |
|
| 4 | +filterIpAddresses=Filter IP Addresses |
|
| 5 | +unlock=Unlock |
|
| 6 | +ipsLockedForUserCreationAbuse=IPs Locked for User Creation Abuse |
|
| 7 | +unableToLoadIpsBlockedForUserCreationAbuse=Unable to load IPs Blocked for User Creation Abuse |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties
| ... | ... | @@ -2576,4 +2576,11 @@ exportTWAHistogramToCsv=Histogramm der wahren Windwinkel in CSV exportieren |
| 2576 | 2576 | exportWindSpeedHistogramToCsv=Histogramm der wahren Windgeschwindigkeiten in CSV exportieren |
| 2577 | 2577 | optionalBearerTokenForWindImport=Optionales Bearer Token für Wind-Import |
| 2578 | 2578 | testConnection=Verbindung testen |
| 2579 | -tracTracConnectionTestFailed=TracTrac-Verbindungstest fehlgeschlagen: {0} |
|
| ... | ... | \ No newline at end of file |
| 0 | +tracTracConnectionTestFailed=TracTrac-Verbindungstest fehlgeschlagen: {0} |
|
| 1 | +ipsLockedForBearerTokenAbuse=IPs wegen Missbrauchs von Bearer-Token gesperrt |
|
| 2 | +unableToLoadIpsBlockedForBearerTokenAbuse=Aufgrund von Missbrauch des Inhabertokens blockierte IPs können nicht geladen werden |
|
| 3 | +ipAddress=IP-Addresse |
|
| 4 | +filterIpAddresses=IP-Adressen filtern |
|
| 5 | +unlock=Entsperren |
|
| 6 | +ipsLockedForUserCreationAbuse=Wegen Missbrauchs bei der Benutzererstellung gesperrte IPs |
|
| 7 | +unableToLoadIpsBlockedForUserCreationAbuse=Wegen Missbrauchs der Benutzererstellung blockierte IPs können nicht geladen werden |
java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/client/images/boatclass/FAREAST28R.png
| ... | ... | Binary files /dev/null and b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/client/images/boatclass/FAREAST28R.png differ |
java/com.sap.sailing.gwt.ui/src/main/resources/shiro.ini
| ... | ... | @@ -22,7 +22,7 @@ securityManager.sessionManager.sessionDAO = $sessionDAO |
| 22 | 22 | securityManager.sessionManager.globalSessionTimeout = 31536000000 |
| 23 | 23 | cacheManager = com.sap.sse.security.SessionCacheManager |
| 24 | 24 | securityManager.cacheManager = $cacheManager |
| 25 | -authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithLockingAndBanning |
|
| 25 | +authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithTimedLocks |
|
| 26 | 26 | securityManager.authenticator.authenticationStrategy = $authenticationStrategy |
| 27 | 27 | |
| 28 | 28 | subjectDAO = com.sap.sse.security.NoSessionStorageForUnauthenticatedSessionsSessionDAO |
java/com.sap.sailing.hanaexport/resources/shiro.ini
| ... | ... | @@ -26,7 +26,7 @@ securityManager.subjectDAO = $subjectDAO |
| 26 | 26 | securityManager.sessionManager.globalSessionTimeout = 31536000000 |
| 27 | 27 | cacheManager = com.sap.sse.security.SessionCacheManager |
| 28 | 28 | securityManager.cacheManager = $cacheManager |
| 29 | -authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithLockingAndBanning |
|
| 29 | +authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithTimedLocks |
|
| 30 | 30 | securityManager.authenticator.authenticationStrategy = $authenticationStrategy |
| 31 | 31 | |
| 32 | 32 | # Support for anonymous user permissions |
java/com.sap.sailing.landscape.gateway/resources/shiro.ini
| ... | ... | @@ -26,7 +26,7 @@ securityManager.subjectDAO = $subjectDAO |
| 26 | 26 | securityManager.sessionManager.globalSessionTimeout = 31536000000 |
| 27 | 27 | cacheManager = com.sap.sse.security.SessionCacheManager |
| 28 | 28 | securityManager.cacheManager = $cacheManager |
| 29 | -authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithLockingAndBanning |
|
| 29 | +authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithTimedLocks |
|
| 30 | 30 | securityManager.authenticator.authenticationStrategy = $authenticationStrategy |
| 31 | 31 | |
| 32 | 32 | # Support for anonymous user permissions |
java/com.sap.sailing.landscape.ui/resources/shiro.ini
| ... | ... | @@ -24,7 +24,7 @@ securityManager.sessionManager.sessionDAO = $sessionDAO |
| 24 | 24 | securityManager.sessionManager.globalSessionTimeout = 31536000000 |
| 25 | 25 | cacheManager = com.sap.sse.security.SessionCacheManager |
| 26 | 26 | securityManager.cacheManager = $cacheManager |
| 27 | -authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithLockingAndBanning |
|
| 27 | +authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithTimedLocks |
|
| 28 | 28 | securityManager.authenticator.authenticationStrategy = $authenticationStrategy |
| 29 | 29 | |
| 30 | 30 | subjectDAO = com.sap.sse.security.NoSessionStorageForUnauthenticatedSessionsSessionDAO |
java/com.sap.sailing.landscape.ui/src/com/sap/sailing/landscape/ui/client/LandscapeManagementPanel.java
| ... | ... | @@ -1561,7 +1561,7 @@ public class LandscapeManagementPanel extends SimplePanel { |
| 1561 | 1561 | new AsyncCallback<Void>() { |
| 1562 | 1562 | @Override |
| 1563 | 1563 | public void onSuccess(Void result) { |
| 1564 | - Notification.notify(stringMessages.success(), NotificationType.SUCCESS); |
|
| 1564 | + Notification.notify(stringMessages.unlockedSuccessfully(), NotificationType.SUCCESS); |
|
| 1565 | 1565 | proxiesTableBusy.setBusy(false); |
| 1566 | 1566 | refreshProxiesTable(); |
| 1567 | 1567 | } |
java/com.sap.sailing.landscape.ui/src/com/sap/sailing/landscape/ui/client/i18n/StringMessages.java
| ... | ... | @@ -172,7 +172,7 @@ com.sap.sse.gwt.adminconsole.StringMessages { |
| 172 | 172 | String successfullyRotatedHttpdLogsOnInstance(String instance); |
| 173 | 173 | String invalidOperationForThisProxy(); |
| 174 | 174 | String pleaseProvideNonEmptyNameAndAZ(); |
| 175 | - String success(); |
|
| 175 | + String unlockedSuccessfully(); |
|
| 176 | 176 | String availabilityZone(); |
| 177 | 177 | String runOnExisting(); |
| 178 | 178 | String publicIp(); |
java/com.sap.sailing.polars/resources/shiro.ini
| ... | ... | @@ -26,7 +26,7 @@ securityManager.subjectDAO = $subjectDAO |
| 26 | 26 | securityManager.sessionManager.globalSessionTimeout = 31536000000 |
| 27 | 27 | cacheManager = com.sap.sse.security.SessionCacheManager |
| 28 | 28 | securityManager.cacheManager = $cacheManager |
| 29 | -authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithLockingAndBanning |
|
| 29 | +authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithTimedLocks |
|
| 30 | 30 | securityManager.authenticator.authenticationStrategy = $authenticationStrategy |
| 31 | 31 | |
| 32 | 32 | # Support for anonymous user permissions |
java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/api/core/ApiContext.java
| ... | ... | @@ -30,6 +30,7 @@ public class ApiContext { |
| 30 | 30 | public static final String SECURITY_CONTEXT = "security"; //$NON-NLS-1$ |
| 31 | 31 | public static final String ADMIN_USERNAME = "admin"; //$NON-NLS-1$ |
| 32 | 32 | public static final String ADMIN_PASSWORD = "admin"; //$NON-NLS-1$ |
| 33 | + public static final String INVALID_TOKEN_SAMPLE = "INVALID_TOKEN_SAMPLE"; |
|
| 33 | 34 | |
| 34 | 35 | private static final Logger logger = Logger.getLogger(ApiContext.class.getName()); |
| 35 | 36 | |
| ... | ... | @@ -75,6 +76,19 @@ public class ApiContext { |
| 75 | 76 | } |
| 76 | 77 | |
| 77 | 78 | /** |
| 79 | + * Creates an ApiContext with an invalid token. Useful for testing. |
|
| 80 | + * |
|
| 81 | + * @param contextRoot |
|
| 82 | + * server instance |
|
| 83 | + * @param context |
|
| 84 | + * web application context |
|
| 85 | + * @return ApiContext with invalid token |
|
| 86 | + */ |
|
| 87 | + public static ApiContext createApiContextWithInvalidToken(String contextRoot, String context) { |
|
| 88 | + return new ApiContext(contextRoot, context, INVALID_TOKEN_SAMPLE); |
|
| 89 | + } |
|
| 90 | + |
|
| 91 | + /** |
|
| 78 | 92 | * Creates an ApiContext with administrator privileges. |
| 79 | 93 | * |
| 80 | 94 | * @param contextRoot |
java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/core/SeleniumTestInvocationProvider.java
| ... | ... | @@ -18,9 +18,10 @@ import com.sap.sailing.selenium.test.AbstractSeleniumTest; |
| 18 | 18 | * Used to extend the {@link SeleniumTestCase} annotation which in turn is used to mark the test methods of all Selenium |
| 19 | 19 | * tests declared in subclasses of {@link AbstractSeleniumTest}. This provider produces test invocation contexts, one |
| 20 | 20 | * for each {@link TestEnvironmentConfiguration#getDriverDefinitions() driver definition} found in the test environment |
| 21 | - * configuration. These contexts provide a test instance-specific extension of type {@link SeleniumTestEnvironmentInjector} |
|
| 22 | - * which is in particular a {@link TestInstancePostProcessor} that creates and injects a {@link TestEnvironment} created |
|
| 23 | - * for the driver definition known by the parameter resolver.<p> |
|
| 21 | + * configuration. These contexts provide a test instance-specific extension of type |
|
| 22 | + * {@link SeleniumTestEnvironmentInjector} which is in particular a {@link TestInstancePostProcessor} that creates and |
|
| 23 | + * injects a {@link TestEnvironment} created for the driver definition known by the parameter resolver. |
|
| 24 | + * <p> |
|
| 24 | 25 | * |
| 25 | 26 | * @author Axel Uhl (d043530) |
| 26 | 27 | * |
java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/ActionsHelper.java
| ... | ... | @@ -23,8 +23,9 @@ public class ActionsHelper { |
| 23 | 23 | public static final String UNLINK_RACE_ACTION = "ACTION_UNLINK"; |
| 24 | 24 | public static final String REFRESH_RACE_ACTION = "ACTION_REFRESH_RACE"; |
| 25 | 25 | public static final String SET_START_TIME_ACTION = "ACTION_SET_STARTTIME"; |
| 26 | - |
|
| 26 | + |
|
| 27 | 27 | public static final String UPDATE_ACTION = "UPDATE"; |
| 28 | + public static final String UNLOCK_ACTION = "MANAGE_LOCK"; |
|
| 28 | 29 | public static final String DELETE_ACTION = "DELETE"; |
| 29 | 30 | public static final String CHANGE_OWNERSHIP_ACTION = "CHANGE_OWNERSHIP"; |
| 30 | 31 | |
| ... | ... | @@ -59,6 +60,10 @@ public class ActionsHelper { |
| 59 | 60 | public static WebElement findUpdateAction(SearchContext context) { |
| 60 | 61 | return context.findElement(By.xpath(String.format(ACTION_XPATH, UPDATE_ACTION))); |
| 61 | 62 | } |
| 63 | + |
|
| 64 | + public static WebElement findUnlockAction(SearchContext context) { |
|
| 65 | + return context.findElement(By.xpath(String.format(ACTION_XPATH, UNLOCK_ACTION))); |
|
| 66 | + } |
|
| 62 | 67 | |
| 63 | 68 | public static WebElement findDeleteAction(SearchContext context) { |
| 64 | 69 | return context.findElement(By.xpath(String.format(ACTION_XPATH, DELETE_ACTION))); |
java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/advanced/IpBlocklistPanelPO.java
| ... | ... | @@ -0,0 +1,75 @@ |
| 1 | +package com.sap.sailing.selenium.pages.adminconsole.advanced; |
|
| 2 | + |
|
| 3 | +import org.openqa.selenium.WebDriver; |
|
| 4 | +import org.openqa.selenium.WebElement; |
|
| 5 | + |
|
| 6 | +import com.sap.sailing.selenium.core.BySeleniumId; |
|
| 7 | +import com.sap.sailing.selenium.core.FindBy; |
|
| 8 | +import com.sap.sailing.selenium.pages.PageArea; |
|
| 9 | +import com.sap.sailing.selenium.pages.gwt.CellTablePO; |
|
| 10 | +import com.sap.sailing.selenium.pages.gwt.DataEntryPO; |
|
| 11 | + |
|
| 12 | +public class IpBlocklistPanelPO extends PageArea { |
|
| 13 | + static class IPBlocklistTablePO extends CellTablePO<IPLockEntry> { |
|
| 14 | + public IPBlocklistTablePO(WebDriver driver, WebElement element) { |
|
| 15 | + super(driver, element); |
|
| 16 | + } |
|
| 17 | + |
|
| 18 | + @Override |
|
| 19 | + protected IPLockEntry createDataEntry(WebElement element) { |
|
| 20 | + return new IPLockEntry(this, element); |
|
| 21 | + } |
|
| 22 | + |
|
| 23 | + } |
|
| 24 | + |
|
| 25 | + public static class IPLockEntry extends DataEntryPO { |
|
| 26 | + private static final String IP_COLUMN = "IP Address"; |
|
| 27 | + private static final String LOCKED_UNTIL_COLUMN = "Locked until"; |
|
| 28 | + |
|
| 29 | + protected IPLockEntry(CellTablePO<IPLockEntry> table, WebElement element) { |
|
| 30 | + super(table, element); |
|
| 31 | + } |
|
| 32 | + |
|
| 33 | + @Override |
|
| 34 | + public String getIdentifier() { |
|
| 35 | + return getIp(); |
|
| 36 | + } |
|
| 37 | + |
|
| 38 | + public String getIp() { |
|
| 39 | + return getColumnContent(IP_COLUMN); |
|
| 40 | + } |
|
| 41 | + |
|
| 42 | + public String getLockedUntil() { |
|
| 43 | + return getColumnContent(LOCKED_UNTIL_COLUMN); |
|
| 44 | + } |
|
| 45 | + } |
|
| 46 | + |
|
| 47 | + public IpBlocklistPanelPO(WebDriver driver, WebElement element) { |
|
| 48 | + super(driver, element); |
|
| 49 | + final WebElement cellTableWebElement = this.findElementBySeleniumId("cellTable"); |
|
| 50 | + this.cellTable = new IPBlocklistTablePO(driver, cellTableWebElement); |
|
| 51 | + } |
|
| 52 | + |
|
| 53 | + @FindBy(how = BySeleniumId.class, using = "refreshButton") |
|
| 54 | + private WebElement refreshButton; |
|
| 55 | + |
|
| 56 | + @FindBy(how = BySeleniumId.class, using = "unlockButton") |
|
| 57 | + private WebElement unlockButton; |
|
| 58 | + |
|
| 59 | + private final IPBlocklistTablePO cellTable; |
|
| 60 | + |
|
| 61 | + public void refresh() { |
|
| 62 | + refreshButton.click(); |
|
| 63 | + } |
|
| 64 | + |
|
| 65 | + public boolean isIpInTable(final String ip) { |
|
| 66 | + final IPLockEntry entry = cellTable.getEntry(ip); |
|
| 67 | + final boolean wasFound = entry != null; |
|
| 68 | + return wasFound; |
|
| 69 | + } |
|
| 70 | + |
|
| 71 | + public void unblockIP(String ip) { |
|
| 72 | + cellTable.getEntry(ip).select(); |
|
| 73 | + unlockButton.click(); |
|
| 74 | + } |
|
| 75 | +} |
java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/advanced/LocalServerPO.java
| ... | ... | @@ -15,12 +15,28 @@ public class LocalServerPO extends PageArea { |
| 15 | 15 | |
| 16 | 16 | @FindBy(how = BySeleniumId.class, using = "isSelfServiceServerCheckbox-input") |
| 17 | 17 | private WebElement isSelfServiceServerCheckbox; |
| 18 | - |
|
| 18 | + |
|
| 19 | 19 | @FindBy(how = BySeleniumId.class, using = "isPublicServerCheckbox-input") |
| 20 | 20 | private WebElement isPublicServerCheckbox; |
| 21 | - |
|
| 21 | + |
|
| 22 | 22 | @FindBy(how = BySeleniumId.class, using = "isStandaloneServerCheckbox-input") |
| 23 | 23 | private WebElement isStandaloneServerCheckbox; |
| 24 | + |
|
| 25 | + @FindBy(how = BySeleniumId.class, using = "bearerTokenAbusePanel") |
|
| 26 | + private WebElement bearerTokenAbusePanel; |
|
| 27 | + |
|
| 28 | + @FindBy(how = BySeleniumId.class, using = "userCreationAbusePanel") |
|
| 29 | + private WebElement userCreationAbusePanel; |
|
| 30 | + |
|
| 31 | + public IpBlocklistPanelPO getBearerTokenAbusePO() { |
|
| 32 | + final WebElement wrappedTable = bearerTokenAbusePanel.findElement(new BySeleniumId("wrappedTable")); |
|
| 33 | + return new IpBlocklistPanelPO(this.driver, wrappedTable); |
|
| 34 | + } |
|
| 35 | + |
|
| 36 | + public IpBlocklistPanelPO getUserCreationAbusePO() { |
|
| 37 | + final WebElement wrappedTable = userCreationAbusePanel.findElement(new BySeleniumId("wrappedTable")); |
|
| 38 | + return new IpBlocklistPanelPO(this.driver, wrappedTable); |
|
| 39 | + } |
|
| 24 | 40 | |
| 25 | 41 | public void setSelfServiceServer(boolean selfService) { |
| 26 | 42 | if (selfService != isSelfServiceServerCheckbox.isSelected()) { |
| ... | ... | @@ -28,14 +44,14 @@ public class LocalServerPO extends PageArea { |
| 28 | 44 | awaitServerConfigurationUpdated(); |
| 29 | 45 | } |
| 30 | 46 | } |
| 31 | - |
|
| 47 | + |
|
| 32 | 48 | public void setPublicServer(boolean publicServer) { |
| 33 | 49 | if (publicServer != isPublicServerCheckbox.isSelected()) { |
| 34 | 50 | isPublicServerCheckbox.click(); |
| 35 | 51 | awaitServerConfigurationUpdated(); |
| 36 | 52 | } |
| 37 | 53 | } |
| 38 | - |
|
| 54 | + |
|
| 39 | 55 | public void setStandaloneServer(boolean standalone) { |
| 40 | 56 | if (standalone != isStandaloneServerCheckbox.isSelected()) { |
| 41 | 57 | isStandaloneServerCheckbox.click(); |
| ... | ... | @@ -52,5 +68,4 @@ public class LocalServerPO extends PageArea { |
| 52 | 68 | final String updating = isSelfServiceServerCheckbox.getAttribute("updating"); |
| 53 | 69 | return "true".equals(updating); |
| 54 | 70 | } |
| 55 | - |
|
| 56 | 71 | } |
java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/usermanagement/UserManagementPanelPO.java
| ... | ... | @@ -30,7 +30,10 @@ public class UserManagementPanelPO extends PageArea { |
| 30 | 30 | private WebElement createUserButton; |
| 31 | 31 | |
| 32 | 32 | @FindBy(how = BySeleniumId.class, using = "DeleteUserButton") |
| 33 | - private WebElement deleteUserButton; |
|
| 33 | + private WebElement deleteUserButton; |
|
| 34 | + |
|
| 35 | + @FindBy(how = BySeleniumId.class, using = "UnlockUserButton") |
|
| 36 | + private WebElement unlockUserButton; |
|
| 34 | 37 | |
| 35 | 38 | @FindBy(how = BySeleniumId.class, using = "UserNameTextbox") |
| 36 | 39 | private WebElement userNameTextbox; |
| ... | ... | @@ -152,6 +155,43 @@ public class UserManagementPanelPO extends PageArea { |
| 152 | 155 | deleteUserButton.click(); |
| 153 | 156 | } |
| 154 | 157 | |
| 158 | + public void unlockSelectedUsers() { |
|
| 159 | + final List<DataEntryPO> selection = getUserTable().getSelectedEntries(); |
|
| 160 | + final List<String> selectedUsersNames = new ArrayList<String>(); |
|
| 161 | + for (DataEntryPO dataEntryPO : selection) { |
|
| 162 | + final String username = dataEntryPO.getColumnContent("User name"); |
|
| 163 | + selectedUsersNames.add(username); |
|
| 164 | + } |
|
| 165 | + unlockUserButton.click(); |
|
| 166 | + // confirmation dialog |
|
| 167 | + waitForAlertAndAccept(); |
|
| 168 | + // response dialog |
|
| 169 | + waitForAlertAndAccept(); |
|
| 170 | + // wait until all locked untils of previously selected then unlocked users are blanked out |
|
| 171 | + waitUntil(() -> { |
|
| 172 | + for (String selectedUsersName : selectedUsersNames) { |
|
| 173 | + final String lockedUntilVal = findUser(selectedUsersName).getColumnContent("Locked until"); |
|
| 174 | + if (!lockedUntilVal.equals("")) { |
|
| 175 | + return false; |
|
| 176 | + } |
|
| 177 | + } |
|
| 178 | + return true; |
|
| 179 | + }); |
|
| 180 | + |
|
| 181 | + } |
|
| 182 | + |
|
| 183 | + public void unlockUser(String name) { |
|
| 184 | + selectUser(name); |
|
| 185 | + final DataEntryPO entry = findUser(name); |
|
| 186 | + if (entry != null) { |
|
| 187 | + final WebElement action = ActionsHelper.findUnlockAction(entry.getWebElement()); |
|
| 188 | + action.click(); |
|
| 189 | + } |
|
| 190 | + waitUntilAlertIsPresent(); |
|
| 191 | + driver.switchTo().alert().accept(); |
|
| 192 | + waitForAjaxRequests(); |
|
| 193 | + } |
|
| 194 | + |
|
| 155 | 195 | public void waitUntilUserFound(String userName) { |
| 156 | 196 | waitUntil(() -> findUser(userName) != null); |
| 157 | 197 | } |
java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/authentication/AuthenticationMenuPO.java
| ... | ... | @@ -7,29 +7,32 @@ import com.sap.sailing.selenium.pages.PageArea; |
| 7 | 7 | import com.sap.sailing.selenium.pages.common.AttributeHelper; |
| 8 | 8 | |
| 9 | 9 | public class AuthenticationMenuPO extends PageArea { |
| 10 | - |
|
| 10 | + |
|
| 11 | 11 | public AuthenticationMenuPO(WebDriver driver, WebElement element) { |
| 12 | 12 | super(driver, element); |
| 13 | 13 | } |
| 14 | - |
|
| 14 | + |
|
| 15 | 15 | public boolean isOpen() { |
| 16 | 16 | return AttributeHelper.isEnabled(getWebElement(), "data-open"); |
| 17 | 17 | } |
| 18 | - |
|
| 18 | + |
|
| 19 | 19 | public boolean isLoggedIn() { |
| 20 | 20 | return AttributeHelper.isEnabled(getWebElement(), "data-auth"); |
| 21 | 21 | } |
| 22 | - |
|
| 23 | - public void doLogin(String username, String password) { |
|
| 22 | + |
|
| 23 | + public boolean attemptLogin(String username, String password) { |
|
| 24 | 24 | AuthenticationViewPO authenticationView = showAuthenticationView(); |
| 25 | 25 | authenticationView.getSignInView().doLogin(username, password); |
| 26 | - waitUntil(this::isLoggedIn); |
|
| 26 | + waitForAjaxRequests(); |
|
| 27 | + return isLoggedIn(); |
|
| 27 | 28 | } |
| 28 | - |
|
| 29 | + |
|
| 29 | 30 | private AuthenticationViewPO showAuthenticationView() { |
| 30 | - getWebElement().click(); |
|
| 31 | + if (!this.isOpen()) { |
|
| 32 | + getWebElement().click(); |
|
| 33 | + } |
|
| 31 | 34 | waitUntil(this::isOpen); |
| 32 | 35 | return getPO(AuthenticationViewPO::new, "authenticationView"); |
| 33 | 36 | } |
| 34 | - |
|
| 37 | + |
|
| 35 | 38 | } |
java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/test/adminconsole/TestIpLocking.java
| ... | ... | @@ -0,0 +1,113 @@ |
| 1 | +package com.sap.sailing.selenium.test.adminconsole; |
|
| 2 | + |
|
| 3 | +import static org.junit.jupiter.api.Assertions.assertFalse; |
|
| 4 | +import static org.junit.jupiter.api.Assertions.assertTrue; |
|
| 5 | + |
|
| 6 | +import java.util.HashMap; |
|
| 7 | +import java.util.Map; |
|
| 8 | + |
|
| 9 | +import org.junit.jupiter.api.BeforeEach; |
|
| 10 | + |
|
| 11 | +import com.sap.sailing.selenium.api.core.ApiContext; |
|
| 12 | +import com.sap.sailing.selenium.api.core.HttpException.Unauthorized; |
|
| 13 | +import com.sap.sailing.selenium.api.event.PreferencesApi; |
|
| 14 | +import com.sap.sailing.selenium.api.event.SecurityApi; |
|
| 15 | +import com.sap.sailing.selenium.core.SeleniumTestCase; |
|
| 16 | +import com.sap.sailing.selenium.pages.adminconsole.AdminConsolePage; |
|
| 17 | +import com.sap.sailing.selenium.pages.adminconsole.advanced.IpBlocklistPanelPO; |
|
| 18 | +import com.sap.sailing.selenium.pages.adminconsole.advanced.LocalServerPO; |
|
| 19 | +import com.sap.sailing.selenium.test.AbstractSeleniumTest; |
|
| 20 | + |
|
| 21 | +public class TestIpLocking extends AbstractSeleniumTest { |
|
| 22 | + @Override |
|
| 23 | + @BeforeEach |
|
| 24 | + public void setUp() { |
|
| 25 | + clearState(getContextRoot()); |
|
| 26 | + super.setUp(); |
|
| 27 | + } |
|
| 28 | + |
|
| 29 | + @SeleniumTestCase |
|
| 30 | + public void testUnlockingForBearerTokenAbuser() throws InterruptedException { |
|
| 31 | + final AdminConsolePage adminConsole = AdminConsolePage.goToPage(getWebDriver(), getContextRoot()); |
|
| 32 | + final LocalServerPO localServerPanel = adminConsole.goToLocalServerPanel(); |
|
| 33 | + IpBlocklistPanelPO tablePO = localServerPanel.getBearerTokenAbusePO(); |
|
| 34 | + attemptBearerTokenAbuse(4); |
|
| 35 | + tablePO.refresh(); |
|
| 36 | + final String ip = "127.0.0.1"; |
|
| 37 | + assertTrue(tablePO.isIpInTable(ip)); |
|
| 38 | + tablePO.unblockIP(ip); |
|
| 39 | + // reference was getting stale otherwise |
|
| 40 | + tablePO = localServerPanel.getBearerTokenAbusePO(); |
|
| 41 | + assertFalse(tablePO.isIpInTable(ip)); |
|
| 42 | + attemptValidBearerTokenUse(); |
|
| 43 | + } |
|
| 44 | + |
|
| 45 | + private void attemptValidBearerTokenUse() { |
|
| 46 | + // prepare api |
|
| 47 | + final ApiContext ctx = ApiContext.createAdminApiContext(getContextRoot(), ApiContext.SECURITY_CONTEXT); |
|
| 48 | + final Map<String, String> prefObjectAttr = new HashMap<String, String>(); |
|
| 49 | + prefObjectAttr.put("key1", "value1"); |
|
| 50 | + PreferencesApi preferencesApi = new PreferencesApi(); |
|
| 51 | + preferencesApi.createPreference(ctx, "pref1", prefObjectAttr); |
|
| 52 | + } |
|
| 53 | + |
|
| 54 | + private void attemptBearerTokenAbuse(final int attempts) throws InterruptedException { |
|
| 55 | + // prepare api |
|
| 56 | + final ApiContext wrongCtx = ApiContext.createApiContextWithInvalidToken(getContextRoot(), |
|
| 57 | + ApiContext.SECURITY_CONTEXT); |
|
| 58 | + final Map<String, String> prefObjectAttr = new HashMap<String, String>(); |
|
| 59 | + prefObjectAttr.put("key1", "value1"); |
|
| 60 | + final PreferencesApi preferencesApi = new PreferencesApi(); |
|
| 61 | + for (int i = 0; i < attempts; i++) { |
|
| 62 | + // call api |
|
| 63 | + try { |
|
| 64 | + preferencesApi.createPreference(wrongCtx, "pref1", prefObjectAttr); |
|
| 65 | + } catch (Unauthorized e) { |
|
| 66 | + // do nothing as this is expected |
|
| 67 | + } |
|
| 68 | + // wait for lock to expire |
|
| 69 | + long lockDuration = (long) Math.pow(2, i) * 1000; |
|
| 70 | + boolean isFinalAttempt = i == (attempts - 1); |
|
| 71 | + if (!isFinalAttempt) { |
|
| 72 | + Thread.sleep(lockDuration); |
|
| 73 | + } |
|
| 74 | + } |
|
| 75 | + } |
|
| 76 | + |
|
| 77 | + @SeleniumTestCase |
|
| 78 | + public void testUnlockingForUserCreationAbuser() throws InterruptedException { |
|
| 79 | + final AdminConsolePage adminConsole = AdminConsolePage.goToPage(getWebDriver(), getContextRoot()); |
|
| 80 | + final LocalServerPO localServerPanel = adminConsole.goToLocalServerPanel(); |
|
| 81 | + IpBlocklistPanelPO tablePO = localServerPanel.getUserCreationAbusePO(); |
|
| 82 | + spamUserCreation(4); |
|
| 83 | + tablePO.refresh(); |
|
| 84 | + final String ip = "127.0.0.1"; |
|
| 85 | + assertTrue(tablePO.isIpInTable(ip)); |
|
| 86 | + tablePO.unblockIP(ip); |
|
| 87 | + // reference was getting stale otherwise |
|
| 88 | + tablePO = localServerPanel.getUserCreationAbusePO(); |
|
| 89 | + assertFalse(tablePO.isIpInTable(ip)); |
|
| 90 | + attemptValidBearerTokenUse(); |
|
| 91 | + } |
|
| 92 | + |
|
| 93 | + private void spamUserCreation(final int attempts) throws InterruptedException { |
|
| 94 | + for (int i = 0; i < attempts; i++) { |
|
| 95 | + attemptUserCreation(String.valueOf(i)); |
|
| 96 | + // wait for lock to expire |
|
| 97 | + final long lockDuration = (long) Math.pow(2, i) * 1000; |
|
| 98 | + final boolean isFinalAttempt = i == (attempts - 1); |
|
| 99 | + if (!isFinalAttempt) { |
|
| 100 | + Thread.sleep(lockDuration); |
|
| 101 | + } |
|
| 102 | + } |
|
| 103 | + } |
|
| 104 | + |
|
| 105 | + private boolean attemptUserCreation(String seed) { |
|
| 106 | + try { |
|
| 107 | + SecurityApi.createUser("USERNAME" + seed, "PASSWORD").run(); |
|
| 108 | + return true; |
|
| 109 | + } catch (Exception e) { |
|
| 110 | + return false; |
|
| 111 | + } |
|
| 112 | + } |
|
| 113 | +} |
java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/test/adminconsole/usermanagement/TestUserManagement.java
| ... | ... | @@ -1,7 +1,9 @@ |
| 1 | 1 | package com.sap.sailing.selenium.test.adminconsole.usermanagement; |
| 2 | 2 | |
| 3 | +import static org.junit.jupiter.api.Assertions.assertFalse; |
|
| 3 | 4 | import static org.junit.jupiter.api.Assertions.assertNotNull; |
| 4 | 5 | import static org.junit.jupiter.api.Assertions.assertNull; |
| 6 | +import static org.junit.jupiter.api.Assertions.assertTrue; |
|
| 5 | 7 | |
| 6 | 8 | import org.junit.jupiter.api.BeforeEach; |
| 7 | 9 | |
| ... | ... | @@ -13,6 +15,7 @@ import com.sap.sailing.selenium.pages.adminconsole.usermanagement.EditUserDialog |
| 13 | 15 | import com.sap.sailing.selenium.pages.adminconsole.usermanagement.UserManagementPanelPO; |
| 14 | 16 | import com.sap.sailing.selenium.pages.adminconsole.usermanagement.UserRoleDefinitionPanelPO; |
| 15 | 17 | import com.sap.sailing.selenium.pages.adminconsole.usermanagement.WildcardPermissionPanelPO; |
| 18 | +import com.sap.sailing.selenium.pages.authentication.AuthenticationMenuPO; |
|
| 16 | 19 | import com.sap.sailing.selenium.test.AbstractSeleniumTest; |
| 17 | 20 | |
| 18 | 21 | public class TestUserManagement extends AbstractSeleniumTest { |
| ... | ... | @@ -55,7 +58,7 @@ public class TestUserManagement extends AbstractSeleniumTest { |
| 55 | 58 | assertNull(userRolesPO.findRole(TEST_ROLE)); |
| 56 | 59 | createRole(userRolesPO); |
| 57 | 60 | userManagementPanel.selectUser(TEST_USER_NAME); |
| 58 | - assertNotNull(userRolesPO.findRole(TEST_ROLE + ":"+ TEST_GROUP + ":" + TEST_USER_NAME)); |
|
| 61 | + assertNotNull(userRolesPO.findRole(TEST_ROLE + ":" + TEST_GROUP + ":" + TEST_USER_NAME)); |
|
| 59 | 62 | } |
| 60 | 63 | |
| 61 | 64 | private void createRole(final UserRoleDefinitionPanelPO userRolesPO) { |
| ... | ... | @@ -101,6 +104,63 @@ public class TestUserManagement extends AbstractSeleniumTest { |
| 101 | 104 | } |
| 102 | 105 | |
| 103 | 106 | @SeleniumTestCase |
| 107 | + public void testUnlockUser() throws InterruptedException { |
|
| 108 | + // at admin |
|
| 109 | + UserManagementPanelPO userManagementPanel = goToUserManagementPanel(); |
|
| 110 | + createUser(userManagementPanel); |
|
| 111 | + // logout so test user can login |
|
| 112 | + AuthenticationMenuPO authenticationMenu = logoutAndGoToAdminConsolePage().getAuthenticationMenu(); |
|
| 113 | + attemptAbusiveLogins(TEST_USER_NAME, "wrongPassword", 6, authenticationMenu); |
|
| 114 | + // 16s lock window in place now, sufficient to log into admin, unlock user |
|
| 115 | + // and return till test user can login, within the erstwhile lock window |
|
| 116 | + assertTrue(authenticationMenu.attemptLogin("admin", "admin")); |
|
| 117 | + userManagementPanel = goToUserManagementPanel(); |
|
| 118 | + userManagementPanel.unlockUser(TEST_USER_NAME); |
|
| 119 | + // logout and correct login within now-unlocked lock window |
|
| 120 | + authenticationMenu = logoutAndGoToAdminConsolePage().getAuthenticationMenu(); |
|
| 121 | + assertTrue( |
|
| 122 | + authenticationMenu.attemptLogin( |
|
| 123 | + TEST_USER_NAME, TEST_USER_PASSWORD + UserManagementPanelPO.PASSWORD_COMPLEXITY_SALT)); |
|
| 124 | + } |
|
| 125 | + |
|
| 126 | + @SeleniumTestCase |
|
| 127 | + public void testUnlockSelectionOfUsers() throws InterruptedException { |
|
| 128 | + // at admin |
|
| 129 | + UserManagementPanelPO userManagementPanel = goToUserManagementPanel(); |
|
| 130 | + createUser(userManagementPanel); |
|
| 131 | + // logout so test user can login |
|
| 132 | + AuthenticationMenuPO authenticationMenu = logoutAndGoToAdminConsolePage().getAuthenticationMenu(); |
|
| 133 | + attemptAbusiveLogins(TEST_USER_NAME, "wrongPassword", 5, authenticationMenu); |
|
| 134 | + // 16s lock window in place now, sufficient to log into admin, unlock user |
|
| 135 | + // and return till test user can login, within the erstwhile lock window |
|
| 136 | + assertTrue(authenticationMenu.attemptLogin("admin", "admin")); |
|
| 137 | + userManagementPanel = goToUserManagementPanel(); |
|
| 138 | + userManagementPanel.selectUser(TEST_USER_NAME); |
|
| 139 | + userManagementPanel.unlockSelectedUsers(); |
|
| 140 | + // logout and correct login within now-unlocked lock window |
|
| 141 | + authenticationMenu = logoutAndGoToAdminConsolePage().getAuthenticationMenu(); |
|
| 142 | + assertTrue( |
|
| 143 | + authenticationMenu.attemptLogin( |
|
| 144 | + TEST_USER_NAME, TEST_USER_PASSWORD + UserManagementPanelPO.PASSWORD_COMPLEXITY_SALT)); |
|
| 145 | + } |
|
| 146 | + |
|
| 147 | + private void attemptAbusiveLogins(final String username, final String wrongPassword, final int attempts, AuthenticationMenuPO authenticationMenu) |
|
| 148 | + throws InterruptedException { |
|
| 149 | + // logout so test user can login |
|
| 150 | + for (int i = 0; i < attempts; i++) { |
|
| 151 | + // attempt login with wrong password |
|
| 152 | + boolean didSucceed = authenticationMenu.attemptLogin(username, wrongPassword); |
|
| 153 | + assertFalse(didSucceed); |
|
| 154 | + // wait for lock to pass |
|
| 155 | + long lockDuration = (long) Math.pow(2, i) * 1000; |
|
| 156 | + boolean isFinalAttempt = i == (attempts - 1); |
|
| 157 | + if (!isFinalAttempt) { |
|
| 158 | + Thread.sleep(lockDuration); |
|
| 159 | + } |
|
| 160 | + } |
|
| 161 | + } |
|
| 162 | + |
|
| 163 | + @SeleniumTestCase |
|
| 104 | 164 | public void testRemoveUserPermission() { |
| 105 | 165 | final UserManagementPanelPO userManagementPanel = goToUserManagementPanel(); |
| 106 | 166 | createUser(userManagementPanel); |
| ... | ... | @@ -128,4 +188,9 @@ public class TestUserManagement extends AbstractSeleniumTest { |
| 128 | 188 | final AdminConsolePage adminConsole = AdminConsolePage.goToPage(getWebDriver(), getContextRoot()); |
| 129 | 189 | return adminConsole.goToUserManagement(); |
| 130 | 190 | } |
| 191 | + |
|
| 192 | + private AdminConsolePage logoutAndGoToAdminConsolePage() { |
|
| 193 | + getWebDriver().manage().deleteCookieNamed("JSESSIONID"); |
|
| 194 | + return AdminConsolePage.goToPage(getWebDriver(), getContextRoot()); |
|
| 195 | + } |
|
| 131 | 196 | } |
java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/test/authentication/TestAuthenticationSignIn.java
| ... | ... | @@ -18,16 +18,44 @@ public class TestAuthenticationSignIn extends AbstractSeleniumTest { |
| 18 | 18 | clearState(getContextRoot()); |
| 19 | 19 | getWebDriver().manage().deleteCookieNamed("JSESSIONID"); |
| 20 | 20 | } |
| 21 | - |
|
| 21 | + |
|
| 22 | 22 | @SeleniumTestCase |
| 23 | 23 | public void testSignInWithExistingUserAdmin() { |
| 24 | 24 | AdminConsolePage adminConsolePage = AdminConsolePage.goToPage(getWebDriver(), getContextRoot()); |
| 25 | 25 | AuthenticationMenuPO authenticationMenu = adminConsolePage.getAuthenticationMenu(); |
| 26 | 26 | assertFalse(authenticationMenu.isOpen()); |
| 27 | 27 | assertFalse(authenticationMenu.isLoggedIn()); |
| 28 | - authenticationMenu.doLogin("admin", "admin"); |
|
| 29 | - assertTrue(authenticationMenu.isOpen()); |
|
| 30 | - assertTrue(authenticationMenu.isLoggedIn()); |
|
| 28 | + final boolean didSucceed = authenticationMenu.attemptLogin("admin", "admin"); |
|
| 29 | + assertTrue(didSucceed); |
|
| 30 | + } |
|
| 31 | + |
|
| 32 | + // test logic is brittle, it is expected that |
|
| 33 | + // login in test environment takes reliably under |
|
| 34 | + // 4 seconds |
|
| 35 | + @SeleniumTestCase |
|
| 36 | + public void testOffendingSignInWithTimedLock() throws InterruptedException { |
|
| 37 | + AdminConsolePage adminConsolePage = AdminConsolePage.goToPage(getWebDriver(), getContextRoot()); |
|
| 38 | + AuthenticationMenuPO authenticationMenu = adminConsolePage.getAuthenticationMenu(); |
|
| 39 | + assertFalse(authenticationMenu.isOpen()); |
|
| 40 | + assertFalse(authenticationMenu.isLoggedIn()); |
|
| 41 | + // wrong login 1 |
|
| 42 | + boolean didSucceed = authenticationMenu.attemptLogin("admin", "wrongPassword"); |
|
| 43 | + assertFalse(didSucceed); |
|
| 44 | + Thread.sleep(2000); // wait for 2s lock to pass |
|
| 45 | + didSucceed = authenticationMenu.attemptLogin("admin", "wrongPassword"); |
|
| 46 | + assertFalse(didSucceed); |
|
| 47 | + Thread.sleep(4000); // wait for 4s lock to pass |
|
| 48 | + didSucceed = authenticationMenu.attemptLogin("admin", "wrongPassword"); |
|
| 49 | + assertFalse(didSucceed); |
|
| 50 | + // 4s lock window in place now |
|
| 51 | + // we assert that even correct credentials |
|
| 52 | + // attempted within this lock window will fail. |
|
| 53 | + didSucceed = authenticationMenu.attemptLogin("admin", "admin"); |
|
| 54 | + assertFalse(didSucceed); |
|
| 55 | + Thread.sleep(8000); // wait for 8s lock to pass (inefficient, but correct) |
|
| 56 | + // correct credentials should work after lock window lapses |
|
| 57 | + didSucceed = authenticationMenu.attemptLogin("admin", "admin"); |
|
| 58 | + assertTrue(didSucceed); |
|
| 31 | 59 | } |
| 32 | 60 | |
| 33 | 61 | } |
java/com.sap.sailing.server.gateway.test.support/resources/shiro.ini
| ... | ... | @@ -32,7 +32,7 @@ securityManager.sessionManager.sessionDAO = $sessionDAO |
| 32 | 32 | securityManager.sessionManager.globalSessionTimeout = 31536000000
|
| 33 | 33 | cacheManager = com.sap.sse.security.SessionCacheManager
|
| 34 | 34 | securityManager.cacheManager = $cacheManager
|
| 35 | -authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithLockingAndBanning
|
|
| 35 | +authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithTimedLocks
|
|
| 36 | 36 | securityManager.authenticator.authenticationStrategy = $authenticationStrategy
|
| 37 | 37 | |
| 38 | 38 | # Authentication Filter Configurations
|
java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/RegattasResourceTest.java
| ... | ... | @@ -49,11 +49,11 @@ import com.sap.sailing.domain.ranking.OneDesignRankingMetric; |
| 49 | 49 | import com.sap.sse.common.Color; |
| 50 | 50 | import com.sap.sse.common.TimePoint; |
| 51 | 51 | import com.sap.sse.common.impl.MillisecondsTimePoint; |
| 52 | +import com.sap.sse.common.impl.TimedLockImpl; |
|
| 52 | 53 | import com.sap.sse.rest.StreamingOutputUtil; |
| 53 | 54 | import com.sap.sse.security.SecurityService; |
| 54 | 55 | import com.sap.sse.security.interfaces.UserImpl; |
| 55 | 56 | import com.sap.sse.security.shared.Account; |
| 56 | -import com.sap.sse.security.shared.impl.LockingAndBanningImpl; |
|
| 57 | 57 | import com.sap.sse.security.shared.impl.User; |
| 58 | 58 | |
| 59 | 59 | public class RegattasResourceTest extends AbstractJaxRsApiTest { |
| ... | ... | @@ -160,7 +160,7 @@ public class RegattasResourceTest extends AbstractJaxRsApiTest { |
| 160 | 160 | public void testCompetitorRegistrationByAdmin() throws Exception { |
| 161 | 161 | doReturn(securityService).when(regattasResource).getService(SecurityService.class); |
| 162 | 162 | doReturn(true).when(securityService).hasCurrentUserUpdatePermission(Mockito.any()); |
| 163 | - User user = new UserImpl("admin", "noreply@sapsailing.com", null, new ArrayList<Account>(0), null, new LockingAndBanningImpl()); |
|
| 163 | + User user = new UserImpl("admin", "noreply@sapsailing.com", null, new ArrayList<Account>(0), null, new TimedLockImpl()); |
|
| 164 | 164 | setUser(user); |
| 165 | 165 | when(securityService.getCurrentUser()).thenReturn(user); |
| 166 | 166 | Response response = regattasResource.createAndAddCompetitor(closedRegattaName, boatClassName, null, "GER", |
| ... | ... | @@ -218,7 +218,7 @@ public class RegattasResourceTest extends AbstractJaxRsApiTest { |
| 218 | 218 | @Test |
| 219 | 219 | public void testCompetitorRegistrationAuthenticatedOnOpenRegatta() throws Exception { |
| 220 | 220 | doReturn(securityService).when(regattasResource).getService(SecurityService.class); |
| 221 | - User user = new UserImpl("max", "noreply@sapsailing.com", null, new ArrayList<Account>(0), null, new LockingAndBanningImpl()); |
|
| 221 | + User user = new UserImpl("max", "noreply@sapsailing.com", null, new ArrayList<Account>(0), null, new TimedLockImpl()); |
|
| 222 | 222 | setUser(user); |
| 223 | 223 | Regatta regatta = racingEventService.getRegattaByName(openRegattaName); |
| 224 | 224 | Response response = regattasResource.createAndAddCompetitor(openRegattaName, boatClassName, null, "GER", "#F00", |
java/com.sap.sailing.server.gateway/resources/shiro.ini
| ... | ... | @@ -26,7 +26,7 @@ securityManager.subjectDAO = $subjectDAO |
| 26 | 26 | securityManager.sessionManager.globalSessionTimeout = 31536000000 |
| 27 | 27 | cacheManager = com.sap.sse.security.SessionCacheManager |
| 28 | 28 | securityManager.cacheManager = $cacheManager |
| 29 | -authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithLockingAndBanning |
|
| 29 | +authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithTimedLocks |
|
| 30 | 30 | securityManager.authenticator.authenticationStrategy = $authenticationStrategy |
| 31 | 31 | |
| 32 | 32 | # Support for anonymous user permissions |
java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/MediaReplicationTest.java
| ... | ... | @@ -57,6 +57,7 @@ import com.sap.sse.common.TimePoint; |
| 57 | 57 | import com.sap.sse.common.Util; |
| 58 | 58 | import com.sap.sse.common.impl.MillisecondsDurationImpl; |
| 59 | 59 | import com.sap.sse.common.impl.MillisecondsTimePoint; |
| 60 | +import com.sap.sse.common.impl.TimedLockImpl; |
|
| 60 | 61 | import com.sap.sse.common.media.MimeType; |
| 61 | 62 | import com.sap.sse.mongodb.MongoDBConfiguration; |
| 62 | 63 | import com.sap.sse.mongodb.MongoDBService; |
| ... | ... | @@ -64,7 +65,6 @@ import com.sap.sse.replication.FullyInitializedReplicableTracker; |
| 64 | 65 | import com.sap.sse.security.SecurityService; |
| 65 | 66 | import com.sap.sse.security.interfaces.UserImpl; |
| 66 | 67 | import com.sap.sse.security.shared.WithQualifiedObjectIdentifier; |
| 67 | -import com.sap.sse.security.shared.impl.LockingAndBanningImpl; |
|
| 68 | 68 | import com.sap.sse.security.shared.impl.SecuredSecurityTypes.PublicReadableActions; |
| 69 | 69 | import com.sap.sse.security.shared.impl.SecuredSecurityTypes.ServerActions; |
| 70 | 70 | import com.sap.sse.security.shared.impl.User; |
| ... | ... | @@ -248,7 +248,7 @@ public class MediaReplicationTest extends AbstractServerReplicationTest { |
| 248 | 248 | @Test |
| 249 | 249 | public void testMasterDataImportForMediaTracks() throws Exception { |
| 250 | 250 | UserGroupImpl defaultTenant = new UserGroupImpl(new UUID(0, 1), "defaultTenant"); |
| 251 | - User currentUser = new UserImpl("test", "email@test", Collections.emptyMap(), null, new LockingAndBanningImpl()); |
|
| 251 | + User currentUser = new UserImpl("test", "email@test", Collections.emptyMap(), null, new TimedLockImpl()); |
|
| 252 | 252 | SecurityService securityService = Mockito.mock(SecurityService.class); |
| 253 | 253 | Mockito.doReturn(defaultTenant).when(securityService).getServerGroup(); |
| 254 | 254 | Mockito.doReturn(currentUser).when(securityService).getCurrentUser(); |
java/com.sap.sailing.shared.server.gateway/resources/shiro.ini
| ... | ... | @@ -26,7 +26,7 @@ securityManager.subjectDAO = $subjectDAO |
| 26 | 26 | securityManager.sessionManager.globalSessionTimeout = 31536000000 |
| 27 | 27 | cacheManager = com.sap.sse.security.SessionCacheManager |
| 28 | 28 | securityManager.cacheManager = $cacheManager |
| 29 | -authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithLockingAndBanning |
|
| 29 | +authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithTimedLocks |
|
| 30 | 30 | securityManager.authenticator.authenticationStrategy = $authenticationStrategy |
| 31 | 31 | |
| 32 | 32 | # Support for anonymous user permissions |
java/com.sap.sailing.windestimation/resources/shiro.ini
| ... | ... | @@ -26,7 +26,7 @@ securityManager.subjectDAO = $subjectDAO |
| 26 | 26 | securityManager.sessionManager.globalSessionTimeout = 31536000000 |
| 27 | 27 | cacheManager = com.sap.sse.security.SessionCacheManager |
| 28 | 28 | securityManager.cacheManager = $cacheManager |
| 29 | -authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithLockingAndBanning |
|
| 29 | +authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithTimedLocks |
|
| 30 | 30 | securityManager.authenticator.authenticationStrategy = $authenticationStrategy |
| 31 | 31 | |
| 32 | 32 | # Support for anonymous user permissions |
java/com.sap.sailing.www/release_notes_admin.html
| ... | ... | @@ -23,6 +23,19 @@ |
| 23 | 23 | <div class="mainContent"> |
| 24 | 24 | <h2 class="releaseHeadline">Release Notes - Administration Console</h2> |
| 25 | 25 | <div class="innerContent"> |
| 26 | + <h2 class="articleSubheadline">December 2025</h2> |
|
| 27 | + <ul class="bulletList"> |
|
| 28 | + <li>Added boat class Fareast 28R.</li> |
|
| 29 | + <li>Added support for unlocking users under Admin Console > |
|
| 30 | + Advanced > User Management. The lock referred to here is that |
|
| 31 | + placed for multiple unsuccessful login attempts, resembling a |
|
| 32 | + brute force attack, onto an account identified by the used email |
|
| 33 | + address or username, for a duration of time.</li> |
|
| 34 | + <li>Added support for unlocking IP addresses under Admin |
|
| 35 | + Console > Advanced > Local Server. The lock referred to here is |
|
| 36 | + that placed for multiple create account API hits or multiple API |
|
| 37 | + hits using an invalid bearer token for a duration of time.</li> |
|
| 38 | + </ul> |
|
| 26 | 39 | <h2 class="articleSubheadline">November 2025</h2> |
| 27 | 40 | <ul class="bulletList"> |
| 28 | 41 | <li>The Landscape Management panel as well as the <tt>refreshInstance.sh</tt> shell script |
java/com.sap.sailing.www/resources/shiro.ini
| ... | ... | @@ -26,7 +26,7 @@ securityManager.subjectDAO = $subjectDAO |
| 26 | 26 | securityManager.sessionManager.globalSessionTimeout = 31536000000 |
| 27 | 27 | cacheManager = com.sap.sse.security.SessionCacheManager |
| 28 | 28 | securityManager.cacheManager = $cacheManager |
| 29 | -authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithLockingAndBanning |
|
| 29 | +authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithTimedLocks |
|
| 30 | 30 | securityManager.authenticator.authenticationStrategy = $authenticationStrategy |
| 31 | 31 | |
| 32 | 32 | # Support for anonymous user permissions |
java/com.sap.sse.common.test/src/com/sap/sse/common/impl/test/TimedLockImplTest.java
| ... | ... | @@ -0,0 +1,63 @@ |
| 1 | +/** |
|
| 2 | + * |
|
| 3 | + */ |
|
| 4 | +package com.sap.sse.common.impl.test; |
|
| 5 | + |
|
| 6 | +import static org.junit.jupiter.api.Assertions.*; |
|
| 7 | + |
|
| 8 | +import org.junit.jupiter.api.BeforeEach; |
|
| 9 | +import org.junit.jupiter.api.Test; |
|
| 10 | + |
|
| 11 | +import com.sap.sse.common.Duration; |
|
| 12 | +import com.sap.sse.common.TimePoint; |
|
| 13 | +import com.sap.sse.common.impl.TimedLockImpl; |
|
| 14 | + |
|
| 15 | +/** |
|
| 16 | + * |
|
| 17 | + */ |
|
| 18 | +class TimedLockImplTest { |
|
| 19 | + |
|
| 20 | + /** |
|
| 21 | + * @throws java.lang.Exception |
|
| 22 | + */ |
|
| 23 | + @BeforeEach |
|
| 24 | + void setUp() throws Exception { |
|
| 25 | + } |
|
| 26 | + |
|
| 27 | + /** |
|
| 28 | + * Test method for {@link com.sap.sse.common.impl.TimedLockImpl#extendLockDuration()}. |
|
| 29 | + */ |
|
| 30 | + @Test |
|
| 31 | + void testExtendLockDuration() { |
|
| 32 | + final TimedLockImpl lock = new TimedLockImpl(); |
|
| 33 | + final Duration firstLockingDelay = lock.getNextLockingDelay(); |
|
| 34 | + // initial locking delay should be greater than 0 |
|
| 35 | + assertTrue(firstLockingDelay.compareTo(Duration.NULL) > 0); |
|
| 36 | + assertTrue(firstLockingDelay.compareTo(Duration.ofMillis(0)) > 0); |
|
| 37 | + final TimePoint expectedLockedUntil = TimePoint.now().plus(firstLockingDelay); |
|
| 38 | + // locking delay should be applied to lockedUntil |
|
| 39 | + lock.extendLockDuration(); |
|
| 40 | + assertEquals(lock.getLockedUntil(), expectedLockedUntil); |
|
| 41 | + // locking delay should double |
|
| 42 | + final Duration secondLockingDelay = firstLockingDelay.plus(firstLockingDelay); |
|
| 43 | + assertEquals(lock.getNextLockingDelay(), secondLockingDelay); |
|
| 44 | + // double locking delay should be applied to lockedUntil |
|
| 45 | + lock.extendLockDuration(); |
|
| 46 | + assertEquals(lock.getLockedUntil(), TimePoint.now().plus(secondLockingDelay)); |
|
| 47 | + } |
|
| 48 | + |
|
| 49 | + /** |
|
| 50 | + * Test method for {@link com.sap.sse.common.impl.TimedLockImpl#resetLock()}. |
|
| 51 | + */ |
|
| 52 | + @Test |
|
| 53 | + void testResetLock() { |
|
| 54 | + final TimedLockImpl lock = new TimedLockImpl(); |
|
| 55 | + lock.extendLockDuration(); |
|
| 56 | + lock.extendLockDuration(); |
|
| 57 | + lock.extendLockDuration(); |
|
| 58 | + assertTrue(lock.isLocked()); |
|
| 59 | + lock.resetLock(); |
|
| 60 | + assertFalse(lock.isLocked()); |
|
| 61 | + } |
|
| 62 | + |
|
| 63 | +} |
java/com.sap.sse.common/src/com/sap/sse/common/TimedLock.java
| ... | ... | @@ -0,0 +1,33 @@ |
| 1 | +package com.sap.sse.common; |
|
| 2 | + |
|
| 3 | +import java.io.Serializable; |
|
| 4 | + |
|
| 5 | +/** |
|
| 6 | + * Holds information about a user's log-on history which is then used to decide whether the user account should be |
|
| 7 | + * locked temporarily or permanently for certain forms of authentication. |
|
| 8 | + * <p> |
|
| 9 | + * |
|
| 10 | + * For example, failed password authentication requests shall be logged by the realm using calls to |
|
| 11 | + * {@link #extendLockDuration()}, successful ones with {@link #resetLock()}. Using the |
|
| 12 | + * {@link #isLocked()} method, a realm can determine if the user account to which this object belongs |
|
| 13 | + * shall currently accept password authentication. |
|
| 14 | + * <p> |
|
| 15 | + * |
|
| 16 | + * A possible strategy for an implementation could be to add an increasing delay for each failed password |
|
| 17 | + * authentication, but reduce or clear the delay after a successful password authentication. |
|
| 18 | + * |
|
| 19 | + * @author Axel Uhl (d043530) |
|
| 20 | + * |
|
| 21 | + */ |
|
| 22 | +public interface TimedLock extends Serializable { |
|
| 23 | + void extendLockDuration(); |
|
| 24 | + |
|
| 25 | + /** |
|
| 26 | + * @return {@code true} if this locking and banning record changed due to this call |
|
| 27 | + */ |
|
| 28 | + boolean resetLock(); |
|
| 29 | + |
|
| 30 | + boolean isLocked(); |
|
| 31 | + |
|
| 32 | + TimePoint getLockedUntil(); |
|
| 33 | +} |
java/com.sap.sse.common/src/com/sap/sse/common/impl/TimedLockImpl.java
| ... | ... | @@ -0,0 +1,83 @@ |
| 1 | +package com.sap.sse.common.impl; |
|
| 2 | + |
|
| 3 | +import com.sap.sse.common.Duration; |
|
| 4 | +import com.sap.sse.common.TimePoint; |
|
| 5 | +import com.sap.sse.common.TimedLock; |
|
| 6 | +import com.sap.sse.common.Util; |
|
| 7 | + |
|
| 8 | +public class TimedLockImpl implements TimedLock { |
|
| 9 | + private static final long serialVersionUID = 3547356744366236677L; |
|
| 10 | + |
|
| 11 | + public static final Duration DEFAULT_INITIAL_LOCKING_DELAY = Duration.ONE_SECOND; |
|
| 12 | + |
|
| 13 | + /** |
|
| 14 | + * An always valid time point which may be in the past. If it is in the future, |
|
| 15 | + * {@link #isLocked()} will return {@code true}. |
|
| 16 | + */ |
|
| 17 | + private TimePoint lockedUntil; |
|
| 18 | + |
|
| 19 | + /** |
|
| 20 | + * An always valid, non-zero duration that indicates for how long into the future the {@link #lockedUntil} time |
|
| 21 | + * point will be set in case a {@link #extendLockDuration() failed password authentication} is notified. |
|
| 22 | + */ |
|
| 23 | + private Duration nextLockingDelay; |
|
| 24 | + |
|
| 25 | + /** |
|
| 26 | + * Creates an instance that is unlocked and has a "last locking delay" of one second |
|
| 27 | + */ |
|
| 28 | + public TimedLockImpl() { |
|
| 29 | + this(TimePoint.BeginningOfTime, DEFAULT_INITIAL_LOCKING_DELAY); |
|
| 30 | + } |
|
| 31 | + |
|
| 32 | + public TimedLockImpl(TimePoint lockedUntil, Duration nextLockingDelay) { |
|
| 33 | + super(); |
|
| 34 | + this.lockedUntil = lockedUntil; |
|
| 35 | + this.nextLockingDelay = nextLockingDelay; |
|
| 36 | + } |
|
| 37 | + |
|
| 38 | + /** |
|
| 39 | + * Locks for the {@link #nextLockingDelay} and doubles the delay for the next failed attempt. |
|
| 40 | + */ |
|
| 41 | + @Override |
|
| 42 | + public void extendLockDuration() { |
|
| 43 | + lockedUntil = TimePoint.now().plus(nextLockingDelay); |
|
| 44 | + nextLockingDelay = nextLockingDelay.times(2); |
|
| 45 | + } |
|
| 46 | + |
|
| 47 | + @Override |
|
| 48 | + public boolean resetLock() { |
|
| 49 | + final Duration oldLockingDelay = nextLockingDelay; |
|
| 50 | + nextLockingDelay = DEFAULT_INITIAL_LOCKING_DELAY; |
|
| 51 | + final TimePoint oldLockedUntil = lockedUntil; |
|
| 52 | + lockedUntil = TimePoint.BeginningOfTime; |
|
| 53 | + return !Util.equalsWithNull(oldLockingDelay, nextLockingDelay) || !Util.equalsWithNull(oldLockedUntil, lockedUntil); |
|
| 54 | + } |
|
| 55 | + |
|
| 56 | + @Override |
|
| 57 | + public boolean isLocked() { |
|
| 58 | + return TimePoint.now().before(lockedUntil); |
|
| 59 | + } |
|
| 60 | + |
|
| 61 | + @Override |
|
| 62 | + public TimePoint getLockedUntil() { |
|
| 63 | + return lockedUntil; |
|
| 64 | + } |
|
| 65 | + |
|
| 66 | + public Duration getNextLockingDelay() { |
|
| 67 | + return nextLockingDelay; |
|
| 68 | + } |
|
| 69 | + |
|
| 70 | + @Override |
|
| 71 | + public String toString() { |
|
| 72 | + final StringBuilder result = new StringBuilder(); |
|
| 73 | + if (isLocked()) { |
|
| 74 | + result.append("locked until "); |
|
| 75 | + result.append(getLockedUntil()); |
|
| 76 | + } else { |
|
| 77 | + result.append("unlocked"); |
|
| 78 | + } |
|
| 79 | + result.append(", next locking duration: "); |
|
| 80 | + result.append(getNextLockingDelay()); |
|
| 81 | + return result.toString(); |
|
| 82 | + } |
|
| 83 | +} |
java/com.sap.sse.gwt.test/.settings/com.gwtplugins.gwt.eclipse.core.prefs
| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | -//gwtVersion_/com.google.gwt.user/lib=2.11.1 |
|
| 1 | +//gwtVersion_/com.google.gwt.user/lib= |
|
| 2 | 2 | //gwtVersion_/opt/gwt-2.11.0=2.11.0 |
| 3 | 3 | //gwtVersion_/opt/gwt-2.11.1=2.11.1 |
| 4 | 4 | //gwtVersion_/opt/gwt-2.12.2=2.12.2 |
java/com.sap.sse.gwt/.settings/com.gwtplugins.gwt.eclipse.core.prefs
| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | -//gwtVersion_/com.google.gwt.user/lib=2.11.1 |
|
| 1 | +//gwtVersion_/com.google.gwt.user/lib= |
|
| 2 | 2 | //gwtVersion_/opt/gwt-2.11.0=2.11.0 |
| 3 | 3 | //gwtVersion_/opt/gwt-2.11.1=2.11.1 |
| 4 | 4 | //gwtVersion_/opt/gwt-2.12.2=2.12.2 |
java/com.sap.sse.gwt/GWT xdStorage Sample SDM.launch
| ... | ... | @@ -3,13 +3,14 @@ |
| 3 | 3 | <booleanAttribute key="com.gwtplugins.gdt.eclipse.core.RUN_SERVER" value="false"/> |
| 4 | 4 | <stringAttribute key="com.gwtplugins.gdt.eclipse.suiteMainTypeProcessor.PREVIOUSLY_SET_MAIN_TYPE_NAME" value="com.google.gwt.dev.DevMode"/> |
| 5 | 5 | <booleanAttribute key="com.gwtplugins.gdt.eclipse.suiteWarArgumentProcessor.IS_WAR_FROM_PROJECT_PROPERTIES" value="true"/> |
| 6 | - <stringAttribute key="com.gwtplugins.gwt.eclipse.core.CLASSIC_DEVMODE_CODE_SERVER_PORT" value="9877"/> |
|
| 6 | + <stringAttribute key="com.gwtplugins.gwt.eclipse.core.CLASSIC_DEVMODE_CODE_SERVER_PORT" value="9878"/> |
|
| 7 | 7 | <listAttribute key="com.gwtplugins.gwt.eclipse.core.ENTRY_POINT_MODULES"> |
| 8 | 8 | <listEntry value="com.sap.sse.gwt.StorageMessagingTest"/> |
| 9 | 9 | <listEntry value="com.sap.sse.gwt.StorageMessaging"/> |
| 10 | 10 | </listAttribute> |
| 11 | 11 | <booleanAttribute key="com.gwtplugins.gwt.eclipse.core.SUPERDEVMODE_ENABLED" value="true"/> |
| 12 | 12 | <stringAttribute key="com.gwtplugins.gwt.eclipse.core.URL" value="/gwt-base/StorageMessagingTest.html"/> |
| 13 | + <booleanAttribute key="org.eclipse.debug.core.ATTR_FORCE_SYSTEM_CONSOLE_ENCODING" value="false"/> |
|
| 13 | 14 | <listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS"> |
| 14 | 15 | <listEntry value="/com.sap.sse.gwt"/> |
| 15 | 16 | </listAttribute> |
| ... | ... | @@ -22,6 +23,8 @@ |
| 22 | 23 | </listAttribute> |
| 23 | 24 | <booleanAttribute key="org.eclipse.jdt.debug.ui.CONSIDER_INHERITED_MAIN" value="true"/> |
| 24 | 25 | <booleanAttribute key="org.eclipse.jdt.debug.ui.INCLUDE_EXTERNAL_JARS" value="true"/> |
| 26 | + <booleanAttribute key="org.eclipse.jdt.launching.ATTR_ATTR_USE_ARGFILE" value="false"/> |
|
| 27 | + <booleanAttribute key="org.eclipse.jdt.launching.ATTR_SHOW_CODEDETAILS_IN_EXCEPTION_MESSAGES" value="true"/> |
|
| 25 | 28 | <booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_CLASSPATH_ONLY_JAR" value="false"/> |
| 26 | 29 | <listAttribute key="org.eclipse.jdt.launching.CLASSPATH"> |
| 27 | 30 | <listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry containerPath="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8" javaProject="com.sap.sse.gwt" path="1" type="4"/> "/> |
| ... | ... | @@ -49,9 +52,10 @@ |
| 49 | 52 | </listAttribute> |
| 50 | 53 | <stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="com.gwtplugins.gwt.eclipse.core.moduleClasspathProvider"/> |
| 51 | 54 | <booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/> |
| 55 | + <stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17/"/> |
|
| 52 | 56 | <stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.DevMode"/> |
| 53 | 57 | <stringAttribute key="org.eclipse.jdt.launching.MODULE_NAME" value="com.sap.sse.gwt"/> |
| 54 | - <stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-style PRETTY -incremental -workDir "${project_loc:com.sap.sse.gwt}/.tmp/gwt-work" -war "${project_loc:com.sap.sse.gwt}" -noserver -remoteUI "${gwt_remote_ui_server_port}:${unique_id}" -logLevel INFO -codeServerPort 9877 -startupUrl /gwt-base/StorageMessagingTest.html -startupUrl /gwt-base/StorageMessaging.html com.sap.sse.gwt.StorageMessaging com.sap.sse.gwt.StorageMessagingTest"/> |
|
| 58 | + <stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-style PRETTY -incremental -workDir "${project_loc:com.sap.sse.gwt}/.tmp/gwt-work" -war "${project_loc:com.sap.sse.gwt}" -noserver -remoteUI "${gwt_remote_ui_server_port}:${unique_id}" -logLevel INFO -codeServerPort 9878 -startupUrl /gwt-base/StorageMessagingTest.html -startupUrl /gwt-base/StorageMessaging.html com.sap.sse.gwt.StorageMessaging com.sap.sse.gwt.StorageMessagingTest"/> |
|
| 55 | 59 | <stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sse.gwt"/> |
| 56 | 60 | <stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-XX:+UseG1GC -XX:-UseStringDeduplication -Dgwt.watchFileChanges=false -Xmx2048m -Dgwt-usearchives=false -Dgwt.persistentunitcache=false"/> |
| 57 | 61 | <stringAttribute key="org.eclipse.jdt.launching.WORKING_DIRECTORY" value="${project_loc:com.sap.sailing.gwt.ui}/.tmp/gwt-work"/> |
java/com.sap.sse.gwt/resources/com/sap/sse/gwt/client/images/unlock.png
| ... | ... | Binary files /dev/null and b/java/com.sap.sse.gwt/resources/com/sap/sse/gwt/client/images/unlock.png differ |
java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/IconResources.java
| ... | ... | @@ -11,6 +11,9 @@ public interface IconResources extends ClientBundle { |
| 11 | 11 | @Source("images/change-acl.png") |
| 12 | 12 | ImageResource changeACLIcon(); |
| 13 | 13 | |
| 14 | + @Source("images/unlock.png") |
|
| 15 | + ImageResource resetLockIcon(); |
|
| 16 | + |
|
| 14 | 17 | @Source("images/change-ownership.png") |
| 15 | 18 | ImageResource changeOwnershipIcon(); |
| 16 | 19 |
java/com.sap.sse.landscape.aws/resources/shiro.ini
| ... | ... | @@ -26,7 +26,7 @@ securityManager.subjectDAO = $subjectDAO |
| 26 | 26 | securityManager.sessionManager.globalSessionTimeout = 31536000000 |
| 27 | 27 | cacheManager = com.sap.sse.security.SessionCacheManager |
| 28 | 28 | securityManager.cacheManager = $cacheManager |
| 29 | -authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithLockingAndBanning |
|
| 29 | +authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithTimedLocks |
|
| 30 | 30 | securityManager.authenticator.authenticationStrategy = $authenticationStrategy |
| 31 | 31 | |
| 32 | 32 | # Support for anonymous user permissions |
java/com.sap.sse.replication/resources/shiro.ini
| ... | ... | @@ -26,7 +26,7 @@ securityManager.subjectDAO = $subjectDAO |
| 26 | 26 | securityManager.sessionManager.globalSessionTimeout = 31536000000 |
| 27 | 27 | cacheManager = com.sap.sse.security.SessionCacheManager |
| 28 | 28 | securityManager.cacheManager = $cacheManager |
| 29 | -authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithLockingAndBanning |
|
| 29 | +authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithTimedLocks |
|
| 30 | 30 | securityManager.authenticator.authenticationStrategy = $authenticationStrategy |
| 31 | 31 | |
| 32 | 32 | # Support for anonymous user permissions |
java/com.sap.sse.security.common/src/com/sap/sse/security/shared/BasicUserStore.java
| ... | ... | @@ -5,8 +5,8 @@ import java.util.Set; |
| 5 | 5 | import java.util.UUID; |
| 6 | 6 | |
| 7 | 7 | import com.sap.sse.common.Named; |
| 8 | +import com.sap.sse.common.TimedLock; |
|
| 8 | 9 | import com.sap.sse.common.Util.Pair; |
| 9 | -import com.sap.sse.security.shared.impl.LockingAndBanning; |
|
| 10 | 10 | import com.sap.sse.security.shared.impl.Ownership; |
| 11 | 11 | import com.sap.sse.security.shared.impl.Role; |
| 12 | 12 | import com.sap.sse.security.shared.impl.SecuredSecurityTypes; |
| ... | ... | @@ -60,7 +60,7 @@ public interface BasicUserStore extends UserGroupProvider, Named { |
| 60 | 60 | |
| 61 | 61 | User getUserByAccessToken(String accessToken); |
| 62 | 62 | |
| 63 | - User createUser(String name, String email, LockingAndBanning lockingAndBanning, Account... accounts) |
|
| 63 | + User createUser(String name, String email, TimedLock timedLock, Account... accounts) |
|
| 64 | 64 | throws UserManagementException; |
| 65 | 65 | |
| 66 | 66 | void addUser(User user) throws UserManagementException; |
java/com.sap.sse.security.common/src/com/sap/sse/security/shared/IPAddress.java
| ... | ... | @@ -0,0 +1,28 @@ |
| 1 | +package com.sap.sse.security.shared; |
|
| 2 | + |
|
| 3 | +import com.sap.sse.security.shared.impl.SecuredSecurityTypes; |
|
| 4 | + |
|
| 5 | +public class IPAddress implements WithQualifiedObjectIdentifier { |
|
| 6 | + private static final long serialVersionUID = 8016397230668484898L; |
|
| 7 | + private final String ipAddress; |
|
| 8 | + |
|
| 9 | + public IPAddress(final String ipAddress) { |
|
| 10 | + this.ipAddress = ipAddress; |
|
| 11 | + } |
|
| 12 | + |
|
| 13 | + @Override |
|
| 14 | + public String getName() { |
|
| 15 | + return ipAddress; |
|
| 16 | + } |
|
| 17 | + |
|
| 18 | + @Override |
|
| 19 | + public QualifiedObjectIdentifier getIdentifier() { |
|
| 20 | + return getPermissionType().getQualifiedObjectIdentifier(new TypeRelativeObjectIdentifier(ipAddress)); |
|
| 21 | + } |
|
| 22 | + |
|
| 23 | + @Override |
|
| 24 | + public HasPermissions getPermissionType() { |
|
| 25 | + return SecuredSecurityTypes.LOCKED_IP; |
|
| 26 | + } |
|
| 27 | + |
|
| 28 | +} |
java/com.sap.sse.security.common/src/com/sap/sse/security/shared/dto/UserDTO.java
| ... | ... | @@ -31,7 +31,7 @@ public class UserDTO extends |
| 31 | 31 | private SecurityInformationDTO securityInformation = new SecurityInformationDTO(); |
| 32 | 32 | private StrippedUserGroupDTO defaultTenantForCurrentServer; |
| 33 | 33 | |
| 34 | - private Set<RoleWithSecurityDTO> roles; |
|
| 34 | + private Set<RoleWithSecurityDTO> roles; // TODO turn to HashSet to reduce number of serializers to generate |
|
| 35 | 35 | |
| 36 | 36 | @Deprecated // gwt only |
| 37 | 37 | UserDTO() { |
| ... | ... | @@ -59,7 +59,22 @@ public class UserDTO extends |
| 59 | 59 | Util.addAll(roles, this.getRolesInternal()); |
| 60 | 60 | this.lockedUntil = lockedUntil; |
| 61 | 61 | } |
| 62 | - |
|
| 62 | + |
|
| 63 | + public UserDTO copyWithTimePoint(TimePoint lockedUntil) { |
|
| 64 | + final List<AccountDTO> accountsCopy = new ArrayList<AccountDTO>(); |
|
| 65 | + Util.addAll(this.accounts, accountsCopy); |
|
| 66 | + final HashSet<RoleWithSecurityDTO> rolesCopy = new HashSet<>(); |
|
| 67 | + Util.addAll(this.roles, rolesCopy); |
|
| 68 | + final List<WildcardPermissionWithSecurityDTO> permissionsCopy = new ArrayList<WildcardPermissionWithSecurityDTO>(); |
|
| 69 | + for (WildcardPermission wp : this.getPermissions()) { |
|
| 70 | + permissionsCopy.add((WildcardPermissionWithSecurityDTO) wp); |
|
| 71 | + } |
|
| 72 | + final List<StrippedUserGroupDTO> groupsCopy = new ArrayList<StrippedUserGroupDTO>(); |
|
| 73 | + Util.addAll(this.groups, groupsCopy); |
|
| 74 | + return new UserDTO(this.getName(), this.email, this.fullName, this.company, this.locale, this.emailValidated, |
|
| 75 | + accountsCopy, rolesCopy, this.defaultTenantForCurrentServer, permissionsCopy, groupsCopy, lockedUntil); |
|
| 76 | + } |
|
| 77 | + |
|
| 63 | 78 | @Override |
| 64 | 79 | protected Set<RoleWithSecurityDTO> getRolesInternal() { |
| 65 | 80 | return roles; |
java/com.sap.sse.security.common/src/com/sap/sse/security/shared/impl/LockingAndBanning.java
| ... | ... | @@ -1,35 +0,0 @@ |
| 1 | -package com.sap.sse.security.shared.impl; |
|
| 2 | - |
|
| 3 | -import java.io.Serializable; |
|
| 4 | - |
|
| 5 | -import com.sap.sse.common.TimePoint; |
|
| 6 | - |
|
| 7 | -/** |
|
| 8 | - * Holds information about a user's log-on history which is then used to decide whether the user account should be |
|
| 9 | - * locked temporarily or permanently for certain forms of authentication. |
|
| 10 | - * <p> |
|
| 11 | - * |
|
| 12 | - * For example, failed password authentication requests shall be logged by the realm using calls to |
|
| 13 | - * {@link #failedPasswordAuthentication()}, successful ones with {@link #successfulPasswordAuthentication()}. Using the |
|
| 14 | - * {@link #isAuthenticationLocked()} method, a realm can determine if the user account to which this object belongs |
|
| 15 | - * shall currently accept password authentication. |
|
| 16 | - * <p> |
|
| 17 | - * |
|
| 18 | - * A possible strategy for an implementation could be to add an increasing delay for each failed password |
|
| 19 | - * authentication, but reduce or clear the delay after a successful password authentication. |
|
| 20 | - * |
|
| 21 | - * @author Axel Uhl (d043530) |
|
| 22 | - * |
|
| 23 | - */ |
|
| 24 | -public interface LockingAndBanning extends Serializable { |
|
| 25 | - void failedPasswordAuthentication(); |
|
| 26 | - |
|
| 27 | - /** |
|
| 28 | - * @return {@code true} if this locking and banning record changed due to this call |
|
| 29 | - */ |
|
| 30 | - boolean successfulPasswordAuthentication(); |
|
| 31 | - |
|
| 32 | - boolean isAuthenticationLocked(); |
|
| 33 | - |
|
| 34 | - TimePoint getLockedUntil(); |
|
| 35 | -} |
java/com.sap.sse.security.common/src/com/sap/sse/security/shared/impl/LockingAndBanningImpl.java
| ... | ... | @@ -1,82 +0,0 @@ |
| 1 | -package com.sap.sse.security.shared.impl; |
|
| 2 | - |
|
| 3 | -import com.sap.sse.common.Duration; |
|
| 4 | -import com.sap.sse.common.TimePoint; |
|
| 5 | -import com.sap.sse.common.Util; |
|
| 6 | - |
|
| 7 | -public class LockingAndBanningImpl implements LockingAndBanning { |
|
| 8 | - private static final long serialVersionUID = 3547356744366236677L; |
|
| 9 | - |
|
| 10 | - public static final Duration DEFAULT_INITIAL_LOCKING_DELAY = Duration.ONE_SECOND; |
|
| 11 | - |
|
| 12 | - /** |
|
| 13 | - * An always valid time point which may be in the past. If it is in the future, |
|
| 14 | - * {@link #isAuthenticationLocked()} will return {@code true}. |
|
| 15 | - */ |
|
| 16 | - private TimePoint lockedUntil; |
|
| 17 | - |
|
| 18 | - /** |
|
| 19 | - * An always valid, non-zero duration that indicates for how long into the future the {@link #lockedUntil} time |
|
| 20 | - * point will be set in case a {@link #failedPasswordAuthentication() failed password authentication} is notified. |
|
| 21 | - */ |
|
| 22 | - private Duration nextLockingDelay; |
|
| 23 | - |
|
| 24 | - /** |
|
| 25 | - * Creates an instance that is unlocked and has a "last locking delay" of one second |
|
| 26 | - */ |
|
| 27 | - public LockingAndBanningImpl() { |
|
| 28 | - this(TimePoint.BeginningOfTime, DEFAULT_INITIAL_LOCKING_DELAY); |
|
| 29 | - } |
|
| 30 | - |
|
| 31 | - public LockingAndBanningImpl(TimePoint lockedUntil, Duration nextLockingDelay) { |
|
| 32 | - super(); |
|
| 33 | - this.lockedUntil = lockedUntil; |
|
| 34 | - this.nextLockingDelay = nextLockingDelay; |
|
| 35 | - } |
|
| 36 | - |
|
| 37 | - /** |
|
| 38 | - * Locks for the {@link #nextLockingDelay} and doubles the delay for the next failed attempt. |
|
| 39 | - */ |
|
| 40 | - @Override |
|
| 41 | - public void failedPasswordAuthentication() { |
|
| 42 | - lockedUntil = TimePoint.now().plus(nextLockingDelay); |
|
| 43 | - nextLockingDelay = nextLockingDelay.times(2); |
|
| 44 | - } |
|
| 45 | - |
|
| 46 | - @Override |
|
| 47 | - public boolean successfulPasswordAuthentication() { |
|
| 48 | - final Duration oldLockingDelay = nextLockingDelay; |
|
| 49 | - nextLockingDelay = DEFAULT_INITIAL_LOCKING_DELAY; |
|
| 50 | - final TimePoint oldLockedUntil = lockedUntil; |
|
| 51 | - lockedUntil = TimePoint.BeginningOfTime; |
|
| 52 | - return !Util.equalsWithNull(oldLockingDelay, nextLockingDelay) || !Util.equalsWithNull(oldLockedUntil, lockedUntil); |
|
| 53 | - } |
|
| 54 | - |
|
| 55 | - @Override |
|
| 56 | - public boolean isAuthenticationLocked() { |
|
| 57 | - return TimePoint.now().before(lockedUntil); |
|
| 58 | - } |
|
| 59 | - |
|
| 60 | - @Override |
|
| 61 | - public TimePoint getLockedUntil() { |
|
| 62 | - return lockedUntil; |
|
| 63 | - } |
|
| 64 | - |
|
| 65 | - public Duration getNextLockingDelay() { |
|
| 66 | - return nextLockingDelay; |
|
| 67 | - } |
|
| 68 | - |
|
| 69 | - @Override |
|
| 70 | - public String toString() { |
|
| 71 | - final StringBuilder result = new StringBuilder(); |
|
| 72 | - if (isAuthenticationLocked()) { |
|
| 73 | - result.append("locked until "); |
|
| 74 | - result.append(getLockedUntil()); |
|
| 75 | - } else { |
|
| 76 | - result.append("unlocked"); |
|
| 77 | - } |
|
| 78 | - result.append(", next locking duration: "); |
|
| 79 | - result.append(getNextLockingDelay()); |
|
| 80 | - return result.toString(); |
|
| 81 | - } |
|
| 82 | -} |
java/com.sap.sse.security.common/src/com/sap/sse/security/shared/impl/SecuredSecurityTypes.java
| ... | ... | @@ -5,6 +5,7 @@ import java.util.HashSet; |
| 5 | 5 | import java.util.Set; |
| 6 | 6 | |
| 7 | 7 | import com.sap.sse.security.shared.HasPermissions; |
| 8 | +import com.sap.sse.security.shared.IPAddress; |
|
| 8 | 9 | import com.sap.sse.security.shared.RoleDefinition; |
| 9 | 10 | |
| 10 | 11 | /** |
| ... | ... | @@ -41,14 +42,20 @@ public class SecuredSecurityTypes extends HasPermissionsImpl { |
| 41 | 42 | |
| 42 | 43 | public static enum UserActions implements Action { |
| 43 | 44 | /** Update a user's password without knowing the old password. */ |
| 44 | - FORCE_OVERWRITE_PASSWORD, ADD_SUBSCRIPTION, BE_PREMIUM |
|
| 45 | + FORCE_OVERWRITE_PASSWORD, ADD_SUBSCRIPTION, BE_PREMIUM, MANAGE_LOCK |
|
| 45 | 46 | }; |
| 46 | 47 | /** |
| 47 | 48 | * type-relative identifier is the {@link User#getName() username}. |
| 48 | 49 | */ |
| 49 | 50 | public static final HasPermissions USER = new SecuredSecurityTypes("USER", DefaultActions |
| 50 | 51 | .plus(UserActions.FORCE_OVERWRITE_PASSWORD, PublicReadableActions.READ_PUBLIC, |
| 51 | - UserActions.ADD_SUBSCRIPTION, UserActions.BE_PREMIUM)); |
|
| 52 | + UserActions.ADD_SUBSCRIPTION, UserActions.BE_PREMIUM, UserActions.MANAGE_LOCK)); |
|
| 53 | + |
|
| 54 | + |
|
| 55 | + /** |
|
| 56 | + * type-relative identifier is the {@link IPAddress#getName() ip address as String}. |
|
| 57 | + */ |
|
| 58 | + public static final HasPermissions LOCKED_IP = new SecuredSecurityTypes("LOCKED_IP", DefaultActions.values()); |
|
| 52 | 59 | |
| 53 | 60 | /** |
| 54 | 61 | * type-relative identifier is the {@link RoleDefinition#getId() role ID's} string representation |
| ... | ... | @@ -115,8 +122,8 @@ public class SecuredSecurityTypes extends HasPermissionsImpl { |
| 115 | 122 | private static final Action[] ALL_ACTIONS = new Action[] { CONFIGURE_FILE_STORAGE, CONFIGURE_LOCAL_SERVER, |
| 116 | 123 | CONFIGURE_REMOTE_INSTANCES, CREATE_OBJECT, CAN_IMPORT_MASTERDATA, CAN_EXPORT_MASTERDATA, DATA_MINING, |
| 117 | 124 | REPLICATE, START_REPLICATION, READ_REPLICATOR, THREADS, CONFIGURE_AI_AGENT, CONFIGURE_CORS_FILTER, |
| 118 | - DefaultActions.CHANGE_OWNERSHIP, DefaultActions.CHANGE_ACL, DefaultActions.CREATE, DefaultActions.DELETE, |
|
| 119 | - DefaultActions.READ, DefaultActions.UPDATE }; |
|
| 125 | + DefaultActions.CHANGE_OWNERSHIP, DefaultActions.CHANGE_ACL, DefaultActions.CREATE, |
|
| 126 | + DefaultActions.DELETE, DefaultActions.READ, DefaultActions.UPDATE }; |
|
| 120 | 127 | } |
| 121 | 128 | |
| 122 | 129 | /** |
java/com.sap.sse.security.common/src/com/sap/sse/security/shared/impl/User.java
| ... | ... | @@ -4,6 +4,7 @@ import java.util.Locale; |
| 4 | 4 | import java.util.Map; |
| 5 | 5 | |
| 6 | 6 | import com.sap.sse.common.Named; |
| 7 | +import com.sap.sse.common.TimedLock; |
|
| 7 | 8 | import com.sap.sse.common.WithID; |
| 8 | 9 | import com.sap.sse.security.shared.Account; |
| 9 | 10 | import com.sap.sse.security.shared.Account.AccountType; |
| ... | ... | @@ -125,5 +126,5 @@ public interface User extends SecurityUser<RoleDefinition, Role, UserGroup> { |
| 125 | 126 | |
| 126 | 127 | Subscription getSubscriptionById(String subscriptionId); |
| 127 | 128 | |
| 128 | - LockingAndBanning getLockingAndBanning(); |
|
| 129 | + TimedLock getTimedLock(); |
|
| 129 | 130 | } |
java/com.sap.sse.security.interface/src/com/sap/sse/security/interfaces/UserImpl.java
| ... | ... | @@ -19,12 +19,12 @@ import java.util.Set; |
| 19 | 19 | |
| 20 | 20 | import org.apache.shiro.crypto.hash.Sha256Hash; |
| 21 | 21 | |
| 22 | +import com.sap.sse.common.TimedLock; |
|
| 22 | 23 | import com.sap.sse.security.shared.Account; |
| 23 | 24 | import com.sap.sse.security.shared.Account.AccountType; |
| 24 | 25 | import com.sap.sse.security.shared.RoleDefinition; |
| 25 | 26 | import com.sap.sse.security.shared.UserGroupProvider; |
| 26 | 27 | import com.sap.sse.security.shared.WildcardPermission; |
| 27 | -import com.sap.sse.security.shared.impl.LockingAndBanning; |
|
| 28 | 28 | import com.sap.sse.security.shared.impl.Ownership; |
| 29 | 29 | import com.sap.sse.security.shared.impl.Role; |
| 30 | 30 | import com.sap.sse.security.shared.impl.SecurityUserImpl; |
| ... | ... | @@ -101,25 +101,25 @@ public class UserImpl extends SecurityUserImpl<RoleDefinition, Role, UserGroup, |
| 101 | 101 | |
| 102 | 102 | private Subscription[] subscriptions; |
| 103 | 103 | |
| 104 | - private final LockingAndBanning lockingAndBanning; |
|
| 104 | + private final TimedLock timedLock; |
|
| 105 | 105 | |
| 106 | 106 | public UserImpl(String name, String email, Map<String, UserGroup> defaultTenantForServer, |
| 107 | - UserGroupProvider userGroupProvider, LockingAndBanning lockingAndBanning, Account... accounts) { |
|
| 108 | - this(name, email, defaultTenantForServer, Arrays.asList(accounts), userGroupProvider, lockingAndBanning); |
|
| 107 | + UserGroupProvider userGroupProvider, TimedLock timedLock, Account... accounts) { |
|
| 108 | + this(name, email, defaultTenantForServer, Arrays.asList(accounts), userGroupProvider, timedLock); |
|
| 109 | 109 | } |
| 110 | 110 | |
| 111 | 111 | public UserImpl(String name, String email, Map<String, UserGroup> defaultTenantForServer, |
| 112 | - Collection<Account> accounts, UserGroupProvider userGroupProvider, LockingAndBanning lockingAndBanning) { |
|
| 112 | + Collection<Account> accounts, UserGroupProvider userGroupProvider, TimedLock timedLock) { |
|
| 113 | 113 | this(name, email, /* fullName */ null, /* company */ null, /* locale */ null, /* is email validated */ false, |
| 114 | 114 | /* password reset secret */ null, /* validation secret */ null, defaultTenantForServer, accounts, |
| 115 | - userGroupProvider, lockingAndBanning); |
|
| 115 | + userGroupProvider, timedLock); |
|
| 116 | 116 | } |
| 117 | 117 | |
| 118 | 118 | public UserImpl(String name, String email, String fullName, String company, Locale locale, Boolean emailValidated, |
| 119 | 119 | String passwordResetSecret, String validationSecret, Map<String, UserGroup> defaultTenantForServer, |
| 120 | - Collection<Account> accounts, UserGroupProvider userGroupProvider, LockingAndBanning lockingAndBanning) { |
|
| 120 | + Collection<Account> accounts, UserGroupProvider userGroupProvider, TimedLock timedLock) { |
|
| 121 | 121 | super(name); |
| 122 | - this.lockingAndBanning = lockingAndBanning; |
|
| 122 | + this.timedLock = timedLock; |
|
| 123 | 123 | this.defaultTenantForServer = defaultTenantForServer; |
| 124 | 124 | this.fullName = fullName; |
| 125 | 125 | this.company = company; |
| ... | ... | @@ -469,7 +469,7 @@ public class UserImpl extends SecurityUserImpl<RoleDefinition, Role, UserGroup, |
| 469 | 469 | } |
| 470 | 470 | |
| 471 | 471 | @Override |
| 472 | - public LockingAndBanning getLockingAndBanning() { |
|
| 473 | - return lockingAndBanning; |
|
| 472 | + public TimedLock getTimedLock() { |
|
| 473 | + return timedLock; |
|
| 474 | 474 | } |
| 475 | 475 | } |
java/com.sap.sse.security.replication.test/src/com/sap/sse/security/replication/test/SimpleSecurityReplicationTest.java
| ... | ... | @@ -99,7 +99,7 @@ public class SimpleSecurityReplicationTest extends AbstractSecurityReplicationTe |
| 99 | 99 | assertEquals(ERNIE, replicatedErnie.getName()); |
| 100 | 100 | assertFalse(replica.checkPassword(ERNIE, BERT_MY_FRIEND)); |
| 101 | 101 | // checking with incorrect password locks user for some time; wait long enough before retrying with correct password |
| 102 | - final TimePoint lockedUntil = replicatedErnie.getLockingAndBanning().getLockedUntil(); |
|
| 102 | + final TimePoint lockedUntil = replicatedErnie.getTimedLock().getLockedUntil(); |
|
| 103 | 103 | Thread.sleep(Math.max(0, TimePoint.now().until(lockedUntil).asMillis()+10)); |
| 104 | 104 | assertTrue(replica.checkPassword(ERNIE, newPassword)); |
| 105 | 105 | } |
java/com.sap.sse.security.storemerging.test/src/com/sap/sse/security/storemerging/TestGroupIdentity.java
| ... | ... | @@ -10,9 +10,9 @@ import java.util.UUID; |
| 10 | 10 | |
| 11 | 11 | import org.junit.jupiter.api.Test; |
| 12 | 12 | |
| 13 | +import com.sap.sse.common.impl.TimedLockImpl; |
|
| 13 | 14 | import com.sap.sse.security.SecurityService; |
| 14 | 15 | import com.sap.sse.security.interfaces.UserImpl; |
| 15 | -import com.sap.sse.security.shared.impl.LockingAndBanningImpl; |
|
| 16 | 16 | import com.sap.sse.security.shared.impl.User; |
| 17 | 17 | import com.sap.sse.security.shared.impl.UserGroup; |
| 18 | 18 | import com.sap.sse.security.shared.impl.UserGroupImpl; |
| ... | ... | @@ -51,7 +51,7 @@ public class TestGroupIdentity { |
| 51 | 51 | final UUID uuid2 = UUID.randomUUID(); |
| 52 | 52 | final String username = "user"; |
| 53 | 53 | final UserGroup g1 = new UserGroupImpl(uuid1, username+SecurityService.TENANT_SUFFIX); |
| 54 | - final User user = new UserImpl(username, /* email */ null, (Map<String, UserGroup>) /* defaultTenantForServer */ null, /* userGroupProvider */ null, new LockingAndBanningImpl()); |
|
| 54 | + final User user = new UserImpl(username, /* email */ null, (Map<String, UserGroup>) /* defaultTenantForServer */ null, /* userGroupProvider */ null, new TimedLockImpl()); |
|
| 55 | 55 | g1.add(user); |
| 56 | 56 | final UserGroup g2 = new UserGroupImpl(uuid2, username+SecurityService.TENANT_SUFFIX); |
| 57 | 57 | assertFalse(SecurityStoreMerger.considerGroupsIdentical(g1, g2, Collections.emptyMap())); |
| ... | ... | @@ -63,10 +63,10 @@ public class TestGroupIdentity { |
| 63 | 63 | final UUID uuid2 = UUID.randomUUID(); |
| 64 | 64 | final String username = "user"; |
| 65 | 65 | final UserGroup g1 = new UserGroupImpl(uuid1, username+SecurityService.TENANT_SUFFIX); |
| 66 | - final User user1 = new UserImpl(username, /* email */ null, (Map<String, UserGroup>) /* defaultTenantForServer */ null, /* userGroupProvider */ null, new LockingAndBanningImpl()); |
|
| 66 | + final User user1 = new UserImpl(username, /* email */ null, (Map<String, UserGroup>) /* defaultTenantForServer */ null, /* userGroupProvider */ null, new TimedLockImpl()); |
|
| 67 | 67 | g1.add(user1); |
| 68 | 68 | final UserGroup g2 = new UserGroupImpl(uuid2, username+SecurityService.TENANT_SUFFIX); |
| 69 | - final User user2 = new UserImpl(username, /* email */ null, (Map<String, UserGroup>) /* defaultTenantForServer */ null, /* userGroupProvider */ null, new LockingAndBanningImpl()); |
|
| 69 | + final User user2 = new UserImpl(username, /* email */ null, (Map<String, UserGroup>) /* defaultTenantForServer */ null, /* userGroupProvider */ null, new TimedLockImpl()); |
|
| 70 | 70 | g2.add(user2); |
| 71 | 71 | final Map<User, User> userMap = new HashMap<>(); |
| 72 | 72 | userMap.put(user2, user1); // user2 assumed to get merged with user1 |
java/com.sap.sse.security.test/src/com/sap/sse/security/test/AccessControlStoreTest.java
| ... | ... | @@ -18,6 +18,7 @@ import org.junit.jupiter.api.Test; |
| 18 | 18 | |
| 19 | 19 | import com.mongodb.MongoException; |
| 20 | 20 | import com.mongodb.client.MongoDatabase; |
| 21 | +import com.sap.sse.common.impl.TimedLockImpl; |
|
| 21 | 22 | import com.sap.sse.mongodb.MongoDBConfiguration; |
| 22 | 23 | import com.sap.sse.mongodb.MongoDBService; |
| 23 | 24 | import com.sap.sse.security.interfaces.AccessControlStore; |
| ... | ... | @@ -29,7 +30,6 @@ import com.sap.sse.security.shared.TypeRelativeObjectIdentifier; |
| 29 | 30 | import com.sap.sse.security.shared.UserGroupManagementException; |
| 30 | 31 | import com.sap.sse.security.shared.UserStoreManagementException; |
| 31 | 32 | import com.sap.sse.security.shared.WildcardPermission; |
| 32 | -import com.sap.sse.security.shared.impl.LockingAndBanningImpl; |
|
| 33 | 33 | import com.sap.sse.security.shared.impl.QualifiedObjectIdentifierImpl; |
| 34 | 34 | import com.sap.sse.security.shared.impl.User; |
| 35 | 35 | import com.sap.sse.security.shared.impl.UserGroup; |
| ... | ... | @@ -69,7 +69,7 @@ public class AccessControlStoreTest { |
| 69 | 69 | Map<String, UserGroup> defaultTenantForUser = new HashMap<>(); |
| 70 | 70 | defaultTenantForUser.put("dummyServer", adminTenant); |
| 71 | 71 | testOwner = new UserImpl("admin", "admin@sapsailing.com", defaultTenantForUser, |
| 72 | - /* userGroupProvider */ null, new LockingAndBanningImpl()); |
|
| 72 | + /* userGroupProvider */ null, new TimedLockImpl()); |
|
| 73 | 73 | } |
| 74 | 74 | |
| 75 | 75 | private void newStores() { |
java/com.sap.sse.security.test/src/com/sap/sse/security/test/LoginTest.java
| ... | ... | @@ -8,11 +8,13 @@ import static org.junit.jupiter.api.Assertions.assertNull; |
| 8 | 8 | import static org.junit.jupiter.api.Assertions.assertSame; |
| 9 | 9 | import static org.junit.jupiter.api.Assertions.assertTrue; |
| 10 | 10 | |
| 11 | +import java.io.IOException; |
|
| 11 | 12 | import java.net.UnknownHostException; |
| 12 | 13 | import java.util.Arrays; |
| 13 | 14 | import java.util.Collections; |
| 14 | 15 | import java.util.HashMap; |
| 15 | 16 | import java.util.HashSet; |
| 17 | +import java.util.Locale; |
|
| 16 | 18 | import java.util.Map; |
| 17 | 19 | import java.util.Set; |
| 18 | 20 | import java.util.UUID; |
| ... | ... | @@ -25,6 +27,7 @@ import org.junit.jupiter.api.Test; |
| 25 | 27 | import com.mongodb.MongoException; |
| 26 | 28 | import com.mongodb.client.MongoDatabase; |
| 27 | 29 | import com.sap.sse.common.Util; |
| 30 | +import com.sap.sse.common.impl.TimedLockImpl; |
|
| 28 | 31 | import com.sap.sse.common.mail.MailException; |
| 29 | 32 | import com.sap.sse.mongodb.MongoDBConfiguration; |
| 30 | 33 | import com.sap.sse.mongodb.MongoDBService; |
| ... | ... | @@ -44,7 +47,6 @@ import com.sap.sse.security.shared.UserStoreManagementException; |
| 44 | 47 | import com.sap.sse.security.shared.WildcardPermission; |
| 45 | 48 | import com.sap.sse.security.shared.WithQualifiedObjectIdentifier; |
| 46 | 49 | import com.sap.sse.security.shared.impl.AccessControlList; |
| 47 | -import com.sap.sse.security.shared.impl.LockingAndBanningImpl; |
|
| 48 | 50 | import com.sap.sse.security.shared.impl.Ownership; |
| 49 | 51 | import com.sap.sse.security.shared.impl.QualifiedObjectIdentifierImpl; |
| 50 | 52 | import com.sap.sse.security.shared.impl.Role; |
| ... | ... | @@ -242,7 +244,7 @@ public class LoginTest { |
| 242 | 244 | |
| 243 | 245 | @Test |
| 244 | 246 | public void rolesTest() throws UserStoreManagementException { |
| 245 | - userStore.createUser("me", "me@sap.com", new LockingAndBanningImpl()); |
|
| 247 | + userStore.createUser("me", "me@sap.com", new TimedLockImpl()); |
|
| 246 | 248 | RoleDefinition testRoleDefinition = userStore.createRoleDefinition(UUID.randomUUID(), "testRole", |
| 247 | 249 | Collections.emptySet()); |
| 248 | 250 | final Role testRole = new Role(testRoleDefinition, true); |
| ... | ... | @@ -254,7 +256,7 @@ public class LoginTest { |
| 254 | 256 | @Test |
| 255 | 257 | public void roleWithQualifiersTest() throws UserStoreManagementException { |
| 256 | 258 | UserGroupImpl userDefaultTenant = userStore.createUserGroup(UUID.randomUUID(), "me-tenant"); |
| 257 | - User meUser = userStore.createUser("me", "me@sap.com", new LockingAndBanningImpl()); |
|
| 259 | + User meUser = userStore.createUser("me", "me@sap.com", new TimedLockImpl()); |
|
| 258 | 260 | RoleDefinition testRoleDefinition = userStore.createRoleDefinition(UUID.randomUUID(), "testRole", |
| 259 | 261 | Collections.emptySet()); |
| 260 | 262 | final Role testRole = new Role(testRoleDefinition, userDefaultTenant, meUser, true); |
| ... | ... | @@ -268,7 +270,7 @@ public class LoginTest { |
| 268 | 270 | |
| 269 | 271 | @Test |
| 270 | 272 | public void permissionsTest() throws UserStoreManagementException { |
| 271 | - userStore.createUser("me", "me@sap.com", new LockingAndBanningImpl()); |
|
| 273 | + userStore.createUser("me", "me@sap.com", new TimedLockImpl()); |
|
| 272 | 274 | userStore.addPermissionForUser("me", new WildcardPermission("a:b:c")); |
| 273 | 275 | UserStoreImpl store2 = createAndLoadUserStore(); |
| 274 | 276 | User allUser = userStore.getUserByName(SecurityService.ALL_USERNAME); |
| ... | ... | @@ -276,6 +278,46 @@ public class LoginTest { |
| 276 | 278 | assertTrue(PermissionChecker.isPermitted(new WildcardPermission("a:b:c"), user, allUser, null, null)); |
| 277 | 279 | } |
| 278 | 280 | |
| 281 | + @Test |
|
| 282 | + public void testUnlockBearerTokenAbuser() throws UserStoreManagementException, IOException, InterruptedException { |
|
| 283 | + final String localhost = "127.0.0.1"; |
|
| 284 | + final int attempts = 4; |
|
| 285 | + for(int i = 0; i < attempts; i++) { |
|
| 286 | + securityService.failedBearerTokenAuthentication(localhost); |
|
| 287 | + final long lockDuration = (long) Math.pow(2, i) * 1000; |
|
| 288 | + final boolean isFinalAttempt = i == (attempts - 1); |
|
| 289 | + if (!isFinalAttempt) { |
|
| 290 | + Thread.sleep(lockDuration); |
|
| 291 | + } |
|
| 292 | + } |
|
| 293 | + assertTrue(securityService.isClientIPLockedForBearerTokenAuthentication(localhost)); |
|
| 294 | + securityService.releaseBearerTokenLockOnIp(localhost); |
|
| 295 | + assertFalse(securityService.isClientIPLockedForBearerTokenAuthentication(localhost)); |
|
| 296 | + } |
|
| 297 | + |
|
| 298 | + @Test |
|
| 299 | + public void testUnlockUserCreationAbuser() throws UserStoreManagementException, IOException, InterruptedException, MailException { |
|
| 300 | + final String localhost = "127.0.0.1"; |
|
| 301 | + final int attempts = 4; |
|
| 302 | + for(int i = 0; i < attempts; i++) { |
|
| 303 | + try { |
|
| 304 | + securityService.createSimpleUser("USERNAME" + i, "a@b.c", "PASSWORD", "The User", "SAP SE", |
|
| 305 | + /* validation URL */ Locale.ENGLISH, null, null, /* clientIP */ localhost, |
|
| 306 | + /* enforce strong password */ false); |
|
| 307 | + } catch (UserManagementException e) { |
|
| 308 | + // do nothing, expected due to lock |
|
| 309 | + } |
|
| 310 | + final long lockDuration = (long) Math.pow(2, i) * 1000; |
|
| 311 | + final boolean isFinalAttempt = i == (attempts - 1); |
|
| 312 | + if (!isFinalAttempt) { |
|
| 313 | + Thread.sleep(lockDuration); |
|
| 314 | + } |
|
| 315 | + } |
|
| 316 | + assertTrue(securityService.isUserCreationLockedForClientIP(localhost)); |
|
| 317 | + securityService.releaseUserCreationLockOnIp(localhost); |
|
| 318 | + assertFalse(securityService.isUserCreationLockedForClientIP(localhost)); |
|
| 319 | + } |
|
| 320 | + |
|
| 279 | 321 | private UserStoreImpl createAndLoadUserStore() throws UserStoreManagementException { |
| 280 | 322 | final UserStoreImpl store = new UserStoreImpl(DEFAULT_TENANT_NAME); |
| 281 | 323 | store.loadAndMigrateUsers(); |
java/com.sap.sse.security.test/src/com/sap/sse/security/test/PermissionCheckerTest.java
| ... | ... | @@ -21,6 +21,7 @@ import org.junit.jupiter.api.BeforeEach; |
| 21 | 21 | import org.junit.jupiter.api.Test; |
| 22 | 22 | |
| 23 | 23 | import com.sap.sailing.domain.common.security.SecuredDomainType; |
| 24 | +import com.sap.sse.common.impl.TimedLockImpl; |
|
| 24 | 25 | import com.sap.sse.security.AbstractCompositeAuthorizingRealm; |
| 25 | 26 | import com.sap.sse.security.UsernamePasswordRealm; |
| 26 | 27 | import com.sap.sse.security.interfaces.AccessControlStore; |
| ... | ... | @@ -39,7 +40,6 @@ import com.sap.sse.security.shared.UserManagementException; |
| 39 | 40 | import com.sap.sse.security.shared.WildcardPermission; |
| 40 | 41 | import com.sap.sse.security.shared.impl.AccessControlList; |
| 41 | 42 | import com.sap.sse.security.shared.impl.HasPermissionsImpl; |
| 42 | -import com.sap.sse.security.shared.impl.LockingAndBanningImpl; |
|
| 43 | 43 | import com.sap.sse.security.shared.impl.Ownership; |
| 44 | 44 | import com.sap.sse.security.shared.impl.Role; |
| 45 | 45 | import com.sap.sse.security.shared.impl.User; |
| ... | ... | @@ -94,7 +94,7 @@ public class PermissionCheckerTest { |
| 94 | 94 | userStore.deleteUser("jonas"); |
| 95 | 95 | } |
| 96 | 96 | userTenant = userStore.createUserGroup(userTenantId, "jonas-tenant"); |
| 97 | - user = userStore.createUser("jonas", "jonas@dann.io", new LockingAndBanningImpl()); |
|
| 97 | + user = userStore.createUser("jonas", "jonas@dann.io", new TimedLockImpl()); |
|
| 98 | 98 | userTenant.add(user); |
| 99 | 99 | userStore.updateUserGroup(userTenant); |
| 100 | 100 | ownership = new Ownership(user, userTenant); |
java/com.sap.sse.security.test/src/com/sap/sse/security/test/PreferenceObjectBasedNotificationSetTest.java
| ... | ... | @@ -18,6 +18,7 @@ import org.junit.jupiter.api.Test; |
| 18 | 18 | import com.mongodb.MongoException; |
| 19 | 19 | import com.mongodb.client.MongoDatabase; |
| 20 | 20 | import com.sap.sse.common.Util; |
| 21 | +import com.sap.sse.common.impl.TimedLockImpl; |
|
| 21 | 22 | import com.sap.sse.mongodb.MongoDBConfiguration; |
| 22 | 23 | import com.sap.sse.mongodb.MongoDBService; |
| 23 | 24 | import com.sap.sse.security.PreferenceObjectBasedNotificationSet; |
| ... | ... | @@ -25,7 +26,6 @@ import com.sap.sse.security.interfaces.UserImpl; |
| 25 | 26 | import com.sap.sse.security.interfaces.UserStore; |
| 26 | 27 | import com.sap.sse.security.shared.UserGroupManagementException; |
| 27 | 28 | import com.sap.sse.security.shared.UserManagementException; |
| 28 | -import com.sap.sse.security.shared.impl.LockingAndBanningImpl; |
|
| 29 | 29 | import com.sap.sse.security.shared.impl.User; |
| 30 | 30 | import com.sap.sse.security.shared.impl.UserGroup; |
| 31 | 31 | import com.sap.sse.security.userstore.mongodb.UserStoreImpl; |
| ... | ... | @@ -191,7 +191,7 @@ public class PreferenceObjectBasedNotificationSetTest { |
| 191 | 191 | |
| 192 | 192 | @Test |
| 193 | 193 | public void userWithNonVerifiedEmailIsSkippedTest() throws UserManagementException, UserGroupManagementException { |
| 194 | - store.createUser(user1, mail, new LockingAndBanningImpl()); |
|
| 194 | + store.createUser(user1, mail, new TimedLockImpl()); |
|
| 195 | 195 | store.registerPreferenceConverter(prefKey, prefConverter); |
| 196 | 196 | store.setPreferenceObject(user1, prefKey, values1); |
| 197 | 197 | PreferenceObjectBasedNotificationSetImpl notificationSet = new PreferenceObjectBasedNotificationSetImpl(prefKey, store); |
| ... | ... | @@ -238,7 +238,7 @@ public class PreferenceObjectBasedNotificationSetTest { |
| 238 | 238 | */ |
| 239 | 239 | @Test |
| 240 | 240 | public void deleteUserWithMappingTest() throws UserManagementException, UserGroupManagementException { |
| 241 | - store.createUser(user1, mail, new LockingAndBanningImpl()); |
|
| 241 | + store.createUser(user1, mail, new TimedLockImpl()); |
|
| 242 | 242 | store.registerPreferenceConverter(prefKey, prefConverter); |
| 243 | 243 | store.setPreferenceObject(user1, prefKey, values1); |
| 244 | 244 | PreferenceObjectBasedNotificationSetImpl notificationSet = new PreferenceObjectBasedNotificationSetImpl(prefKey, store); |
| ... | ... | @@ -250,7 +250,7 @@ public class PreferenceObjectBasedNotificationSetTest { |
| 250 | 250 | |
| 251 | 251 | @Test |
| 252 | 252 | public void removePreferenceConverterTest() throws UserManagementException, UserGroupManagementException { |
| 253 | - store.createUser(user1, mail, new LockingAndBanningImpl()); |
|
| 253 | + store.createUser(user1, mail, new TimedLockImpl()); |
|
| 254 | 254 | store.registerPreferenceConverter(prefKey, prefConverter); |
| 255 | 255 | store.setPreferenceObject(user1, prefKey, values1); |
| 256 | 256 | PreferenceObjectBasedNotificationSetImpl notificationSet = new PreferenceObjectBasedNotificationSetImpl(prefKey, store); |
| ... | ... | @@ -272,9 +272,9 @@ public class PreferenceObjectBasedNotificationSetTest { |
| 272 | 272 | UserGroup defaultTenantForSingleServer = store.createUserGroup(UUID.randomUUID(), username + "-tenant"); |
| 273 | 273 | Map<String, UserGroup> defaultTenantForServer = new ConcurrentHashMap<>(); |
| 274 | 274 | defaultTenantForServer.put(serverName, defaultTenantForSingleServer); |
| 275 | - store.createUser(username, email, new LockingAndBanningImpl()); |
|
| 275 | + store.createUser(username, email, new TimedLockImpl()); |
|
| 276 | 276 | store.updateUser(new UserImpl(username, email, null, null, null, true, null, null, defaultTenantForServer, |
| 277 | - Collections.emptySet(), /* userGroupProvider */ null, new LockingAndBanningImpl())); |
|
| 277 | + Collections.emptySet(), /* userGroupProvider */ null, new TimedLockImpl())); |
|
| 278 | 278 | } |
| 279 | 279 | |
| 280 | 280 | private static class PreferenceObjectBasedNotificationSetImpl extends PreferenceObjectBasedNotificationSet<HashSet<String>, String> { |
java/com.sap.sse.security.test/src/com/sap/sse/security/test/PrivilegeEscalationTest.java
| ... | ... | @@ -11,6 +11,7 @@ import org.junit.jupiter.api.AfterEach; |
| 11 | 11 | import org.junit.jupiter.api.BeforeEach; |
| 12 | 12 | import org.junit.jupiter.api.Test; |
| 13 | 13 | |
| 14 | +import com.sap.sse.common.impl.TimedLockImpl; |
|
| 14 | 15 | import com.sap.sse.security.SecurityService; |
| 15 | 16 | import com.sap.sse.security.impl.SecurityServiceImpl; |
| 16 | 17 | import com.sap.sse.security.shared.HasPermissions; |
| ... | ... | @@ -26,7 +27,6 @@ import com.sap.sse.security.shared.UserStoreManagementException; |
| 26 | 27 | import com.sap.sse.security.shared.WildcardPermission; |
| 27 | 28 | import com.sap.sse.security.shared.impl.AccessControlList; |
| 28 | 29 | import com.sap.sse.security.shared.impl.HasPermissionsImpl; |
| 29 | -import com.sap.sse.security.shared.impl.LockingAndBanningImpl; |
|
| 30 | 30 | import com.sap.sse.security.shared.impl.Ownership; |
| 31 | 31 | import com.sap.sse.security.shared.impl.Role; |
| 32 | 32 | import com.sap.sse.security.shared.impl.SecuredSecurityTypes; |
| ... | ... | @@ -65,8 +65,8 @@ public class PrivilegeEscalationTest { |
| 65 | 65 | PersistenceFactory.INSTANCE.getDefaultMongoObjectFactory(), TEST_DEFAULT_TENANT); |
| 66 | 66 | userStore.ensureDefaultRolesExist(); |
| 67 | 67 | userStore.loadAndMigrateUsers(); |
| 68 | - user = userStore.createUser(USER_USERNAME, null, new LockingAndBanningImpl()); |
|
| 69 | - user2 = userStore.createUser(USER2_USERNAME, null, new LockingAndBanningImpl()); |
|
| 68 | + user = userStore.createUser(USER_USERNAME, null, new TimedLockImpl()); |
|
| 69 | + user2 = userStore.createUser(USER2_USERNAME, null, new TimedLockImpl()); |
|
| 70 | 70 | userGroup = userStore.createUserGroup(USER_GROUP_UUID, USER_USERNAME+"-tenant"); |
| 71 | 71 | userGroup.add(user); |
| 72 | 72 | userGroup.add(user2); |
java/com.sap.sse.security.test/src/com/sap/sse/security/test/RoleDefinitionsTest.java
| ... | ... | @@ -8,11 +8,11 @@ import java.util.UUID; |
| 8 | 8 | import org.junit.jupiter.api.BeforeEach; |
| 9 | 9 | import org.junit.jupiter.api.Test; |
| 10 | 10 | |
| 11 | +import com.sap.sse.common.impl.TimedLockImpl; |
|
| 11 | 12 | import com.sap.sse.security.interfaces.UserStore; |
| 12 | 13 | import com.sap.sse.security.shared.RoleDefinition; |
| 13 | 14 | import com.sap.sse.security.shared.UserManagementException; |
| 14 | 15 | import com.sap.sse.security.shared.UserStoreManagementException; |
| 15 | -import com.sap.sse.security.shared.impl.LockingAndBanningImpl; |
|
| 16 | 16 | import com.sap.sse.security.shared.impl.Role; |
| 17 | 17 | import com.sap.sse.security.shared.impl.User; |
| 18 | 18 | import com.sap.sse.security.shared.impl.UserGroup; |
| ... | ... | @@ -37,7 +37,7 @@ public class RoleDefinitionsTest { |
| 37 | 37 | @BeforeEach |
| 38 | 38 | public void doBefore() throws UserStoreManagementException { |
| 39 | 39 | userStore.clear(); |
| 40 | - user = userStore.createUser(username, email, new LockingAndBanningImpl()); |
|
| 40 | + user = userStore.createUser(username, email, new TimedLockImpl()); |
|
| 41 | 41 | roleDefinition = userStore.createRoleDefinition(testRoleUUID, TEST_ROLE, Collections.emptySet()); |
| 42 | 42 | userGroup = userStore.createUserGroup(testGroupUUID, groupName); |
| 43 | 43 | } |
java/com.sap.sse.security.test/src/com/sap/sse/security/test/UserPreferenceObjectAndConverterTest.java
| ... | ... | @@ -9,11 +9,11 @@ import org.junit.jupiter.api.Test; |
| 9 | 9 | |
| 10 | 10 | import com.mongodb.MongoException; |
| 11 | 11 | import com.mongodb.client.MongoDatabase; |
| 12 | +import com.sap.sse.common.impl.TimedLockImpl; |
|
| 12 | 13 | import com.sap.sse.mongodb.MongoDBConfiguration; |
| 13 | 14 | import com.sap.sse.mongodb.MongoDBService; |
| 14 | 15 | import com.sap.sse.security.shared.UserGroupManagementException; |
| 15 | 16 | import com.sap.sse.security.shared.UserManagementException; |
| 16 | -import com.sap.sse.security.shared.impl.LockingAndBanningImpl; |
|
| 17 | 17 | import com.sap.sse.security.userstore.mongodb.UserStoreImpl; |
| 18 | 18 | import com.sap.sse.security.userstore.mongodb.impl.CollectionNames; |
| 19 | 19 | |
| ... | ... | @@ -115,7 +115,7 @@ public class UserPreferenceObjectAndConverterTest { |
| 115 | 115 | */ |
| 116 | 116 | @Test |
| 117 | 117 | public void deleteUserWithPreferenceObjectTest() throws UserManagementException, UserGroupManagementException { |
| 118 | - store.createUser(user1, email, new LockingAndBanningImpl()); |
|
| 118 | + store.createUser(user1, email, new TimedLockImpl()); |
|
| 119 | 119 | store.registerPreferenceConverter(prefKey1, prefConverter); |
| 120 | 120 | store.setPreferenceObject(user1, prefKey1, pref1); |
| 121 | 121 | store.deleteUser(user1); |
| ... | ... | @@ -124,7 +124,7 @@ public class UserPreferenceObjectAndConverterTest { |
| 124 | 124 | |
| 125 | 125 | @Test |
| 126 | 126 | public void removeConverterTest() throws UserManagementException, UserGroupManagementException { |
| 127 | - store.createUser(user1, email, new LockingAndBanningImpl()); |
|
| 127 | + store.createUser(user1, email, new TimedLockImpl()); |
|
| 128 | 128 | store.registerPreferenceConverter(prefKey1, prefConverter); |
| 129 | 129 | store.setPreference(user1, prefKey1, serializedPref1); |
| 130 | 130 | store.removePreferenceConverter(prefKey1); |
java/com.sap.sse.security.test/src/com/sap/sse/security/test/UserStoreTest.java
| ... | ... | @@ -6,10 +6,10 @@ import static org.junit.jupiter.api.Assertions.assertNull; |
| 6 | 6 | import org.junit.jupiter.api.BeforeEach; |
| 7 | 7 | import org.junit.jupiter.api.Test; |
| 8 | 8 | |
| 9 | +import com.sap.sse.common.impl.TimedLockImpl; |
|
| 9 | 10 | import com.sap.sse.security.interfaces.UserStore; |
| 10 | 11 | import com.sap.sse.security.shared.UserGroupManagementException; |
| 11 | 12 | import com.sap.sse.security.shared.UserManagementException; |
| 12 | -import com.sap.sse.security.shared.impl.LockingAndBanningImpl; |
|
| 13 | 13 | import com.sap.sse.security.userstore.mongodb.UserStoreImpl; |
| 14 | 14 | |
| 15 | 15 | public class UserStoreTest { |
| ... | ... | @@ -26,7 +26,7 @@ public class UserStoreTest { |
| 26 | 26 | |
| 27 | 27 | @BeforeEach |
| 28 | 28 | public void setUp() throws UserManagementException, UserGroupManagementException { |
| 29 | - userStore.createUser(username, email, new LockingAndBanningImpl()); |
|
| 29 | + userStore.createUser(username, email, new TimedLockImpl()); |
|
| 30 | 30 | userStore.setAccessToken(username, accessToken); |
| 31 | 31 | userStore.setPreference(username, prefKey, prefValue); |
| 32 | 32 | } |
java/com.sap.sse.security.test/src/com/sap/sse/security/test/UserStoreWithPersistenceTest.java
| ... | ... | @@ -24,6 +24,7 @@ import com.mongodb.MongoException; |
| 24 | 24 | import com.mongodb.client.MongoDatabase; |
| 25 | 25 | import com.sap.sse.common.Util; |
| 26 | 26 | import com.sap.sse.common.Util.Pair; |
| 27 | +import com.sap.sse.common.impl.TimedLockImpl; |
|
| 27 | 28 | import com.sap.sse.mongodb.MongoDBConfiguration; |
| 28 | 29 | import com.sap.sse.mongodb.MongoDBService; |
| 29 | 30 | import com.sap.sse.security.interfaces.UserImpl; |
| ... | ... | @@ -34,7 +35,6 @@ import com.sap.sse.security.shared.SecurityUser; |
| 34 | 35 | import com.sap.sse.security.shared.UserGroupManagementException; |
| 35 | 36 | import com.sap.sse.security.shared.UserManagementException; |
| 36 | 37 | import com.sap.sse.security.shared.UserStoreManagementException; |
| 37 | -import com.sap.sse.security.shared.impl.LockingAndBanningImpl; |
|
| 38 | 38 | import com.sap.sse.security.shared.impl.Ownership; |
| 39 | 39 | import com.sap.sse.security.shared.impl.Role; |
| 40 | 40 | import com.sap.sse.security.shared.impl.User; |
| ... | ... | @@ -92,7 +92,7 @@ public class UserStoreWithPersistenceTest { |
| 92 | 92 | |
| 93 | 93 | @Test |
| 94 | 94 | public void testCreateUser() throws UserManagementException { |
| 95 | - store.createUser(username, email, new LockingAndBanningImpl()); |
|
| 95 | + store.createUser(username, email, new TimedLockImpl()); |
|
| 96 | 96 | assertNotNull(store.getUserByName(username)); |
| 97 | 97 | assertNotNull(store.getUserByEmail(email)); |
| 98 | 98 | |
| ... | ... | @@ -103,12 +103,12 @@ public class UserStoreWithPersistenceTest { |
| 103 | 103 | |
| 104 | 104 | @Test |
| 105 | 105 | public void testMasterdataIsSaved() throws UserStoreManagementException { |
| 106 | - store.createUser(username, email, new LockingAndBanningImpl()); |
|
| 106 | + store.createUser(username, email, new TimedLockImpl()); |
|
| 107 | 107 | UserGroupImpl defaultTenant = createUserGroup(); |
| 108 | 108 | HashMap<String, UserGroup> defaultTenantForServers = new HashMap<>(); |
| 109 | 109 | defaultTenantForServers.put(serverName, defaultTenant); |
| 110 | 110 | store.updateUser(new UserImpl(username, email, fullName, company, Locale.GERMAN, false, null, null, |
| 111 | - defaultTenantForServers, Collections.emptySet(), /* userGroupProvider */ null, new LockingAndBanningImpl())); |
|
| 111 | + defaultTenantForServers, Collections.emptySet(), /* userGroupProvider */ null, new TimedLockImpl())); |
|
| 112 | 112 | newStore(); |
| 113 | 113 | User savedUser = store.getUserByName(username); |
| 114 | 114 | assertEquals(username, savedUser.getName()); |
| ... | ... | @@ -123,7 +123,7 @@ public class UserStoreWithPersistenceTest { |
| 123 | 123 | */ |
| 124 | 124 | @Test |
| 125 | 125 | public void testDeleteUser() throws UserManagementException { |
| 126 | - store.createUser(username, email, new LockingAndBanningImpl()); |
|
| 126 | + store.createUser(username, email, new TimedLockImpl()); |
|
| 127 | 127 | store.deleteUser(username); |
| 128 | 128 | assertNull(store.getUserByName(username)); |
| 129 | 129 | assertNull(store.getUserByEmail(email)); |
| ... | ... | @@ -135,7 +135,7 @@ public class UserStoreWithPersistenceTest { |
| 135 | 135 | |
| 136 | 136 | @Test |
| 137 | 137 | public void testSetPreferences() throws UserManagementException { |
| 138 | - store.createUser(username, email, new LockingAndBanningImpl()); |
|
| 138 | + store.createUser(username, email, new TimedLockImpl()); |
|
| 139 | 139 | store.setPreference(username, prefKey, prefValue); |
| 140 | 140 | assertEquals(prefValue, store.getPreference(username, prefKey)); |
| 141 | 141 | newStore(); |
| ... | ... | @@ -144,7 +144,7 @@ public class UserStoreWithPersistenceTest { |
| 144 | 144 | |
| 145 | 145 | @Test |
| 146 | 146 | public void testUnsetPreferences() throws UserManagementException { |
| 147 | - store.createUser(username, email, new LockingAndBanningImpl()); |
|
| 147 | + store.createUser(username, email, new TimedLockImpl()); |
|
| 148 | 148 | store.setPreference(username, prefKey, prefValue); |
| 149 | 149 | store.unsetPreference(username, prefKey); |
| 150 | 150 | assertNull(store.getPreference(username, prefKey)); |
| ... | ... | @@ -157,7 +157,7 @@ public class UserStoreWithPersistenceTest { |
| 157 | 157 | */ |
| 158 | 158 | @Test |
| 159 | 159 | public void testDeleteUserWithPreferences() throws UserManagementException { |
| 160 | - store.createUser(username, email, new LockingAndBanningImpl()); |
|
| 160 | + store.createUser(username, email, new TimedLockImpl()); |
|
| 161 | 161 | store.setPreference(username, prefKey, prefValue); |
| 162 | 162 | store.deleteUser(username); |
| 163 | 163 | assertNull(store.getPreference(username, prefKey)); |
| ... | ... | @@ -167,7 +167,7 @@ public class UserStoreWithPersistenceTest { |
| 167 | 167 | |
| 168 | 168 | @Test |
| 169 | 169 | public void testCreateUserGroup() throws UserGroupManagementException, UserManagementException { |
| 170 | - final User user = store.createUser(username, email, new LockingAndBanningImpl()); |
|
| 170 | + final User user = store.createUser(username, email, new TimedLockImpl()); |
|
| 171 | 171 | UserGroupImpl createUserGroup = createUserGroup(); |
| 172 | 172 | createUserGroup.add(user); |
| 173 | 173 | store.updateUserGroup(createUserGroup); |
| ... | ... | @@ -196,7 +196,7 @@ public class UserStoreWithPersistenceTest { |
| 196 | 196 | @Test |
| 197 | 197 | public void testTenantUsers() throws UserManagementException, UserGroupManagementException { |
| 198 | 198 | UserGroupImpl defaultTenant = createUserGroup(); |
| 199 | - final User user = store.createUser(username, email, new LockingAndBanningImpl()); |
|
| 199 | + final User user = store.createUser(username, email, new TimedLockImpl()); |
|
| 200 | 200 | defaultTenant.add(user); |
| 201 | 201 | store.updateUserGroup(defaultTenant); |
| 202 | 202 | user.getDefaultTenantMap().put(serverName, defaultTenant); |
| ... | ... | @@ -218,7 +218,7 @@ public class UserStoreWithPersistenceTest { |
| 218 | 218 | |
| 219 | 219 | @Test |
| 220 | 220 | public void testUserGroups() throws UserManagementException, UserGroupManagementException { |
| 221 | - final User user = store.createUser(username, email, new LockingAndBanningImpl()); |
|
| 221 | + final User user = store.createUser(username, email, new TimedLockImpl()); |
|
| 222 | 222 | final String GROUP_NAME = "group"; |
| 223 | 223 | final UserGroupImpl group = store.createUserGroup(UUID.randomUUID(), GROUP_NAME); |
| 224 | 224 | group.add(user); |
| ... | ... | @@ -242,7 +242,7 @@ public class UserStoreWithPersistenceTest { |
| 242 | 242 | @Test |
| 243 | 243 | public void testGetExistingQualificationsForRoleDefinition() |
| 244 | 244 | throws UserManagementException, UserGroupManagementException { |
| 245 | - User user = store.createUser("def", "d@test.de", new LockingAndBanningImpl()); |
|
| 245 | + User user = store.createUser("def", "d@test.de", new TimedLockImpl()); |
|
| 246 | 246 | RoleDefinitionImpl roleDefinition = new RoleDefinitionImpl(UUID.randomUUID(), "My-Test-Role"); |
| 247 | 247 | store.createRoleDefinition(roleDefinition.getId(), roleDefinition.getName(), new ArrayList<>()); |
| 248 | 248 | UserGroupImpl userGroup = store.createUserGroup(UUID.randomUUID(), "Test-Usergroup"); |
java/com.sap.sse.security.ui/.settings/com.gwtplugins.gwt.eclipse.core.prefs
| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | -//gwtVersion_/com.google.gwt.user/lib=2.11.1 |
|
| 1 | +//gwtVersion_/com.google.gwt.user/lib= |
|
| 2 | 2 | //gwtVersion_/opt/gwt-2.11.0=2.11.0 |
| 3 | 3 | //gwtVersion_/opt/gwt-2.11.1=2.11.1 |
| 4 | 4 | //gwtVersion_/opt/gwt-2.12.2=2.12.2 |
java/com.sap.sse.security.ui/GWT Security SDM.launch
| ... | ... | @@ -27,6 +27,8 @@ |
| 27 | 27 | </listAttribute> |
| 28 | 28 | <booleanAttribute key="org.eclipse.jdt.debug.ui.CONSIDER_INHERITED_MAIN" value="true"/> |
| 29 | 29 | <booleanAttribute key="org.eclipse.jdt.debug.ui.INCLUDE_EXTERNAL_JARS" value="true"/> |
| 30 | + <booleanAttribute key="org.eclipse.jdt.launching.ATTR_ATTR_USE_ARGFILE" value="false"/> |
|
| 31 | + <booleanAttribute key="org.eclipse.jdt.launching.ATTR_SHOW_CODEDETAILS_IN_EXCEPTION_MESSAGES" value="true"/> |
|
| 30 | 32 | <booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_CLASSPATH_ONLY_JAR" value="false"/> |
| 31 | 33 | <listAttribute key="org.eclipse.jdt.launching.CLASSPATH"> |
| 32 | 34 | <listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry containerPath="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8" javaProject="com.sap.sailing.gwt.ui" path="1" type="4"/> "/> |
| ... | ... | @@ -101,12 +103,14 @@ |
| 101 | 103 | <listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry id="org.eclipse.jdt.launching.classpathentry.defaultClasspath"> <memento exportedEntriesOnly="false" project="com.sap.sailing.gwt.ui"/> </runtimeClasspathEntry> "/> |
| 102 | 104 | <listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry path="3" projectName="com.sap.sse.common" type="1"/> "/> |
| 103 | 105 | <listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.landscape.aws.common/src" path="3" type="2"/> "/> |
| 106 | + <listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.branding/src" path="3" type="2"/> "/> |
|
| 104 | 107 | </listAttribute> |
| 105 | 108 | <stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="com.gwtplugins.gwt.eclipse.core.moduleClasspathProvider"/> |
| 106 | 109 | <booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/> |
| 110 | + <stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17/"/> |
|
| 107 | 111 | <stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.DevMode"/> |
| 108 | 112 | <stringAttribute key="org.eclipse.jdt.launching.MODULE_NAME" value="com.sap.sse.security.ui"/> |
| 109 | - <stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-incremental -workDir "${project_loc:com.sap.sse.security.ui}/.tmp/gwt-work" -war "${project_loc:com.sap.sse.security.ui}" -noserver -remoteUI "${gwt_remote_ui_server_port}:${unique_id}" -logLevel INFO -codeServerPort 9877 -startupUrl /security/ui/Login.html -startupUrl /security/ui/EditProfile.html -startupUrl /security/ui/EmailValidation.html -startupUrl /security/ui/UserManagement.html -startupUrl /security/ui/Register.html -startupUrl /security/ui/Login.html com.sap.sse.security.ui.EditProfile com.sap.sse.security.ui.EmailValidation com.sap.sse.security.ui.OAuthLogin com.sap.sse.security.ui.UserManagementEntryPoint com.sap.sse.security.ui.Register com.sap.sse.security.ui.Login"/> |
|
| 113 | + <stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-incremental -workDir "${project_loc:com.sap.sse.security.ui}/.tmp/gwt-work" -war "${project_loc:com.sap.sse.security.ui}" -noserver -remoteUI "${gwt_remote_ui_server_port}:${unique_id}" -bindAddress 0.0.0.0 -logLevel INFO -codeServerPort 9877 -startupUrl /security/ui/Login.html -startupUrl /security/ui/EditProfile.html -startupUrl /security/ui/EmailValidation.html -startupUrl /security/ui/UserManagement.html -startupUrl /security/ui/Register.html -startupUrl /security/ui/Login.html com.sap.sse.security.ui.EditProfile com.sap.sse.security.ui.EmailValidation com.sap.sse.security.ui.OAuthLogin com.sap.sse.security.ui.UserManagementEntryPoint com.sap.sse.security.ui.Register com.sap.sse.security.ui.Login"/> |
|
| 110 | 114 | <stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sse.security.ui"/> |
| 111 | 115 | <stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-XX:+UseG1GC -XX:-UseStringDeduplication -Dgwt.watchFileChanges=false -Xmx2048m -Dgwt-usearchives=false -Dgwt.persistentunitcache=false"/> |
| 112 | 116 | <stringAttribute key="org.eclipse.jdt.launching.WORKING_DIRECTORY" value="${project_loc:com.sap.sailing.gwt.ui}/.tmp/gwt-work"/> |
java/com.sap.sse.security.ui/pom.xml
| ... | ... | @@ -77,9 +77,19 @@ |
| 77 | 77 | <!-- GWT version detected from dependencyManagement --> |
| 78 | 78 | <execution> |
| 79 | 79 | <configuration> |
| 80 | + <!-- for debugger attachment: --> |
|
| 81 | + <!-- |
|
| 82 | + <extraJvmArgs> |
|
| 83 | + -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:8003 |
|
| 84 | + </extraJvmArgs> |
|
| 85 | + <extraJvmArgsCompiler> |
|
| 86 | + -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:8003 |
|
| 87 | + </extraJvmArgsCompiler> |
|
| 88 | + <logLevel>DEBUG</logLevel> |
|
| 89 | + --> |
|
| 80 | 90 | <!-- <userAgents>chrome</userAgents> --> |
| 81 | 91 | <modules> |
| 82 | - <module>com.sap.sse.security.ui.UserManagement</module> |
|
| 92 | + <module>com.sap.sse.security.ui.UserManagement</module> |
|
| 83 | 93 | <module>com.sap.sse.security.ui.UserManagementEntryPoint</module> |
| 84 | 94 | <module>com.sap.sse.security.ui.Login</module> |
| 85 | 95 | <module>com.sap.sse.security.ui.OAuthLogin</module> |
java/com.sap.sse.security.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sse/common/impl/MillisecondsDurationImpl_CustomFieldSerializer.java
| ... | ... | @@ -0,0 +1,48 @@ |
| 1 | +package com.google.gwt.user.client.rpc.core.com.sap.sse.common.impl; |
|
| 2 | + |
|
| 3 | +import com.google.gwt.user.client.rpc.CustomFieldSerializer; |
|
| 4 | +import com.google.gwt.user.client.rpc.SerializationException; |
|
| 5 | +import com.google.gwt.user.client.rpc.SerializationStreamReader; |
|
| 6 | +import com.google.gwt.user.client.rpc.SerializationStreamWriter; |
|
| 7 | +import com.sap.sse.common.impl.MillisecondsDurationImpl; |
|
| 8 | + |
|
| 9 | +public class MillisecondsDurationImpl_CustomFieldSerializer extends CustomFieldSerializer<MillisecondsDurationImpl> { |
|
| 10 | + |
|
| 11 | + @Override |
|
| 12 | + public void serializeInstance(SerializationStreamWriter streamWriter, MillisecondsDurationImpl instance) |
|
| 13 | + throws SerializationException { |
|
| 14 | + serialize(streamWriter, instance); |
|
| 15 | + } |
|
| 16 | + |
|
| 17 | + public static void serialize(SerializationStreamWriter streamWriter, MillisecondsDurationImpl instance) |
|
| 18 | + throws SerializationException { |
|
| 19 | + streamWriter.writeLong(instance.asMillis()); |
|
| 20 | + } |
|
| 21 | + |
|
| 22 | + @Override |
|
| 23 | + public boolean hasCustomInstantiateInstance() { |
|
| 24 | + return true; |
|
| 25 | + } |
|
| 26 | + |
|
| 27 | + @Override |
|
| 28 | + public MillisecondsDurationImpl instantiateInstance(SerializationStreamReader streamReader) |
|
| 29 | + throws SerializationException { |
|
| 30 | + return instantiate(streamReader); |
|
| 31 | + } |
|
| 32 | + |
|
| 33 | + public static MillisecondsDurationImpl instantiate(SerializationStreamReader streamReader) |
|
| 34 | + throws SerializationException { |
|
| 35 | + return new MillisecondsDurationImpl(streamReader.readLong()); |
|
| 36 | + } |
|
| 37 | + |
|
| 38 | + @Override |
|
| 39 | + public void deserializeInstance(SerializationStreamReader streamReader, MillisecondsDurationImpl instance) |
|
| 40 | + throws SerializationException { |
|
| 41 | + deserialize(streamReader, instance); |
|
| 42 | + } |
|
| 43 | + |
|
| 44 | + public static void deserialize(SerializationStreamReader streamReader, MillisecondsDurationImpl instance) { |
|
| 45 | + // Done by instantiateInstance |
|
| 46 | + } |
|
| 47 | + |
|
| 48 | +} |
java/com.sap.sse.security.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sse/common/impl/SecondsDurationImpl_CustomFieldSerializer.java
| ... | ... | @@ -0,0 +1,48 @@ |
| 1 | +package com.google.gwt.user.client.rpc.core.com.sap.sse.common.impl; |
|
| 2 | + |
|
| 3 | +import com.google.gwt.user.client.rpc.CustomFieldSerializer; |
|
| 4 | +import com.google.gwt.user.client.rpc.SerializationException; |
|
| 5 | +import com.google.gwt.user.client.rpc.SerializationStreamReader; |
|
| 6 | +import com.google.gwt.user.client.rpc.SerializationStreamWriter; |
|
| 7 | +import com.sap.sse.common.impl.SecondsDurationImpl; |
|
| 8 | + |
|
| 9 | +public class SecondsDurationImpl_CustomFieldSerializer extends CustomFieldSerializer<SecondsDurationImpl> { |
|
| 10 | + |
|
| 11 | + @Override |
|
| 12 | + public void serializeInstance(SerializationStreamWriter streamWriter, SecondsDurationImpl instance) |
|
| 13 | + throws SerializationException { |
|
| 14 | + serialize(streamWriter, instance); |
|
| 15 | + } |
|
| 16 | + |
|
| 17 | + public static void serialize(SerializationStreamWriter streamWriter, SecondsDurationImpl instance) |
|
| 18 | + throws SerializationException { |
|
| 19 | + streamWriter.writeDouble(instance.asSeconds()); |
|
| 20 | + } |
|
| 21 | + |
|
| 22 | + @Override |
|
| 23 | + public boolean hasCustomInstantiateInstance() { |
|
| 24 | + return true; |
|
| 25 | + } |
|
| 26 | + |
|
| 27 | + @Override |
|
| 28 | + public SecondsDurationImpl instantiateInstance(SerializationStreamReader streamReader) |
|
| 29 | + throws SerializationException { |
|
| 30 | + return instantiate(streamReader); |
|
| 31 | + } |
|
| 32 | + |
|
| 33 | + public static SecondsDurationImpl instantiate(SerializationStreamReader streamReader) |
|
| 34 | + throws SerializationException { |
|
| 35 | + return new SecondsDurationImpl(streamReader.readDouble()); |
|
| 36 | + } |
|
| 37 | + |
|
| 38 | + @Override |
|
| 39 | + public void deserializeInstance(SerializationStreamReader streamReader, SecondsDurationImpl instance) |
|
| 40 | + throws SerializationException { |
|
| 41 | + deserialize(streamReader, instance); |
|
| 42 | + } |
|
| 43 | + |
|
| 44 | + public static void deserialize(SerializationStreamReader streamReader, SecondsDurationImpl instance) { |
|
| 45 | + // Done by instantiateInstance |
|
| 46 | + } |
|
| 47 | + |
|
| 48 | +} |
java/com.sap.sse.security.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sse/security/ui/shared/IpToTimedLockDTO_CustomFieldSerializer.java
| ... | ... | @@ -0,0 +1,48 @@ |
| 1 | +package com.google.gwt.user.client.rpc.core.com.sap.sse.security.ui.shared; |
|
| 2 | + |
|
| 3 | +import com.google.gwt.user.client.rpc.CustomFieldSerializer; |
|
| 4 | +import com.google.gwt.user.client.rpc.SerializationException; |
|
| 5 | +import com.google.gwt.user.client.rpc.SerializationStreamReader; |
|
| 6 | +import com.google.gwt.user.client.rpc.SerializationStreamWriter; |
|
| 7 | +import com.sap.sse.common.TimedLock; |
|
| 8 | +import com.sap.sse.security.ui.shared.IpToTimedLockDTO; |
|
| 9 | + |
|
| 10 | +public class IpToTimedLockDTO_CustomFieldSerializer extends CustomFieldSerializer<IpToTimedLockDTO> { |
|
| 11 | + @Override |
|
| 12 | + public void serializeInstance(SerializationStreamWriter streamWriter, IpToTimedLockDTO instance) |
|
| 13 | + throws SerializationException { |
|
| 14 | + serialize(streamWriter, instance); |
|
| 15 | + } |
|
| 16 | + |
|
| 17 | + public static void serialize(SerializationStreamWriter streamWriter, IpToTimedLockDTO instance) |
|
| 18 | + throws SerializationException { |
|
| 19 | + streamWriter.writeString(instance.getIp()); |
|
| 20 | + streamWriter.writeObject(instance.getTimedLock()); |
|
| 21 | + } |
|
| 22 | + |
|
| 23 | + @Override |
|
| 24 | + public boolean hasCustomInstantiateInstance() { |
|
| 25 | + return true; |
|
| 26 | + } |
|
| 27 | + |
|
| 28 | + @Override |
|
| 29 | + public IpToTimedLockDTO instantiateInstance(SerializationStreamReader streamReader) |
|
| 30 | + throws SerializationException { |
|
| 31 | + return instantiate(streamReader); |
|
| 32 | + } |
|
| 33 | + |
|
| 34 | + public static IpToTimedLockDTO instantiate(SerializationStreamReader streamReader) |
|
| 35 | + throws SerializationException { |
|
| 36 | + return new IpToTimedLockDTO(streamReader.readString(), (TimedLock) streamReader.readObject()); |
|
| 37 | + } |
|
| 38 | + |
|
| 39 | + @Override |
|
| 40 | + public void deserializeInstance(SerializationStreamReader streamReader, IpToTimedLockDTO instance) |
|
| 41 | + throws SerializationException { |
|
| 42 | + deserialize(streamReader, instance); |
|
| 43 | + } |
|
| 44 | + |
|
| 45 | + public static void deserialize(SerializationStreamReader streamReader, IpToTimedLockDTO instance) { |
|
| 46 | + // Done by instantiateInstance |
|
| 47 | + } |
|
| 48 | +} |
java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/SerializationDummy.java
| ... | ... | @@ -1,12 +1,14 @@ |
| 1 | 1 | package com.sap.sse.security.ui.client; |
| 2 | 2 | |
| 3 | 3 | import com.google.gwt.user.client.rpc.IsSerializable; |
| 4 | +import com.sap.sse.common.TimedLock; |
|
| 4 | 5 | import com.sap.sse.landscape.aws.common.shared.SecuredAwsLandscapeType; |
| 5 | 6 | import com.sap.sse.security.shared.HasPermissions; |
| 6 | 7 | import com.sap.sse.security.shared.TypeRelativeObjectIdentifier; |
| 7 | 8 | |
| 8 | 9 | public class SerializationDummy implements IsSerializable { |
| 9 | - public TypeRelativeObjectIdentifier typeRelativeObjectIdentifier; |
|
| 10 | - public HasPermissions hasPermissions; |
|
| 11 | - public SecuredAwsLandscapeType securedAwsLandscapeType; |
|
| 10 | + TypeRelativeObjectIdentifier typeRelativeObjectIdentifier; |
|
| 11 | + HasPermissions hasPermissions; |
|
| 12 | + SecuredAwsLandscapeType securedAwsLandscapeType; |
|
| 13 | + TimedLock timedLock; |
|
| 12 | 14 | } |
java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/UserManagementService.java
| ... | ... | @@ -24,6 +24,7 @@ import com.sap.sse.security.shared.dto.StrippedUserGroupDTO; |
| 24 | 24 | import com.sap.sse.security.shared.dto.UserDTO; |
| 25 | 25 | import com.sap.sse.security.shared.dto.UserGroupDTO; |
| 26 | 26 | import com.sap.sse.security.ui.oauth.client.CredentialDTO; |
| 27 | +import com.sap.sse.security.ui.shared.IpToTimedLockDTO; |
|
| 27 | 28 | import com.sap.sse.security.ui.shared.SecurityServiceSharingDTO; |
| 28 | 29 | import com.sap.sse.security.ui.shared.SuccessInfo; |
| 29 | 30 | |
| ... | ... | @@ -33,7 +34,7 @@ public interface UserManagementService extends RemoteService { |
| 33 | 34 | throws UnauthorizedException, org.apache.shiro.authz.UnauthorizedException; |
| 34 | 35 | |
| 35 | 36 | Collection<UserGroupDTO> getUserGroups() throws org.apache.shiro.authz.UnauthorizedException; |
| 36 | - |
|
| 37 | + |
|
| 37 | 38 | UserGroupDTO getUserGroupByName(String userGroupName) |
| 38 | 39 | throws UnauthorizedException, org.apache.shiro.authz.UnauthorizedException; |
| 39 | 40 | |
| ... | ... | @@ -94,4 +95,8 @@ public interface UserManagementService extends RemoteService { |
| 94 | 95 | Pair<Boolean, ArrayList<String>> getCORSFilterConfiguration(); |
| 95 | 96 | |
| 96 | 97 | String getBrandingConfigurationId(); |
| 98 | + |
|
| 99 | + ArrayList<IpToTimedLockDTO> getClientIPBasedTimedLocksForUserCreation() throws UnauthorizedException; |
|
| 100 | + |
|
| 101 | + ArrayList<IpToTimedLockDTO> getClientIPBasedTimedLocksForBearerTokenAbuse() throws UnauthorizedException; |
|
| 97 | 102 | } |
java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/UserManagementServiceAsync.java
| ... | ... | @@ -22,6 +22,7 @@ import com.sap.sse.security.shared.dto.StrippedUserGroupDTO; |
| 22 | 22 | import com.sap.sse.security.shared.dto.UserDTO; |
| 23 | 23 | import com.sap.sse.security.shared.dto.UserGroupDTO; |
| 24 | 24 | import com.sap.sse.security.ui.oauth.client.CredentialDTO; |
| 25 | +import com.sap.sse.security.ui.shared.IpToTimedLockDTO; |
|
| 25 | 26 | import com.sap.sse.security.ui.shared.SecurityServiceSharingDTO; |
| 26 | 27 | import com.sap.sse.security.ui.shared.SuccessInfo; |
| 27 | 28 | |
| ... | ... | @@ -107,4 +108,8 @@ public interface UserManagementServiceAsync { |
| 107 | 108 | void getCORSFilterConfiguration(AsyncCallback<Pair<Boolean, ArrayList<String>>> callback); |
| 108 | 109 | |
| 109 | 110 | void getBrandingConfigurationId(AsyncCallback<String> callback); |
| 111 | + |
|
| 112 | + void getClientIPBasedTimedLocksForUserCreation(AsyncCallback<ArrayList<IpToTimedLockDTO>> callback); |
|
| 113 | + |
|
| 114 | + void getClientIPBasedTimedLocksForBearerTokenAbuse(AsyncCallback<ArrayList<IpToTimedLockDTO>> callback); |
|
| 110 | 115 | } |
java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/UserManagementWriteService.java
| ... | ... | @@ -78,6 +78,11 @@ public interface UserManagementWriteService extends UserManagementService { |
| 78 | 78 | throws UserManagementException, org.apache.shiro.authz.UnauthorizedException; |
| 79 | 79 | |
| 80 | 80 | SuccessInfo deleteUser(String username) throws UnauthorizedException, org.apache.shiro.authz.UnauthorizedException; |
| 81 | + |
|
| 82 | + SuccessInfo unlockUser(String username) throws UnauthorizedException, org.apache.shiro.authz.UnauthorizedException; |
|
| 83 | + |
|
| 84 | + Set<SuccessInfo> unlockUsers(Set<String> usernames) |
|
| 85 | + throws UnauthorizedException, org.apache.shiro.authz.UnauthorizedException; |
|
| 81 | 86 | |
| 82 | 87 | Set<SuccessInfo> deleteUsers(Set<String> usernames) |
| 83 | 88 | throws UnauthorizedException, org.apache.shiro.authz.UnauthorizedException; |
| ... | ... | @@ -136,4 +141,9 @@ public interface UserManagementWriteService extends UserManagementService { |
| 136 | 141 | |
| 137 | 142 | AccessControlListDTO overrideAccessControlList(QualifiedObjectIdentifier idOfAccessControlledObject, |
| 138 | 143 | AccessControlListDTO acl) throws UnauthorizedException, org.apache.shiro.authz.UnauthorizedException; |
| 144 | + |
|
| 145 | + void releaseUserCreationLockOnIp(String ip) throws UnauthorizedException; |
|
| 146 | + |
|
| 147 | + void releaseBearerTokenLockOnIp(String ip) throws UnauthorizedException; |
|
| 148 | + |
|
| 139 | 149 | } |
java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/UserManagementWriteServiceAsync.java
| ... | ... | @@ -61,6 +61,10 @@ public interface UserManagementWriteServiceAsync extends UserManagementServiceAs |
| 61 | 61 | void updateRoleDefinition(RoleDefinitionDTO roleWithNewProperties, AsyncCallback<Void> callback); |
| 62 | 62 | |
| 63 | 63 | void deleteUser(String username, AsyncCallback<SuccessInfo> callback); |
| 64 | + |
|
| 65 | + void unlockUser(String username, AsyncCallback<SuccessInfo> callback); |
|
| 66 | + |
|
| 67 | + void unlockUsers(Set<String> username, AsyncCallback<Set<SuccessInfo>> callback); |
|
| 64 | 68 | |
| 65 | 69 | void deleteUsers(Set<String> usernames, AsyncCallback<Set<SuccessInfo>> callback); |
| 66 | 70 | |
| ... | ... | @@ -121,4 +125,8 @@ public interface UserManagementWriteServiceAsync extends UserManagementServiceAs |
| 121 | 125 | void setCORSFilterConfigurationAllowedOrigins(ArrayList<String> allowedOrigins, AsyncCallback<Void> callback); |
| 122 | 126 | |
| 123 | 127 | void fileTakedownNotice(TakedownNoticeRequestContext takedownNoticeRequestContext, AsyncCallback<Void> callback); |
| 128 | + |
|
| 129 | + void releaseUserCreationLockOnIp(String ip, AsyncCallback<Void> asyncCallback); |
|
| 130 | + |
|
| 131 | + void releaseBearerTokenLockOnIp(String ip, AsyncCallback<Void> asyncCallback); |
|
| 124 | 132 | } |
java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/component/AccessControlledButtonPanel.java
| ... | ... | @@ -179,6 +179,28 @@ public class AccessControlledButtonPanel extends Composite { |
| 179 | 179 | return resolveButtonVisibility(permissionCheck, new Button(text, wrap(permissionCheck, callback))); |
| 180 | 180 | } |
| 181 | 181 | |
| 182 | + /** |
|
| 183 | + * Adds an action button, appended with selection count, whose visibility depends on the provided {@link Supplier |
|
| 184 | + * permission check}. |
|
| 185 | + * |
|
| 186 | + * @param text |
|
| 187 | + * the {@link String text} to show on the button |
|
| 188 | + * @param permissionCheck |
|
| 189 | + * the {@link Supplier permission check} to decide if the action button is visible or not |
|
| 190 | + * @param callback |
|
| 191 | + * the {@link Command callback} to execute on button click, if permission is granted |
|
| 192 | + * @return the created {@link Button} instance |
|
| 193 | + */ |
|
| 194 | + public <T extends Named> Button addCountingAction(final String text, final SetSelectionModel<T> selectionModel, |
|
| 195 | + final Supplier<Boolean> permissionCheck, final Command callback) { |
|
| 196 | + if (selectionModel == null) { |
|
| 197 | + throw new IllegalArgumentException("Selection model for a remove action must not be null"); |
|
| 198 | + } |
|
| 199 | + final SelectedElementsCountingButton<T> button = new SelectedElementsCountingButton<T>(text, |
|
| 200 | + selectionModel, wrap(permissionCheck, callback)); |
|
| 201 | + return resolveButtonVisibility(permissionCheck, button); |
|
| 202 | + } |
|
| 203 | + |
|
| 182 | 204 | private Button resolveButtonVisibility(final Supplier<Boolean> permissionCheck, final Button button) { |
| 183 | 205 | this.buttonToPermissions.put(button, permissionCheck); |
| 184 | 206 | button.getElement().getStyle().setMarginRight(5, Unit.PX); |
java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/component/DefaultActionsImagesBarCell.java
| ... | ... | @@ -5,6 +5,7 @@ import java.util.Arrays; |
| 5 | 5 | import com.sap.sse.gwt.client.IconResources; |
| 6 | 6 | import com.sap.sse.gwt.client.celltable.ImagesBarCell; |
| 7 | 7 | import com.sap.sse.security.shared.HasPermissions.DefaultActions; |
| 8 | +import com.sap.sse.security.shared.impl.SecuredSecurityTypes.UserActions; |
|
| 8 | 9 | import com.sap.sse.security.ui.client.i18n.StringMessages; |
| 9 | 10 | |
| 10 | 11 | public class DefaultActionsImagesBarCell extends ImagesBarCell { |
| ... | ... | @@ -14,6 +15,7 @@ public class DefaultActionsImagesBarCell extends ImagesBarCell { |
| 14 | 15 | public static final String ACTION_CHANGE_OWNERSHIP = DefaultActions.CHANGE_OWNERSHIP.name(); |
| 15 | 16 | public static final String ACTION_MIGRATE_GROUP_OWNERSHIP_HIERARCHY = "MIGRATE_GROUP_OWNERSHIP_HIERARCHY"; |
| 16 | 17 | public static final String ACTION_CHANGE_ACL = DefaultActions.CHANGE_ACL.name(); |
| 18 | + public static final String ACTION_MANAGE_LOCK = UserActions.MANAGE_LOCK.name(); |
|
| 17 | 19 | |
| 18 | 20 | protected final StringMessages stringMessages; |
| 19 | 21 | |
| ... | ... | @@ -24,7 +26,7 @@ public class DefaultActionsImagesBarCell extends ImagesBarCell { |
| 24 | 26 | @Override |
| 25 | 27 | protected Iterable<ImageSpec> getImageSpecs() { |
| 26 | 28 | return Arrays.asList(getUpdateImageSpec(), getDeleteImageSpec(), getChangeOwnershipImageSpec(), |
| 27 | - getChangeACLImageSpec()); |
|
| 29 | + getChangeACLImageSpec(), getResetLockImageSpec()); |
|
| 28 | 30 | } |
| 29 | 31 | |
| 30 | 32 | /** |
| ... | ... | @@ -65,4 +67,12 @@ public class DefaultActionsImagesBarCell extends ImagesBarCell { |
| 65 | 67 | IconResources.INSTANCE.changeACLIcon()); |
| 66 | 68 | } |
| 67 | 69 | |
| 70 | + /** |
|
| 71 | + * @return {@link ImageSpec} for {@link DefaultActions#CHANGE_ACL reset lock} action |
|
| 72 | + */ |
|
| 73 | + protected ImageSpec getResetLockImageSpec() { |
|
| 74 | + return new ImageSpec(ACTION_MANAGE_LOCK, stringMessages.resetLock(), |
|
| 75 | + IconResources.INSTANCE.resetLockIcon()); |
|
| 76 | + } |
|
| 77 | + |
|
| 68 | 78 | } |
| ... | ... | \ No newline at end of file |
java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages.java
| ... | ... | @@ -150,7 +150,11 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages { |
| 150 | 150 | String groups(); |
| 151 | 151 | String group(); |
| 152 | 152 | String errorDeletingUser(String username, String message); |
| 153 | - String doYouReallyWantToRemoveUser(String name); |
|
| 153 | + String doYouReallyWantToUnlockUser(String name); |
|
| 154 | + String doYouReallyWantToUnlockNUsers(int n); |
|
| 155 | + String unlockSucceededForUser(String username); |
|
| 156 | + String unlockFailedForUser(String username); |
|
| 157 | + String userIsAlreadyUnlocked(); |
|
| 154 | 158 | String errorTryingToUpdateUser(String username, String message); |
| 155 | 159 | String ownership(); |
| 156 | 160 | String editObjectOwnership(); |
| ... | ... | @@ -161,6 +165,7 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages { |
| 161 | 165 | String userNotFound(String username); |
| 162 | 166 | String usergroupNotFound(String userGroupName); |
| 163 | 167 | String actionChangeACL(); |
| 168 | + String resetLock(); |
|
| 164 | 169 | String editACLForObject(String objectName); |
| 165 | 170 | String acl(); |
| 166 | 171 | String errorUpdatingAcl(String name); |
| ... | ... | @@ -226,4 +231,6 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages { |
| 226 | 231 | String lockedUntil(); |
| 227 | 232 | String passwordAuthenticationCurrentlyLockedForUser(); |
| 228 | 233 | String clientCurrentlyLockedForUserCreation(); |
| 234 | + String unlockedSuccessfully(); |
|
| 235 | + String failedToUnlock(); |
|
| 229 | 236 | } |
java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages.properties
| ... | ... | @@ -156,7 +156,11 @@ validated=Validated |
| 156 | 156 | groups=Groups |
| 157 | 157 | group=Group |
| 158 | 158 | errorDeletingUser=Error deleting user {0}: {1} |
| 159 | -doYouReallyWantToRemoveUser=Really remove user {0}? |
|
| 159 | +doYouReallyWantToUnlockUser=Really allow locked user {0} to access SAP Sailing Analytics again? |
|
| 160 | +doYouReallyWantToUnlockNUsers=Really allow {0} users to access SAP Sailing Analytics again? |
|
| 161 | +unlockSucceededForUser=Unlock succeeded for user {0}. |
|
| 162 | +unlockFailedForUser=Unlock failed for user {0}. |
|
| 163 | +userIsAlreadyUnlocked=User is already unlocked. |
|
| 160 | 164 | errorTryingToUpdateUser=Error trying to update user {0}: {1} |
| 161 | 165 | ownership=Ownership |
| 162 | 166 | editObjectOwnership=Edit object ownership |
| ... | ... | @@ -167,6 +171,7 @@ pleaseWaitUntilUserGroupNameIsResolved=Please wait until user group name is reso |
| 167 | 171 | userNotFound=User {0} not found |
| 168 | 172 | usergroupNotFound=User group {0} not found |
| 169 | 173 | actionChangeACL=Change ACL |
| 174 | +resetLock=Reset Lock |
|
| 170 | 175 | editACLForObject=Edit ACL for ''{0}'' |
| 171 | 176 | acl=ACL |
| 172 | 177 | errorUpdatingAcl=Error updating ACL: {0}. |
| ... | ... | @@ -232,4 +237,6 @@ paymentUnfinished=Waiting for payment to process. |
| 232 | 237 | paymentFinished=Payment successful. Plan roles have been granted. |
| 233 | 238 | lockedUntil=Locked until |
| 234 | 239 | passwordAuthenticationCurrentlyLockedForUser=Password authentication is currently locked for this user due to too many failed login attempts. Please try again later. |
| 235 | -clientCurrentlyLockedForUserCreation=Client is currently locked for user creation after too many user creations by the same client. Please try again later. |
|
| ... | ... | \ No newline at end of file |
| 0 | +clientCurrentlyLockedForUserCreation=Client is currently locked for user creation after too many user creations by the same client. Please try again later. |
|
| 1 | +unlockedSuccessfully=Unlocked successfully |
|
| 2 | +failedToUnlock=Failed to unlock |
|
| ... | ... | \ No newline at end of file |
java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages_de.properties
| ... | ... | @@ -100,6 +100,7 @@ error=Es ist ein Fehler aufgetreten |
| 100 | 100 | refresh=Aktualisieren |
| 101 | 101 | preferredLanguage=Bevorzugte Sprache |
| 102 | 102 | cannotResetInvalidURL="Fehlerhafte Reseturl ..." |
| 103 | +cannotUploadVideosAndImagesTogether=Videos und Bilder können nicht gleichzeitig hochgeladen werden, bitte laden Sie sie separat hoch. |
|
| 103 | 104 | sharedSettingsLink=Link mit Settings |
| 104 | 105 | makeDefault=Als Voreinstellung |
| 105 | 106 | makeDefaultInProgress=In Bearbeitung... |
| ... | ... | @@ -154,7 +155,11 @@ validated=Geprüft |
| 154 | 155 | groups=Gruppen |
| 155 | 156 | group=Gruppe |
| 156 | 157 | errorDeletingUser=Fehler beim Löschen des Benutzers {0}: {1} |
| 157 | -doYouReallyWantToRemoveUser=Benutzer {0} wirklich löschen? |
|
| 158 | +doYouReallyWantToUnlockUser=Soll dem gesperrten Benutzer {0} der Zugriff auf SAP Sailing Analytics wirklich wieder gewährt werden? |
|
| 159 | +doYouReallyWantToUnlockNUsers=Soll {0} Benutzern wirklich wieder Zugriff auf SAP Sailing Analytics gewährt werden? |
|
| 160 | +unlockSucceededForUser=Entsperrung für Benutzer {0} erfolgreich. |
|
| 161 | +unlockFailedForUser=Entsperrung für Benutzer {0} fehlgeschlagen. |
|
| 162 | +userIsAlreadyUnlocked=Der Benutzer ist bereits entsperrt. |
|
| 158 | 163 | errorTryingToUpdateUser=Fehler beim Aktualisieren des Benutzers {0}: {1} |
| 159 | 164 | ownership=Besitzer |
| 160 | 165 | editObjectOwnership=Objekt-Besitzer bearbeiten |
| ... | ... | @@ -165,6 +170,7 @@ pleaseWaitUntilUserGroupNameIsResolved=Bitte warten, bis der Benutzergruppen-Nam |
| 165 | 170 | userNotFound=Benutzer {0} nicht gefunden |
| 166 | 171 | usergroupNotFound=Benutzergruppe {0} nicht gefunden |
| 167 | 172 | actionChangeACL=ACL ändern |
| 173 | +resetLock=Sperre zurücksetzen |
|
| 168 | 174 | editACLForObject=ACL für ''{0}'' ändern |
| 169 | 175 | acl=ACL |
| 170 | 176 | errorUpdatingAcl=Fehler beim Aktualisieren der ACL: {0}. |
| ... | ... | @@ -196,6 +202,7 @@ nullUserGroup=<Anonyme Usergruppe> |
| 196 | 202 | editRolesAndPermissionsForUser=Rollen und Berechtigungen für Benutzer {0} editieren |
| 197 | 203 | permissionType=Berechtigungstyp |
| 198 | 204 | couldNotLoadMarkTemplates=Markvorlagen konnten nicht geladen werden. |
| 205 | +transitive=Transitiv |
|
| 199 | 206 | failGeneratingHostedPageObject=Fehler beim Erzeugen der eingebetteten Seiten-Objekte, bitte nochmals versuchen |
| 200 | 207 | errorOpenCheckout=Fehler beim Öffnen des Bezahlformulars: {0} |
| 201 | 208 | errorSaveSubscription=Fehler beim Abspeichern des Abonnements: {0} |
| ... | ... | @@ -211,6 +218,7 @@ premiumFeatureListDescription=Vergleichen Sie alle Funktionen des FREE- oder PRE |
| 211 | 218 | selectSubscriptionPlan=Bitte wählen sie einen Abonnementplan aus. |
| 212 | 219 | features=Enthalten |
| 213 | 220 | premiumFeature=Premium-Feature |
| 221 | +premium=Prämie |
|
| 214 | 222 | price=Preis |
| 215 | 223 | pleaseSubscribeToUse=Erlaubnis verweigert! Möchten sie ein Abonement abschließen? |
| 216 | 224 | pleaseSubscribeToUseSpecific=Sie sind nicht berechtigt, die Funktion "{0}" zu verwenden! Möchten sie ein Abonement abschließen? |
| ... | ... | @@ -228,4 +236,6 @@ paymentUnfinished=Warte auf Zahlungsbestätigung. |
| 228 | 236 | paymentFinished=Zahlung erfolgreich. Planrollen wurden verliehen. |
| 229 | 237 | lockedUntil=Gesperrt bis |
| 230 | 238 | passwordAuthenticationCurrentlyLockedForUser=Passwort-Authentifizierung ist für diesen Benutzer derzeit aufgrund zu vieler fehlgeschlagener Versuche gesperrt. Bitter später erneut versuchen. |
| 231 | -clientCurrentlyLockedForUserCreation=Benutzererstellung für den aktuellen Client ist derzeit wegen zu vieler neuer Nutzer vom selben Client gesperrt. Bitte später erneut versuchen. |
|
| ... | ... | \ No newline at end of file |
| 0 | +clientCurrentlyLockedForUserCreation=Benutzererstellung für den aktuellen Client ist derzeit wegen zu vieler neuer Nutzer vom selben Client gesperrt. Bitte später erneut versuchen. |
|
| 1 | +unlockedSuccessfully=Erfolgreich entsperrt. |
|
| 2 | +failedToUnlock=Entsperren fehlgeschlagen |
|
| ... | ... | \ No newline at end of file |
java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/usermanagement/UserManagementPanel.java
| ... | ... | @@ -26,6 +26,7 @@ import com.sap.sse.gwt.client.celltable.RefreshableMultiSelectionModel; |
| 26 | 26 | import com.sap.sse.gwt.client.dialog.DataEntryDialog.DialogCallback; |
| 27 | 27 | import com.sap.sse.gwt.client.panels.LabeledAbstractFilterablePanel; |
| 28 | 28 | import com.sap.sse.security.shared.dto.UserDTO; |
| 29 | +import com.sap.sse.security.shared.impl.SecuredSecurityTypes.UserActions; |
|
| 29 | 30 | import com.sap.sse.security.ui.client.UserManagementWriteServiceAsync; |
| 30 | 31 | import com.sap.sse.security.ui.client.UserService; |
| 31 | 32 | import com.sap.sse.security.ui.client.component.AccessControlledButtonPanel; |
| ... | ... | @@ -102,6 +103,12 @@ public class UserManagementPanel<TR extends CellTableWithCheckboxResources> exte |
| 102 | 103 | } |
| 103 | 104 | }); |
| 104 | 105 | removeButton.ensureDebugId("DeleteUserButton"); |
| 106 | + final boolean hasManageLockPermissionOnAnyUser = userService.hasCurrentUserAnyPermission(USER.getPermission(UserActions.MANAGE_LOCK), null); |
|
| 107 | + if (hasManageLockPermissionOnAnyUser) { |
|
| 108 | + final Button unlockButton = buttonPanel.addCountingAction("Unlock", userSelectionModel, () -> true, |
|
| 109 | + () -> onCountingUnlockButtonClick(stringMessages, userManagementWriteService, errorReporter)); |
|
| 110 | + unlockButton.ensureDebugId("UnlockUserButton"); |
|
| 111 | + } |
|
| 105 | 112 | ScrollPanel scrollPanel = new ScrollPanel(userList.asWidget()); |
| 106 | 113 | LabeledAbstractFilterablePanel<UserDTO> filterBox = userList.getFilterField(); |
| 107 | 114 | filterBox.getElement().setPropertyString("placeholder", stringMessages.filterUsers()); |
| ... | ... | @@ -137,6 +144,67 @@ public class UserManagementPanel<TR extends CellTableWithCheckboxResources> exte |
| 137 | 144 | west.add(detailsPanel); |
| 138 | 145 | } |
| 139 | 146 | |
| 147 | + private void onCountingUnlockButtonClick(final StringMessages stringMessages, |
|
| 148 | + final UserManagementWriteServiceAsync userManagementWriteService, final ErrorReporter errorReporter) { |
|
| 149 | + final Set<UserDTO> selectedUsers = userSelectionModel.getSelectedSet(); |
|
| 150 | + final Set<String> selectedUsernames = new HashSet<String>(); |
|
| 151 | + selectedUsers.forEach((u) -> selectedUsernames.add(u.getName())); |
|
| 152 | + final boolean didConfirm = Window.confirm(stringMessages.doYouReallyWantToUnlockNUsers(selectedUsers.size())); |
|
| 153 | + if (didConfirm) { |
|
| 154 | + // run api, collect results |
|
| 155 | + final Set<String> successUserNames = new HashSet<String>(); |
|
| 156 | + final Set<String> failureUserNames = new HashSet<String>(); |
|
| 157 | + userManagementWriteService.unlockUsers(selectedUsernames, new AsyncCallback<Set<SuccessInfo>>() { |
|
| 158 | + @Override |
|
| 159 | + public void onSuccess(Set<SuccessInfo> result) { |
|
| 160 | + for (SuccessInfo si : result) { |
|
| 161 | + final String username = si.getExtra(); |
|
| 162 | + if (si.isSuccessful()) { |
|
| 163 | + successUserNames.add(username); |
|
| 164 | + } else { |
|
| 165 | + failureUserNames.add(username); |
|
| 166 | + } |
|
| 167 | + } |
|
| 168 | + // update locked until entries for successful unlocks |
|
| 169 | + final LabeledAbstractFilterablePanel<UserDTO> filterField = userList.getFilterField(); |
|
| 170 | + final List<UserDTO> allUsersWithUpdatedEntries = new ArrayList<UserDTO>(); |
|
| 171 | + for (UserDTO user : filterField.getAll()) { |
|
| 172 | + final boolean didSucceed = successUserNames.contains(user.getName()); |
|
| 173 | + if (didSucceed) { |
|
| 174 | + allUsersWithUpdatedEntries.add(user.copyWithTimePoint(null)); |
|
| 175 | + } else { |
|
| 176 | + allUsersWithUpdatedEntries.add(user); |
|
| 177 | + } |
|
| 178 | + } |
|
| 179 | + filterField.updateAll(allUsersWithUpdatedEntries); |
|
| 180 | + // create alert dialog |
|
| 181 | + String text = ""; |
|
| 182 | + if (successUserNames.size() != 0) { |
|
| 183 | + text += stringMessages.unlockedSuccessfully() + ":\n"; |
|
| 184 | + for (String name : successUserNames) { |
|
| 185 | + text += " • " + name + "\n"; |
|
| 186 | + } |
|
| 187 | + text += "\n"; |
|
| 188 | + } |
|
| 189 | + if (failureUserNames.size() != 0) { |
|
| 190 | + text += stringMessages.failedToUnlock() + ":\n"; |
|
| 191 | + for (String name : failureUserNames) { |
|
| 192 | + text += " • " + name + "\n"; |
|
| 193 | + } |
|
| 194 | + } |
|
| 195 | + if(!text.equals("")) { |
|
| 196 | + Window.alert(text); |
|
| 197 | + } |
|
| 198 | + } |
|
| 199 | + |
|
| 200 | + @Override |
|
| 201 | + public void onFailure(Throwable caught) { |
|
| 202 | + errorReporter.reportError(stringMessages.failedToUnlock() + ": " + caught.getMessage()); |
|
| 203 | + } |
|
| 204 | + }); |
|
| 205 | + } |
|
| 206 | + } |
|
| 207 | + |
|
| 140 | 208 | /** shows the edit dialog if the user exists */ |
| 141 | 209 | private void showRolesAndPermissionsEditDialog(UserService userService, |
| 142 | 210 | final CellTableWithCheckboxResources tableResources, final ErrorReporter errorReporter) { |
java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/usermanagement/UserTableWrapper.java
| ... | ... | @@ -14,6 +14,7 @@ import java.util.Comparator; |
| 14 | 14 | import java.util.HashMap; |
| 15 | 15 | import java.util.Iterator; |
| 16 | 16 | import java.util.List; |
| 17 | +import java.util.function.Consumer; |
|
| 17 | 18 | |
| 18 | 19 | import com.google.gwt.cell.client.SafeHtmlCell; |
| 19 | 20 | import com.google.gwt.core.client.Callback; |
| ... | ... | @@ -31,6 +32,8 @@ import com.sap.sse.common.Util; |
| 31 | 32 | import com.sap.sse.common.util.NaturalComparator; |
| 32 | 33 | import com.sap.sse.gwt.client.DateAndTimeFormatterUtil; |
| 33 | 34 | import com.sap.sse.gwt.client.ErrorReporter; |
| 35 | +import com.sap.sse.gwt.client.Notification; |
|
| 36 | +import com.sap.sse.gwt.client.Notification.NotificationType; |
|
| 34 | 37 | import com.sap.sse.gwt.client.celltable.AbstractSortableTextColumn; |
| 35 | 38 | import com.sap.sse.gwt.client.celltable.CellTableWithCheckboxResources; |
| 36 | 39 | import com.sap.sse.gwt.client.celltable.EntityIdentityComparator; |
| ... | ... | @@ -45,6 +48,7 @@ import com.sap.sse.security.shared.dto.RoleWithSecurityDTO; |
| 45 | 48 | import com.sap.sse.security.shared.dto.StrippedUserGroupDTO; |
| 46 | 49 | import com.sap.sse.security.shared.dto.UserDTO; |
| 47 | 50 | import com.sap.sse.security.shared.impl.SecuredSecurityTypes; |
| 51 | +import com.sap.sse.security.shared.impl.SecuredSecurityTypes.UserActions; |
|
| 48 | 52 | import com.sap.sse.security.ui.client.EntryPointLinkFactory; |
| 49 | 53 | import com.sap.sse.security.ui.client.UserManagementServiceAsync; |
| 50 | 54 | import com.sap.sse.security.ui.client.UserManagementWriteServiceAsync; |
| ... | ... | @@ -162,12 +166,53 @@ extends TableWrapper<UserDTO, S, StringMessages, TR> { |
| 162 | 166 | user->user.getLockedUntil() != null && user.getLockedUntil().after(TimePoint.now()) ? |
| 163 | 167 | DateAndTimeFormatterUtil.dateTimeMedium.render(user.getLockedUntil().asDate()) : "", |
| 164 | 168 | userColumnListHandler); |
| 169 | + final AccessControlledActionsColumn<UserDTO, DefaultActionsImagesBarCell> userActionColumn = composeUserActionColumn( |
|
| 170 | + stringMessages, errorReporter); |
|
| 171 | + filterField = new LabeledAbstractFilterablePanel<UserDTO>(new Label(stringMessages.filterUsers()), |
|
| 172 | + new ArrayList<UserDTO>(), dataProvider, stringMessages) { |
|
| 173 | + @Override |
|
| 174 | + public Iterable<String> getSearchableStrings(UserDTO t) { |
|
| 175 | + List<String> strings = new ArrayList<>(); |
|
| 176 | + strings.add(t.getName()); |
|
| 177 | + strings.add(t.getFullName()); |
|
| 178 | + strings.add(t.getEmail()); |
|
| 179 | + strings.add(t.getCompany()); |
|
| 180 | + Util.addAll(Util.map(t.getRoles(), RoleWithSecurityDTO::getName), strings); |
|
| 181 | + Util.addAll(Util.map(t.getUserGroups(), StrippedUserGroupDTO::getName), strings); |
|
| 182 | + return strings; |
|
| 183 | + } |
|
| 184 | + |
|
| 185 | + @Override |
|
| 186 | + public AbstractCellTable<UserDTO> getCellTable() { |
|
| 187 | + return table; |
|
| 188 | + } |
|
| 189 | + }; |
|
| 190 | + registerSelectionModelOnNewDataProvider(filterField.getAllListDataProvider()); |
|
| 191 | + filterField.setUpdatePermissionFilterForCheckbox(user -> userService.hasPermission(user, DefaultActions.UPDATE)); |
|
| 192 | + mainPanel.insert(filterField, 0); |
|
| 193 | + table.addColumnSortHandler(userColumnListHandler); |
|
| 194 | + table.addColumn(usernameColumn, getStringMessages().username()); |
|
| 195 | + table.addColumn(fullNameColumn, stringMessages.name()); |
|
| 196 | + table.addColumn(emailColumn, stringMessages.email()); |
|
| 197 | + table.addColumn(emailValidatedColumn, stringMessages.validated()); |
|
| 198 | + table.addColumn(companyColumn, stringMessages.company()); |
|
| 199 | + table.addColumn(groupsColumn, stringMessages.groups()); |
|
| 200 | + table.addColumn(rolesColumn, stringMessages.roles()); |
|
| 201 | + table.addColumn(permissionsColumn, stringMessages.permissions()); |
|
| 202 | + table.addColumn(lockedUntilColumn, stringMessages.lockedUntil()); |
|
| 203 | + SecuredDTOOwnerColumn.configureOwnerColumns(table, userColumnListHandler, stringMessages); |
|
| 204 | + table.addColumn(userActionColumn, stringMessages.actions()); |
|
| 205 | + table.ensureDebugId("UsersTable"); |
|
| 206 | + } |
|
| 207 | + |
|
| 208 | + private AccessControlledActionsColumn<UserDTO, DefaultActionsImagesBarCell> composeUserActionColumn( |
|
| 209 | + StringMessages stringMessages, ErrorReporter errorReporter) { |
|
| 165 | 210 | final HasPermissions type = SecuredSecurityTypes.USER; |
| 166 | 211 | final AccessControlledActionsColumn<UserDTO, DefaultActionsImagesBarCell> userActionColumn = create( |
| 167 | 212 | new DefaultActionsImagesBarCell(stringMessages), userService); |
| 168 | 213 | userActionColumn.addAction(ACTION_UPDATE, UPDATE, user -> editUser(user)); |
| 169 | 214 | userActionColumn.addAction(ACTION_DELETE, DELETE, user -> { |
| 170 | - if (Window.confirm(stringMessages.doYouReallyWantToRemoveUser(user.getName()))) { |
|
| 215 | + if (Window.confirm(stringMessages.doYouReallyWantToDeleteUser(user.getName()))) { |
|
| 171 | 216 | getUserManagementWriteService().deleteUser(user.getName(), new AsyncCallback<SuccessInfo>() { |
| 172 | 217 | @Override |
| 173 | 218 | public void onFailure(Throwable caught) { |
| ... | ... | @@ -192,47 +237,53 @@ extends TableWrapper<UserDTO, S, StringMessages, TR> { |
| 192 | 237 | final EditOwnershipDialog.DialogConfig<UserDTO> configOwnership = EditOwnershipDialog.create( |
| 193 | 238 | userService.getUserManagementWriteService(), type, |
| 194 | 239 | user -> refreshUserList((Callback<Iterable<UserDTO>, Throwable>) null), stringMessages); |
| 240 | + userActionColumn.addAction(ACTION_CHANGE_OWNERSHIP, CHANGE_OWNERSHIP, configOwnership::openOwnershipDialog); |
|
| 195 | 241 | final EditACLDialog.DialogConfig<UserDTO> configACL = EditACLDialog.create( |
| 196 | 242 | userService.getUserManagementWriteService(), type, |
| 197 | 243 | user -> user.getAccessControlList(), stringMessages); |
| 198 | - userActionColumn.addAction(ACTION_CHANGE_OWNERSHIP, CHANGE_OWNERSHIP, configOwnership::openOwnershipDialog); |
|
| 199 | 244 | userActionColumn.addAction(DefaultActionsImagesBarCell.ACTION_CHANGE_ACL, DefaultActions.CHANGE_ACL, |
| 200 | 245 | u -> configACL.openDialog(u)); |
| 201 | - filterField = new LabeledAbstractFilterablePanel<UserDTO>(new Label(stringMessages.filterUsers()), |
|
| 202 | - new ArrayList<UserDTO>(), dataProvider, stringMessages) { |
|
| 203 | - @Override |
|
| 204 | - public Iterable<String> getSearchableStrings(UserDTO t) { |
|
| 205 | - List<String> strings = new ArrayList<>(); |
|
| 206 | - strings.add(t.getName()); |
|
| 207 | - strings.add(t.getFullName()); |
|
| 208 | - strings.add(t.getEmail()); |
|
| 209 | - strings.add(t.getCompany()); |
|
| 210 | - Util.addAll(Util.map(t.getRoles(), RoleWithSecurityDTO::getName), strings); |
|
| 211 | - Util.addAll(Util.map(t.getUserGroups(), StrippedUserGroupDTO::getName), strings); |
|
| 212 | - return strings; |
|
| 213 | - } |
|
| 246 | + userActionColumn.addAction( |
|
| 247 | + DefaultActionsImagesBarCell.ACTION_MANAGE_LOCK, |
|
| 248 | + UserActions.MANAGE_LOCK, |
|
| 249 | + onManageLockPressed(stringMessages, errorReporter) |
|
| 250 | + ); |
|
| 251 | + return userActionColumn; |
|
| 252 | + } |
|
| 214 | 253 | |
| 215 | - @Override |
|
| 216 | - public AbstractCellTable<UserDTO> getCellTable() { |
|
| 217 | - return table; |
|
| 254 | + private Consumer<UserDTO> onManageLockPressed(StringMessages stringMessages, ErrorReporter errorReporter) { |
|
| 255 | + return selectedUser -> { |
|
| 256 | + final boolean isLocked = selectedUser.getLockedUntil().after(TimePoint.now()); |
|
| 257 | + if (isLocked) { |
|
| 258 | + final String userName = selectedUser.getName(); |
|
| 259 | + final boolean didConfirm = Window.confirm(stringMessages.doYouReallyWantToUnlockUser(userName)); |
|
| 260 | + if (didConfirm) { |
|
| 261 | + getUserManagementWriteService().unlockUser(userName, new AsyncCallback<SuccessInfo>() { |
|
| 262 | + @Override |
|
| 263 | + public void onSuccess(SuccessInfo result) { |
|
| 264 | + Notification.notify(stringMessages.unlockSucceededForUser(userName), NotificationType.SUCCESS); |
|
| 265 | + final List<UserDTO> usersWithUpdatedEntry = new ArrayList<UserDTO>(); |
|
| 266 | + for (UserDTO user : getAllUsers()) { |
|
| 267 | + if (user.getFullName() == selectedUser.getFullName()) { |
|
| 268 | + usersWithUpdatedEntry.add(user.copyWithTimePoint(null)); |
|
| 269 | + } else { |
|
| 270 | + usersWithUpdatedEntry.add(user); |
|
| 271 | + } |
|
| 272 | + } |
|
| 273 | + filterField.updateAll(usersWithUpdatedEntry); |
|
| 274 | + } |
|
| 275 | + |
|
| 276 | + @Override |
|
| 277 | + public void onFailure(Throwable caught) { |
|
| 278 | + Notification.notify(stringMessages.unlockFailedForUser(userName), NotificationType.ERROR); |
|
| 279 | + errorReporter.reportError(caught.getMessage()); |
|
| 280 | + } |
|
| 281 | + }); |
|
| 282 | + } |
|
| 283 | + } else { |
|
| 284 | + Notification.notify(stringMessages.userIsAlreadyUnlocked(), NotificationType.INFO); |
|
| 218 | 285 | } |
| 219 | 286 | }; |
| 220 | - registerSelectionModelOnNewDataProvider(filterField.getAllListDataProvider()); |
|
| 221 | - filterField.setUpdatePermissionFilterForCheckbox(user -> userService.hasPermission(user, DefaultActions.UPDATE)); |
|
| 222 | - mainPanel.insert(filterField, 0); |
|
| 223 | - table.addColumnSortHandler(userColumnListHandler); |
|
| 224 | - table.addColumn(usernameColumn, getStringMessages().username()); |
|
| 225 | - table.addColumn(fullNameColumn, stringMessages.name()); |
|
| 226 | - table.addColumn(emailColumn, stringMessages.email()); |
|
| 227 | - table.addColumn(emailValidatedColumn, stringMessages.validated()); |
|
| 228 | - table.addColumn(companyColumn, stringMessages.company()); |
|
| 229 | - table.addColumn(groupsColumn, stringMessages.groups()); |
|
| 230 | - table.addColumn(rolesColumn, stringMessages.roles()); |
|
| 231 | - table.addColumn(permissionsColumn, stringMessages.permissions()); |
|
| 232 | - table.addColumn(lockedUntilColumn, stringMessages.lockedUntil()); |
|
| 233 | - SecuredDTOOwnerColumn.configureOwnerColumns(table, userColumnListHandler, stringMessages); |
|
| 234 | - table.addColumn(userActionColumn, stringMessages.actions()); |
|
| 235 | - table.ensureDebugId("UsersTable"); |
|
| 236 | 287 | } |
| 237 | 288 | |
| 238 | 289 | public Iterable<UserDTO> getAllUsers() { |
java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/server/SecurityDTOFactory.java
| ... | ... | @@ -96,7 +96,7 @@ public class SecurityDTOFactory { |
| 96 | 96 | /* default tenant filled in later */ null, |
| 97 | 97 | getSecuredPermissions(filteredPermissions, user, securityService), |
| 98 | 98 | createStrippedUserGroupDTOsFromUserGroups(securityService.getUserGroupsOfUser(user), |
| 99 | - fromOriginalToStrippedDownUser, fromOriginalToStrippedDownUserGroup), user.getLockingAndBanning().getLockedUntil()); |
|
| 99 | + fromOriginalToStrippedDownUser, fromOriginalToStrippedDownUserGroup), user.getTimedLock().getLockedUntil()); |
|
| 100 | 100 | userDTO.setDefaultTenantForCurrentServer(createStrippedUserGroupDTOFromUserGroup( |
| 101 | 101 | securityService.getDefaultTenantForCurrentUser(), |
| 102 | 102 | fromOriginalToStrippedDownUserGroup)); |
java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/server/UserManagementServiceImpl.java
| ... | ... | @@ -26,6 +26,7 @@ import com.google.gwt.user.server.rpc.RemoteServiceServlet; |
| 26 | 26 | import com.sap.sse.ServerInfo; |
| 27 | 27 | import com.sap.sse.branding.BrandingConfigurationService; |
| 28 | 28 | import com.sap.sse.branding.shared.BrandingConfiguration; |
| 29 | +import com.sap.sse.common.TimedLock; |
|
| 29 | 30 | import com.sap.sse.common.Util; |
| 30 | 31 | import com.sap.sse.common.Util.Pair; |
| 31 | 32 | import com.sap.sse.common.Util.Triple; |
| ... | ... | @@ -36,6 +37,7 @@ import com.sap.sse.security.interfaces.Credential; |
| 36 | 37 | import com.sap.sse.security.shared.AccessControlListAnnotation; |
| 37 | 38 | import com.sap.sse.security.shared.HasPermissions; |
| 38 | 39 | import com.sap.sse.security.shared.HasPermissions.DefaultActions; |
| 40 | +import com.sap.sse.security.shared.IPAddress; |
|
| 39 | 41 | import com.sap.sse.security.shared.QualifiedObjectIdentifier; |
| 40 | 42 | import com.sap.sse.security.shared.TypeRelativeObjectIdentifier; |
| 41 | 43 | import com.sap.sse.security.shared.UnauthorizedException; |
| ... | ... | @@ -59,6 +61,7 @@ import com.sap.sse.security.shared.impl.UserGroup; |
| 59 | 61 | import com.sap.sse.security.ui.client.SerializationDummy; |
| 60 | 62 | import com.sap.sse.security.ui.client.UserManagementService; |
| 61 | 63 | import com.sap.sse.security.ui.oauth.client.CredentialDTO; |
| 64 | +import com.sap.sse.security.ui.shared.IpToTimedLockDTO; |
|
| 62 | 65 | import com.sap.sse.security.ui.shared.SecurityServiceSharingDTO; |
| 63 | 66 | import com.sap.sse.security.ui.shared.SuccessInfo; |
| 64 | 67 | import com.sap.sse.util.ServiceTrackerFactory; |
| ... | ... | @@ -435,4 +438,25 @@ public class UserManagementServiceImpl extends RemoteServiceServlet implements U |
| 435 | 438 | private BrandingConfigurationService getBrandingConfigurationService() { |
| 436 | 439 | return brandingConfigurationServiceTracker.getService(); |
| 437 | 440 | } |
| 441 | + |
|
| 442 | + @Override |
|
| 443 | + public ArrayList<IpToTimedLockDTO> getClientIPBasedTimedLocksForUserCreation() throws UnauthorizedException { |
|
| 444 | + final HashMap<String, TimedLock> ipToLockMap = getSecurityService().getClientIPBasedTimedLocksForUserCreation(); |
|
| 445 | + return filterIpToTimedLockTableByCurrentUserReadPermission(ipToLockMap); |
|
| 446 | + } |
|
| 447 | + |
|
| 448 | + private ArrayList<IpToTimedLockDTO> filterIpToTimedLockTableByCurrentUserReadPermission( |
|
| 449 | + final HashMap<String, TimedLock> ipToLockMap) { |
|
| 450 | + final SecurityService securityService = getSecurityService(); |
|
| 451 | + return Util.mapToArrayList( |
|
| 452 | + Util.filter(ipToLockMap.entrySet(), |
|
| 453 | + e->securityService.hasCurrentUserReadPermission(new IPAddress(e.getKey()))), |
|
| 454 | + e->new IpToTimedLockDTO(e.getKey(), e.getValue())); |
|
| 455 | + } |
|
| 456 | + |
|
| 457 | + @Override |
|
| 458 | + public ArrayList<IpToTimedLockDTO> getClientIPBasedTimedLocksForBearerTokenAbuse() throws UnauthorizedException { |
|
| 459 | + final HashMap<String, TimedLock> ipToLockMap = getSecurityService().getClientIPBasedTimedLocksForBearerTokenAbuse(); |
|
| 460 | + return filterIpToTimedLockTableByCurrentUserReadPermission(ipToLockMap); |
|
| 461 | + } |
|
| 438 | 462 | } |
java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/server/UserManagementWriteServiceImpl.java
| ... | ... | @@ -23,6 +23,7 @@ import com.sap.sse.common.media.TakedownNoticeRequestContext; |
| 23 | 23 | import com.sap.sse.security.Action; |
| 24 | 24 | import com.sap.sse.security.SecurityService; |
| 25 | 25 | import com.sap.sse.security.shared.HasPermissions.DefaultActions; |
| 26 | +import com.sap.sse.security.shared.IPAddress; |
|
| 26 | 27 | import com.sap.sse.security.shared.PermissionChecker; |
| 27 | 28 | import com.sap.sse.security.shared.QualifiedObjectIdentifier; |
| 28 | 29 | import com.sap.sse.security.shared.RoleDefinition; |
| ... | ... | @@ -374,6 +375,38 @@ public class UserManagementWriteServiceImpl extends UserManagementServiceImpl im |
| 374 | 375 | return new SuccessInfo(false, "Could not delete user.", /* redirectURL */ null, null); |
| 375 | 376 | } |
| 376 | 377 | } |
| 378 | + |
|
| 379 | + @Override |
|
| 380 | + public SuccessInfo unlockUser(String username) throws UnauthorizedException { |
|
| 381 | + User user = getSecurityService().getUserByName(username); |
|
| 382 | + if (user != null) { |
|
| 383 | + if (!getSecurityService().hasCurrentUserExplicitPermissions(user, UserActions.MANAGE_LOCK)) { |
|
| 384 | + logger.info("You are not permitted to manage locking on user " + username); |
|
| 385 | + return new SuccessInfo(false, "You are not permitted to manage locking on user " + username, |
|
| 386 | + /* redirectURL */ null, null, username); |
|
| 387 | + } |
|
| 388 | + try { |
|
| 389 | + getSecurityService().resetUserTimedLock(username); |
|
| 390 | + logger.info("Reset lock on user: " + username + "."); |
|
| 391 | + return new SuccessInfo(true, "Reset lock on user: " + username + ".", /* redirectURL */ null, null, username); |
|
| 392 | + } catch (UserManagementException e) { |
|
| 393 | + logger.info("Could not reset lock on user: " + username + "."); |
|
| 394 | + return new SuccessInfo(false, "Could not reset lock on user " + username, /* redirectURL */ null, null, username); |
|
| 395 | + } |
|
| 396 | + } else { |
|
| 397 | + logger.info("Could not reset lock on user: " + username + "."); |
|
| 398 | + return new SuccessInfo(false, "Could not reset lock on user " + username, /* redirectURL */ null, null, username); |
|
| 399 | + } |
|
| 400 | + } |
|
| 401 | + |
|
| 402 | + @Override |
|
| 403 | + public Set<SuccessInfo> unlockUsers(Set<String> usernames) throws UnauthorizedException { |
|
| 404 | + final Set<SuccessInfo> result = new HashSet<>(); |
|
| 405 | + for (String username : usernames) { |
|
| 406 | + result.add(unlockUser(username)); |
|
| 407 | + } |
|
| 408 | + return result; |
|
| 409 | + } |
|
| 377 | 410 | |
| 378 | 411 | @Override |
| 379 | 412 | public Set<SuccessInfo> deleteUsers(Set<String> usernames) throws UnauthorizedException { |
| ... | ... | @@ -709,4 +742,24 @@ public class UserManagementWriteServiceImpl extends UserManagementServiceImpl im |
| 709 | 742 | public void fileTakedownNotice(TakedownNoticeRequestContext takedownNoticeRequestContext) throws MailException { |
| 710 | 743 | getSecurityService().fileTakedownNotice(takedownNoticeRequestContext); |
| 711 | 744 | } |
| 745 | + |
|
| 746 | + @Override |
|
| 747 | + public void releaseUserCreationLockOnIp(String ip) throws UnauthorizedException { |
|
| 748 | + final SecurityService securityService = getSecurityService(); |
|
| 749 | + final WildcardPermission deletePermission = SecuredSecurityTypes.LOCKED_IP |
|
| 750 | + .getPermissionForObject(DefaultActions.DELETE, new IPAddress(ip)); |
|
| 751 | + // throws exception if not permitted |
|
| 752 | + SecurityUtils.getSubject().checkPermission(deletePermission.toString()); |
|
| 753 | + securityService.releaseUserCreationLockOnIp(ip); |
|
| 754 | + } |
|
| 755 | + |
|
| 756 | + @Override |
|
| 757 | + public void releaseBearerTokenLockOnIp(String ip) throws UnauthorizedException { |
|
| 758 | + final SecurityService securityService = getSecurityService(); |
|
| 759 | + final WildcardPermission deletePermission = SecuredSecurityTypes.LOCKED_IP |
|
| 760 | + .getPermissionForObject(DefaultActions.DELETE, new IPAddress(ip)); |
|
| 761 | + // throws exception if not permitted |
|
| 762 | + SecurityUtils.getSubject().checkPermission(deletePermission.toString()); |
|
| 763 | + securityService.releaseBearerTokenLockOnIp(ip); |
|
| 764 | + } |
|
| 712 | 765 | } |
java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/shared/IpToTimedLockDTO.java
| ... | ... | @@ -0,0 +1,28 @@ |
| 1 | +package com.sap.sse.security.ui.shared; |
|
| 2 | + |
|
| 3 | +import com.sap.sse.common.TimedLock; |
|
| 4 | +import com.sap.sse.common.Named; |
|
| 5 | + |
|
| 6 | +public class IpToTimedLockDTO implements Named { |
|
| 7 | + private static final long serialVersionUID = 7877190394556881643L; |
|
| 8 | + private final String ip; |
|
| 9 | + private final TimedLock timedLock; |
|
| 10 | + |
|
| 11 | + public IpToTimedLockDTO(final String ip, final TimedLock timedLock) { |
|
| 12 | + this.ip = ip; |
|
| 13 | + this.timedLock = timedLock; |
|
| 14 | + } |
|
| 15 | + |
|
| 16 | + @Override |
|
| 17 | + public String getName() { |
|
| 18 | + return "IpToTimedLockDTO"; |
|
| 19 | + } |
|
| 20 | + |
|
| 21 | + public String getIp() { |
|
| 22 | + return ip; |
|
| 23 | + } |
|
| 24 | + |
|
| 25 | + public TimedLock getTimedLock() { |
|
| 26 | + return timedLock; |
|
| 27 | + } |
|
| 28 | +} |
java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/shared/SuccessInfo.java
| ... | ... | @@ -15,6 +15,7 @@ public class SuccessInfo implements IsSerializable { |
| 15 | 15 | private String message; |
| 16 | 16 | private Triple<UserDTO, UserDTO, ServerInfoDTO> userDTO; |
| 17 | 17 | private String redirectURL; |
| 18 | + private String extra; |
|
| 18 | 19 | |
| 19 | 20 | SuccessInfo() {} // for serializtion only |
| 20 | 21 | |
| ... | ... | @@ -25,6 +26,15 @@ public class SuccessInfo implements IsSerializable { |
| 25 | 26 | this.redirectURL = redirectURL; |
| 26 | 27 | this.userDTO = userDTO; |
| 27 | 28 | } |
| 29 | + |
|
| 30 | + public SuccessInfo(boolean successful, String message, String redirectURL, Triple<UserDTO, UserDTO, ServerInfoDTO> userDTO, String extra) { |
|
| 31 | + super(); |
|
| 32 | + this.successful = successful; |
|
| 33 | + this.message = message; |
|
| 34 | + this.redirectURL = redirectURL; |
|
| 35 | + this.userDTO = userDTO; |
|
| 36 | + this.extra = extra; |
|
| 37 | + } |
|
| 28 | 38 | |
| 29 | 39 | public boolean isSuccessful() { |
| 30 | 40 | return successful; |
| ... | ... | @@ -37,9 +47,12 @@ public class SuccessInfo implements IsSerializable { |
| 37 | 47 | public Triple<UserDTO, UserDTO, ServerInfoDTO> getUserDTO() { |
| 38 | 48 | return userDTO; |
| 39 | 49 | } |
| 40 | - |
|
| 50 | + |
|
| 41 | 51 | public String getRedirectURL() { |
| 42 | 52 | return redirectURL; |
| 43 | 53 | } |
| 44 | - |
|
| 54 | + |
|
| 55 | + public String getExtra() { |
|
| 56 | + return extra; |
|
| 57 | + } |
|
| 45 | 58 | } |
java/com.sap.sse.security.ui/src/main/resources/com/sap/sse/security/SSESecuritySerializers.gwt.xml
| ... | ... | @@ -1,5 +1,6 @@ |
| 1 | 1 | <?xml version="1.0" encoding="UTF-8"?> |
| 2 | 2 | <!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 2.7.0//EN" "http://gwtproject.org/doctype/2.7.0/gwt-module.dtd"> |
| 3 | 3 | <module> |
| 4 | + <inherits name="com.sap.sse.SSECommon" /> |
|
| 4 | 5 | <source path='shared'/> |
| 5 | 6 | </module> |
java/com.sap.sse.security.ui/src/main/resources/com/sap/sse/security/ui/Login.gwt.xml
| ... | ... | @@ -7,6 +7,8 @@ |
| 7 | 7 | <inherits name="com.sap.sse.security.ui.SecurityLocalesAllPermutations" /> |
| 8 | 8 | <inherits name="com.sap.sse.security.ui.LoginPanel"/> |
| 9 | 9 | <inherits name="com.sap.sse.landscape.aws.common.SSELandscapeAWSCommon" /> |
| 10 | + <inherits name="com.sap.sse.gwt.SSESharedGWT" /> |
|
| 11 | + <inherits name="com.sap.sse.branding.SSEBranding" /> |
|
| 10 | 12 | |
| 11 | 13 | <!-- Specify the app entry point class. --> |
| 12 | 14 | <entry-point class='com.sap.sse.security.ui.login.LoginEntryPoint'/> |
java/com.sap.sse.security.ui/src/main/resources/com/sap/sse/security/ui/Register.gwt.xml
| ... | ... | @@ -11,6 +11,8 @@ |
| 11 | 11 | <inherits name="com.sap.sse.Security" /> |
| 12 | 12 | <inherits name="com.sap.sse.security.ui.SecurityLocalesAllPermutations" /> |
| 13 | 13 | <inherits name="com.sap.sse.security.SSESecuritySerializers" /> |
| 14 | + <inherits name="com.sap.sse.gwt.SSESharedGWT" /> |
|
| 15 | + <inherits name="com.sap.sse.branding.SSEBranding" /> |
|
| 14 | 16 | <inherits name="com.sap.sse.landscape.aws.common.SSELandscapeAWSCommon" /> |
| 15 | 17 | |
| 16 | 18 | <super-source path="jre" /> |
java/com.sap.sse.security.ui/src/main/resources/shiro.ini
| ... | ... | @@ -24,7 +24,7 @@ securityManager.sessionManager.sessionDAO = $sessionDAO |
| 24 | 24 | securityManager.sessionManager.globalSessionTimeout = 31536000000 |
| 25 | 25 | cacheManager = com.sap.sse.security.SessionCacheManager |
| 26 | 26 | securityManager.cacheManager = $cacheManager |
| 27 | -authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithLockingAndBanning |
|
| 27 | +authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithTimedLocks |
|
| 28 | 28 | securityManager.authenticator.authenticationStrategy = $authenticationStrategy |
| 29 | 29 | |
| 30 | 30 | subjectDAO = com.sap.sse.security.NoSessionStorageForUnauthenticatedSessionsSessionDAO |
java/com.sap.sse.security.userstore.mongodb/src/com/sap/sse/security/userstore/mongodb/UserStoreImpl.java
| ... | ... | @@ -14,6 +14,7 @@ import java.util.concurrent.ConcurrentHashMap; |
| 14 | 14 | import java.util.logging.Level; |
| 15 | 15 | import java.util.logging.Logger; |
| 16 | 16 | |
| 17 | +import com.sap.sse.common.TimedLock; |
|
| 17 | 18 | import com.sap.sse.common.Util; |
| 18 | 19 | import com.sap.sse.common.Util.Pair; |
| 19 | 20 | import com.sap.sse.concurrent.LockUtil; |
| ... | ... | @@ -34,7 +35,6 @@ import com.sap.sse.security.shared.UserManagementException; |
| 34 | 35 | import com.sap.sse.security.shared.UserRole; |
| 35 | 36 | import com.sap.sse.security.shared.UserStoreManagementException; |
| 36 | 37 | import com.sap.sse.security.shared.WildcardPermission; |
| 37 | -import com.sap.sse.security.shared.impl.LockingAndBanning; |
|
| 38 | 38 | import com.sap.sse.security.shared.impl.Ownership; |
| 39 | 39 | import com.sap.sse.security.shared.impl.Role; |
| 40 | 40 | import com.sap.sse.security.shared.impl.User; |
| ... | ... | @@ -854,12 +854,12 @@ public class UserStoreImpl implements UserStore { |
| 854 | 854 | } |
| 855 | 855 | |
| 856 | 856 | @Override |
| 857 | - public User createUser(String name, String email, LockingAndBanning lockingAndBanning, Account... accounts) |
|
| 857 | + public User createUser(String name, String email, TimedLock timedLock, Account... accounts) |
|
| 858 | 858 | throws UserManagementException { |
| 859 | 859 | return LockUtil.executeWithWriteLockAndResultExpectException(usersLock, () -> { |
| 860 | 860 | checkUsernameUniqueness(name); |
| 861 | 861 | final Map<String, UserGroup> tenantsForServer = new ConcurrentHashMap<>(); |
| 862 | - final User user = new UserImpl(name, email, tenantsForServer, /* user group provider */ this, lockingAndBanning, accounts); |
|
| 862 | + final User user = new UserImpl(name, email, tenantsForServer, /* user group provider */ this, timedLock, accounts); |
|
| 863 | 863 | logger.info("Creating user: " + user + " with e-mail " + email); |
| 864 | 864 | addAndStoreUserInternal(user); |
| 865 | 865 | return user; |
java/com.sap.sse.security.userstore.mongodb/src/com/sap/sse/security/userstore/mongodb/impl/DomainObjectFactoryImpl.java
| ... | ... | @@ -20,7 +20,9 @@ import com.mongodb.client.MongoCollection; |
| 20 | 20 | import com.mongodb.client.MongoDatabase; |
| 21 | 21 | import com.sap.sse.common.Duration; |
| 22 | 22 | import com.sap.sse.common.TimePoint; |
| 23 | +import com.sap.sse.common.TimedLock; |
|
| 23 | 24 | import com.sap.sse.common.Util; |
| 25 | +import com.sap.sse.common.impl.TimedLockImpl; |
|
| 24 | 26 | import com.sap.sse.security.interfaces.Social; |
| 25 | 27 | import com.sap.sse.security.interfaces.UserImpl; |
| 26 | 28 | import com.sap.sse.security.interfaces.UserStore; |
| ... | ... | @@ -37,8 +39,6 @@ import com.sap.sse.security.shared.UserManagementException; |
| 37 | 39 | import com.sap.sse.security.shared.UsernamePasswordAccount; |
| 38 | 40 | import com.sap.sse.security.shared.WildcardPermission; |
| 39 | 41 | import com.sap.sse.security.shared.impl.AccessControlList; |
| 40 | -import com.sap.sse.security.shared.impl.LockingAndBanning; |
|
| 41 | -import com.sap.sse.security.shared.impl.LockingAndBanningImpl; |
|
| 42 | 42 | import com.sap.sse.security.shared.impl.Ownership; |
| 43 | 43 | import com.sap.sse.security.shared.impl.QualifiedObjectIdentifierImpl; |
| 44 | 44 | import com.sap.sse.security.shared.impl.Role; |
| ... | ... | @@ -289,9 +289,9 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { |
| 289 | 289 | final String validationSecret = (String) userDBObject.get(FieldNames.User.VALIDATION_SECRET.name()); |
| 290 | 290 | final Long lockedUntilMillis = userDBObject.getLong(FieldNames.User.LOCKED_UNTIL_MILLIS.name()); |
| 291 | 291 | final Long nextLockingDurationMillis = userDBObject.getLong(FieldNames.User.NEXT_LOCKING_DURATION_MILLIS.name()); |
| 292 | - final LockingAndBanning lockingAndBanning = new LockingAndBanningImpl( |
|
| 292 | + final TimedLock timedLock = new TimedLockImpl( |
|
| 293 | 293 | lockedUntilMillis == null ? TimePoint.BeginningOfTime : TimePoint.of(lockedUntilMillis), |
| 294 | - nextLockingDurationMillis == null ? LockingAndBanningImpl.DEFAULT_INITIAL_LOCKING_DELAY : Duration.ofMillis(nextLockingDurationMillis)); |
|
| 294 | + nextLockingDurationMillis == null ? TimedLockImpl.DEFAULT_INITIAL_LOCKING_DELAY : Duration.ofMillis(nextLockingDurationMillis)); |
|
| 295 | 295 | final Set<Role> roles = new HashSet<>(); |
| 296 | 296 | final Set<String> permissions = new HashSet<>(); |
| 297 | 297 | final List<?> rolesO = (List<?>) userDBObject.get(FieldNames.User.ROLE_IDS.name()); |
| ... | ... | @@ -358,7 +358,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { |
| 358 | 358 | Map<AccountType, Account> accounts = createAccountMapFromdDBObject(accountsMap); |
| 359 | 359 | User result = new UserImpl(username, email, fullName, company, locale, |
| 360 | 360 | emailValidated == null ? false : emailValidated, passwordResetSecret, validationSecret, defaultTenant, |
| 361 | - accounts.values(), userGroupProvider, lockingAndBanning); |
|
| 361 | + accounts.values(), userGroupProvider, timedLock); |
|
| 362 | 362 | for (final Role role : roles) { |
| 363 | 363 | result.addRole(role); |
| 364 | 364 | } |
java/com.sap.sse.security.userstore.mongodb/src/com/sap/sse/security/userstore/mongodb/impl/MongoObjectFactoryImpl.java
| ... | ... | @@ -16,6 +16,7 @@ import com.mongodb.client.MongoCollection; |
| 16 | 16 | import com.mongodb.client.MongoDatabase; |
| 17 | 17 | import com.mongodb.client.model.IndexOptions; |
| 18 | 18 | import com.mongodb.client.model.ReplaceOptions; |
| 19 | +import com.sap.sse.common.impl.TimedLockImpl; |
|
| 19 | 20 | import com.sap.sse.security.interfaces.Social; |
| 20 | 21 | import com.sap.sse.security.shared.AccessControlListAnnotation; |
| 21 | 22 | import com.sap.sse.security.shared.Account; |
| ... | ... | @@ -27,7 +28,6 @@ import com.sap.sse.security.shared.SocialUserAccount; |
| 27 | 28 | import com.sap.sse.security.shared.UsernamePasswordAccount; |
| 28 | 29 | import com.sap.sse.security.shared.WildcardPermission; |
| 29 | 30 | import com.sap.sse.security.shared.impl.AccessControlList; |
| 30 | -import com.sap.sse.security.shared.impl.LockingAndBanningImpl; |
|
| 31 | 31 | import com.sap.sse.security.shared.impl.Ownership; |
| 32 | 32 | import com.sap.sse.security.shared.impl.Role; |
| 33 | 33 | import com.sap.sse.security.shared.impl.User; |
| ... | ... | @@ -216,13 +216,13 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory { |
| 216 | 216 | dbUser.put(FieldNames.User.PASSWORD_RESET_SECRET.name(), user.getPasswordResetSecret()); |
| 217 | 217 | dbUser.put(FieldNames.User.VALIDATION_SECRET.name(), user.getValidationSecret()); |
| 218 | 218 | dbUser.put(FieldNames.User.ACCOUNTS.name(), createAccountMapObject(user.getAllAccounts())); |
| 219 | - if (user.getLockingAndBanning() instanceof LockingAndBanningImpl) { |
|
| 220 | - final LockingAndBanningImpl lockingAndBanning = ((LockingAndBanningImpl) user.getLockingAndBanning()); |
|
| 221 | - dbUser.put(FieldNames.User.LOCKED_UNTIL_MILLIS.name(), lockingAndBanning.getLockedUntil().asMillis()); |
|
| 222 | - dbUser.put(FieldNames.User.NEXT_LOCKING_DURATION_MILLIS.name(), lockingAndBanning.getNextLockingDelay().asMillis()); |
|
| 219 | + if (user.getTimedLock() instanceof TimedLockImpl) { |
|
| 220 | + final TimedLockImpl timedLock = ((TimedLockImpl) user.getTimedLock()); |
|
| 221 | + dbUser.put(FieldNames.User.LOCKED_UNTIL_MILLIS.name(), timedLock.getLockedUntil().asMillis()); |
|
| 222 | + dbUser.put(FieldNames.User.NEXT_LOCKING_DURATION_MILLIS.name(), timedLock.getNextLockingDelay().asMillis()); |
|
| 223 | 223 | } else { |
| 224 | - logger.warning("Expected user locking/banning to be of type "+LockingAndBanningImpl.class.getSimpleName() |
|
| 225 | - +" but was of type "+user.getLockingAndBanning().getClass().getSimpleName()+"; not storing to DB"); |
|
| 224 | + logger.warning("Expected user locking/banning to be of type "+TimedLockImpl.class.getSimpleName() |
|
| 225 | + +" but was of type "+user.getTimedLock().getClass().getSimpleName()+"; not storing to DB"); |
|
| 226 | 226 | } |
| 227 | 227 | BasicDBList dbRoles = new BasicDBList(); |
| 228 | 228 | for (Role role : user.getRoles()) { |
java/com.sap.sse.security.userstore.mongodb/src/com/sap/sse/security/userstore/mongodb/impl/UserProxy.java
| ... | ... | @@ -4,13 +4,13 @@ import java.io.Serializable; |
| 4 | 4 | import java.util.Locale; |
| 5 | 5 | import java.util.Map; |
| 6 | 6 | |
| 7 | +import com.sap.sse.common.TimedLock; |
|
| 7 | 8 | import com.sap.sse.security.shared.Account; |
| 8 | 9 | import com.sap.sse.security.shared.Account.AccountType; |
| 9 | 10 | import com.sap.sse.security.shared.HasPermissions; |
| 10 | 11 | import com.sap.sse.security.shared.QualifiedObjectIdentifier; |
| 11 | 12 | import com.sap.sse.security.shared.UserGroupProvider; |
| 12 | 13 | import com.sap.sse.security.shared.WildcardPermission; |
| 13 | -import com.sap.sse.security.shared.impl.LockingAndBanning; |
|
| 14 | 14 | import com.sap.sse.security.shared.impl.Role; |
| 15 | 15 | import com.sap.sse.security.shared.impl.User; |
| 16 | 16 | import com.sap.sse.security.shared.impl.UserGroup; |
| ... | ... | @@ -242,7 +242,7 @@ public class UserProxy implements User { |
| 242 | 242 | } |
| 243 | 243 | |
| 244 | 244 | @Override |
| 245 | - public LockingAndBanning getLockingAndBanning() { |
|
| 245 | + public TimedLock getTimedLock() { |
|
| 246 | 246 | throw new UnsupportedOperationException(); |
| 247 | 247 | } |
| 248 | 248 | } |
java/com.sap.sse.security/resources/shiro.ini
| ... | ... | @@ -26,7 +26,7 @@ securityManager.subjectDAO = $subjectDAO |
| 26 | 26 | securityManager.sessionManager.globalSessionTimeout = 31536000000 |
| 27 | 27 | cacheManager = com.sap.sse.security.SessionCacheManager |
| 28 | 28 | securityManager.cacheManager = $cacheManager |
| 29 | -authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithLockingAndBanning |
|
| 29 | +authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithTimedLocks |
|
| 30 | 30 | securityManager.authenticator.authenticationStrategy = $authenticationStrategy |
| 31 | 31 | |
| 32 | 32 | # Support for anonymous user permissions |
java/com.sap.sse.security/src/com/sap/sse/security/AtLeastOneSuccessfulStrategyWithLockingAndBanning.java
| ... | ... | @@ -1,103 +0,0 @@ |
| 1 | -package com.sap.sse.security; |
|
| 2 | - |
|
| 3 | -import java.util.concurrent.ExecutionException; |
|
| 4 | -import java.util.concurrent.Future; |
|
| 5 | -import java.util.logging.Level; |
|
| 6 | -import java.util.logging.Logger; |
|
| 7 | - |
|
| 8 | -import org.apache.shiro.authc.AuthenticationException; |
|
| 9 | -import org.apache.shiro.authc.AuthenticationInfo; |
|
| 10 | -import org.apache.shiro.authc.AuthenticationToken; |
|
| 11 | -import org.apache.shiro.authc.IncorrectCredentialsException; |
|
| 12 | -import org.apache.shiro.authc.LockedAccountException; |
|
| 13 | -import org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy; |
|
| 14 | -import org.apache.shiro.realm.Realm; |
|
| 15 | - |
|
| 16 | -import com.sap.sse.security.impl.Activator; |
|
| 17 | -import com.sap.sse.security.shared.impl.LockingAndBanning; |
|
| 18 | -import com.sap.sse.security.shared.impl.User; |
|
| 19 | -import com.sap.sse.util.ServiceTrackerFactory; |
|
| 20 | - |
|
| 21 | -public class AtLeastOneSuccessfulStrategyWithLockingAndBanning extends AtLeastOneSuccessfulStrategy { |
|
| 22 | - private static final Logger logger = Logger.getLogger(AtLeastOneSuccessfulStrategyWithLockingAndBanning.class.getName()); |
|
| 23 | - |
|
| 24 | - private final Future<SecurityService> securityService; |
|
| 25 | - |
|
| 26 | - public AtLeastOneSuccessfulStrategyWithLockingAndBanning() { |
|
| 27 | - if (Activator.getContext() != null) { |
|
| 28 | - securityService = ServiceTrackerFactory.createServiceFuture(Activator.getContext(), SecurityService.class); |
|
| 29 | - } else { |
|
| 30 | - securityService = null; |
|
| 31 | - } |
|
| 32 | - } |
|
| 33 | - |
|
| 34 | - private SecurityService getSecurityService() { |
|
| 35 | - SecurityService result; |
|
| 36 | - try { |
|
| 37 | - result = securityService == null ? null : securityService.get(); |
|
| 38 | - } catch (InterruptedException | ExecutionException e) { |
|
| 39 | - logger.log(Level.SEVERE, "Error retrieving security service", e); |
|
| 40 | - result = null; |
|
| 41 | - } |
|
| 42 | - return result; |
|
| 43 | - } |
|
| 44 | - |
|
| 45 | - @Override |
|
| 46 | - public AuthenticationInfo afterAttempt(Realm realm, AuthenticationToken token, AuthenticationInfo singleRealmInfo, |
|
| 47 | - AuthenticationInfo aggregateInfo, Throwable t) throws AuthenticationException { |
|
| 48 | - if (token != null && token.getPrincipal() != null && realm instanceof UsernamePasswordRealm) { |
|
| 49 | - final UsernamePasswordRealm upRealm = (UsernamePasswordRealm) realm; |
|
| 50 | - final String username = token.getPrincipal().toString(); |
|
| 51 | - final User user = upRealm.getUserStore().getUserByName(username); |
|
| 52 | - if (user != null) { |
|
| 53 | - if (t != null) { |
|
| 54 | - if (t instanceof IncorrectCredentialsException) { |
|
| 55 | - logger.info("failed password authentication for user "+username); |
|
| 56 | - final SecurityService mySecurityService = getSecurityService(); |
|
| 57 | - if (mySecurityService != null) { |
|
| 58 | - final LockingAndBanning lockingAndBanning = mySecurityService.failedPasswordAuthentication(user); |
|
| 59 | - if (lockingAndBanning != null) { |
|
| 60 | - logger.info("User "+username+" locked for password authentication: "+lockingAndBanning); |
|
| 61 | - } |
|
| 62 | - } else { |
|
| 63 | - logger.warning("Account locking due to failed password authentication for user "+username+" not possible; security service not found"); |
|
| 64 | - } |
|
| 65 | - } |
|
| 66 | - } else { |
|
| 67 | - // no exception, so the authentication must have been successful |
|
| 68 | - final SecurityService mySecurityService = getSecurityService(); |
|
| 69 | - if (mySecurityService != null) { |
|
| 70 | - mySecurityService.successfulPasswordAuthentication(user); |
|
| 71 | - } |
|
| 72 | - } |
|
| 73 | - } |
|
| 74 | - } else if (token != null && realm instanceof BearerTokenRealm) { |
|
| 75 | - final BearerAuthenticationToken bearerToken = (BearerAuthenticationToken) token; |
|
| 76 | - if (singleRealmInfo == null || singleRealmInfo.getPrincipals().isEmpty()) { |
|
| 77 | - if (t != null && t instanceof LockedAccountException) { |
|
| 78 | - logger.fine(()->"Bearer token authentication from client IP "+bearerToken.getClientIP()+" with user agent "+bearerToken.getUserAgent()+" currently locked"); |
|
| 79 | - } else { |
|
| 80 | - // authentication failed |
|
| 81 | - logger.info("failed bearer token authentication for client IP "+bearerToken.getClientIP()+" with user agent "+bearerToken.getUserAgent()); |
|
| 82 | - final SecurityService mySecurityService = getSecurityService(); |
|
| 83 | - if (mySecurityService != null) { |
|
| 84 | - final LockingAndBanning lockingAndBanning = mySecurityService.failedBearerTokenAuthentication(bearerToken.getClientIP()); |
|
| 85 | - if (lockingAndBanning != null) { |
|
| 86 | - logger.info("Client IP "+bearerToken.getClientIP()+" locked for bearer token authentication: "+lockingAndBanning); |
|
| 87 | - } |
|
| 88 | - } else { |
|
| 89 | - logger.warning("Client IP locking due to failed bearer token authentication for client IP " |
|
| 90 | - +bearerToken.getClientIP()+" with user agent "+bearerToken.getUserAgent() |
|
| 91 | - +" not possible; security service not found"); |
|
| 92 | - } |
|
| 93 | - } |
|
| 94 | - } else { // valid authentication info means authentication was successful |
|
| 95 | - final SecurityService mySecurityService = getSecurityService(); |
|
| 96 | - if (mySecurityService != null) { |
|
| 97 | - mySecurityService.successfulBearerTokenAuthentication(bearerToken.getClientIP()); |
|
| 98 | - } |
|
| 99 | - } |
|
| 100 | - } |
|
| 101 | - return super.afterAttempt(realm, token, singleRealmInfo, aggregateInfo, t); |
|
| 102 | - } |
|
| 103 | -} |
java/com.sap.sse.security/src/com/sap/sse/security/AtLeastOneSuccessfulStrategyWithTimedLocks.java
| ... | ... | @@ -0,0 +1,103 @@ |
| 1 | +package com.sap.sse.security; |
|
| 2 | + |
|
| 3 | +import java.util.concurrent.ExecutionException; |
|
| 4 | +import java.util.concurrent.Future; |
|
| 5 | +import java.util.logging.Level; |
|
| 6 | +import java.util.logging.Logger; |
|
| 7 | + |
|
| 8 | +import org.apache.shiro.authc.AuthenticationException; |
|
| 9 | +import org.apache.shiro.authc.AuthenticationInfo; |
|
| 10 | +import org.apache.shiro.authc.AuthenticationToken; |
|
| 11 | +import org.apache.shiro.authc.IncorrectCredentialsException; |
|
| 12 | +import org.apache.shiro.authc.LockedAccountException; |
|
| 13 | +import org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy; |
|
| 14 | +import org.apache.shiro.realm.Realm; |
|
| 15 | + |
|
| 16 | +import com.sap.sse.common.TimedLock; |
|
| 17 | +import com.sap.sse.security.impl.Activator; |
|
| 18 | +import com.sap.sse.security.shared.impl.User; |
|
| 19 | +import com.sap.sse.util.ServiceTrackerFactory; |
|
| 20 | + |
|
| 21 | +public class AtLeastOneSuccessfulStrategyWithTimedLocks extends AtLeastOneSuccessfulStrategy { |
|
| 22 | + private static final Logger logger = Logger.getLogger(AtLeastOneSuccessfulStrategyWithTimedLocks.class.getName()); |
|
| 23 | + |
|
| 24 | + private final Future<SecurityService> securityService; |
|
| 25 | + |
|
| 26 | + public AtLeastOneSuccessfulStrategyWithTimedLocks() { |
|
| 27 | + if (Activator.getContext() != null) { |
|
| 28 | + securityService = ServiceTrackerFactory.createServiceFuture(Activator.getContext(), SecurityService.class); |
|
| 29 | + } else { |
|
| 30 | + securityService = null; |
|
| 31 | + } |
|
| 32 | + } |
|
| 33 | + |
|
| 34 | + private SecurityService getSecurityService() { |
|
| 35 | + SecurityService result; |
|
| 36 | + try { |
|
| 37 | + result = securityService == null ? null : securityService.get(); |
|
| 38 | + } catch (InterruptedException | ExecutionException e) { |
|
| 39 | + logger.log(Level.SEVERE, "Error retrieving security service", e); |
|
| 40 | + result = null; |
|
| 41 | + } |
|
| 42 | + return result; |
|
| 43 | + } |
|
| 44 | + |
|
| 45 | + @Override |
|
| 46 | + public AuthenticationInfo afterAttempt(Realm realm, AuthenticationToken token, AuthenticationInfo singleRealmInfo, |
|
| 47 | + AuthenticationInfo aggregateInfo, Throwable t) throws AuthenticationException { |
|
| 48 | + if (token != null && token.getPrincipal() != null && realm instanceof UsernamePasswordRealm) { |
|
| 49 | + final UsernamePasswordRealm upRealm = (UsernamePasswordRealm) realm; |
|
| 50 | + final String username = token.getPrincipal().toString(); |
|
| 51 | + final User user = upRealm.getUserStore().getUserByName(username); |
|
| 52 | + if (user != null) { |
|
| 53 | + if (t != null) { |
|
| 54 | + if (t instanceof IncorrectCredentialsException) { |
|
| 55 | + logger.info("failed password authentication for user "+username); |
|
| 56 | + final SecurityService mySecurityService = getSecurityService(); |
|
| 57 | + if (mySecurityService != null) { |
|
| 58 | + final TimedLock timedLock = mySecurityService.failedPasswordAuthentication(user); |
|
| 59 | + if (timedLock != null) { |
|
| 60 | + logger.info("User "+username+" locked for password authentication: "+timedLock); |
|
| 61 | + } |
|
| 62 | + } else { |
|
| 63 | + logger.warning("Account locking due to failed password authentication for user "+username+" not possible; security service not found"); |
|
| 64 | + } |
|
| 65 | + } |
|
| 66 | + } else { |
|
| 67 | + // no exception, so the authentication must have been successful |
|
| 68 | + final SecurityService mySecurityService = getSecurityService(); |
|
| 69 | + if (mySecurityService != null) { |
|
| 70 | + mySecurityService.successfulPasswordAuthentication(user); |
|
| 71 | + } |
|
| 72 | + } |
|
| 73 | + } |
|
| 74 | + } else if (token != null && realm instanceof BearerTokenRealm) { |
|
| 75 | + final BearerAuthenticationToken bearerToken = (BearerAuthenticationToken) token; |
|
| 76 | + if (singleRealmInfo == null || singleRealmInfo.getPrincipals().isEmpty()) { |
|
| 77 | + if (t != null && t instanceof LockedAccountException) { |
|
| 78 | + logger.fine(()->"Bearer token authentication from client IP "+bearerToken.getClientIP()+" with user agent "+bearerToken.getUserAgent()+" currently locked"); |
|
| 79 | + } else { |
|
| 80 | + // authentication failed |
|
| 81 | + logger.info("failed bearer token authentication for client IP "+bearerToken.getClientIP()+" with user agent "+bearerToken.getUserAgent()); |
|
| 82 | + final SecurityService mySecurityService = getSecurityService(); |
|
| 83 | + if (mySecurityService != null) { |
|
| 84 | + final TimedLock timedLock = mySecurityService.failedBearerTokenAuthentication(bearerToken.getClientIP()); |
|
| 85 | + if (timedLock != null) { |
|
| 86 | + logger.info("Client IP "+bearerToken.getClientIP()+" locked for bearer token authentication: "+timedLock); |
|
| 87 | + } |
|
| 88 | + } else { |
|
| 89 | + logger.warning("Client IP locking due to failed bearer token authentication for client IP " |
|
| 90 | + +bearerToken.getClientIP()+" with user agent "+bearerToken.getUserAgent() |
|
| 91 | + +" not possible; security service not found"); |
|
| 92 | + } |
|
| 93 | + } |
|
| 94 | + } else { // valid authentication info means authentication was successful |
|
| 95 | + final SecurityService mySecurityService = getSecurityService(); |
|
| 96 | + if (mySecurityService != null) { |
|
| 97 | + mySecurityService.successfulBearerTokenAuthentication(bearerToken.getClientIP()); |
|
| 98 | + } |
|
| 99 | + } |
|
| 100 | + } |
|
| 101 | + return super.afterAttempt(realm, token, singleRealmInfo, aggregateInfo, t); |
|
| 102 | + } |
|
| 103 | +} |
java/com.sap.sse.security/src/com/sap/sse/security/OAuthRealm.java
| ... | ... | @@ -33,6 +33,7 @@ import org.scribe.model.Verb; |
| 33 | 33 | import org.scribe.model.Verifier; |
| 34 | 34 | import org.scribe.oauth.OAuthService; |
| 35 | 35 | |
| 36 | +import com.sap.sse.common.impl.TimedLockImpl; |
|
| 36 | 37 | import com.sap.sse.security.interfaces.Credential; |
| 37 | 38 | import com.sap.sse.security.interfaces.OAuthToken; |
| 38 | 39 | import com.sap.sse.security.interfaces.Social; |
| ... | ... | @@ -40,7 +41,6 @@ import com.sap.sse.security.interfaces.SocialSettingsKeys; |
| 40 | 41 | import com.sap.sse.security.shared.SocialUserAccount; |
| 41 | 42 | import com.sap.sse.security.shared.UserGroupManagementException; |
| 42 | 43 | import com.sap.sse.security.shared.UserManagementException; |
| 43 | -import com.sap.sse.security.shared.impl.LockingAndBanningImpl; |
|
| 44 | 44 | import com.sap.sse.security.shared.impl.User; |
| 45 | 45 | import com.sap.sse.security.shared.impl.UserGroup; |
| 46 | 46 | |
| ... | ... | @@ -189,7 +189,7 @@ public class OAuthRealm extends AbstractCompositeAuthorizingRealm { |
| 189 | 189 | try { |
| 190 | 190 | UserGroup tenant = getUserStore().createUserGroup(UUID.randomUUID(), socialname + SecurityService.TENANT_SUFFIX); |
| 191 | 191 | getAccessControlStore().setOwnership(tenant.getIdentifier(), user, tenant, tenant.getName()); |
| 192 | - user = getUserStore().createUser(socialname, socialUser.getProperty(Social.EMAIL.name()), new LockingAndBanningImpl(), socialUser); |
|
| 192 | + user = getUserStore().createUser(socialname, socialUser.getProperty(Social.EMAIL.name()), new TimedLockImpl(), socialUser); |
|
| 193 | 193 | tenant.add(user); |
| 194 | 194 | getUserStore().updateUserGroup(tenant); |
| 195 | 195 | } catch (UserManagementException | UserGroupManagementException e) { |
java/com.sap.sse.security/src/com/sap/sse/security/SecurityService.java
| ... | ... | @@ -2,6 +2,7 @@ package com.sap.sse.security; |
| 2 | 2 | |
| 3 | 3 | import java.io.Serializable; |
| 4 | 4 | import java.math.BigDecimal; |
| 5 | +import java.util.HashMap; |
|
| 5 | 6 | import java.util.List; |
| 6 | 7 | import java.util.Locale; |
| 7 | 8 | import java.util.Map; |
| ... | ... | @@ -22,6 +23,7 @@ import org.apache.shiro.subject.Subject; |
| 22 | 23 | import org.osgi.framework.BundleContext; |
| 23 | 24 | |
| 24 | 25 | import com.sap.sse.common.Duration; |
| 26 | +import com.sap.sse.common.TimedLock; |
|
| 25 | 27 | import com.sap.sse.common.Util; |
| 26 | 28 | import com.sap.sse.common.Util.Pair; |
| 27 | 29 | import com.sap.sse.common.http.HttpHeaderUtil; |
| ... | ... | @@ -53,7 +55,6 @@ import com.sap.sse.security.shared.UserManagementException; |
| 53 | 55 | import com.sap.sse.security.shared.WildcardPermission; |
| 54 | 56 | import com.sap.sse.security.shared.WithQualifiedObjectIdentifier; |
| 55 | 57 | import com.sap.sse.security.shared.impl.AccessControlList; |
| 56 | -import com.sap.sse.security.shared.impl.LockingAndBanning; |
|
| 57 | 58 | import com.sap.sse.security.shared.impl.Ownership; |
| 58 | 59 | import com.sap.sse.security.shared.impl.Role; |
| 59 | 60 | import com.sap.sse.security.shared.impl.SecuredSecurityTypes; |
| ... | ... | @@ -178,6 +179,16 @@ public interface SecurityService extends ReplicableWithObjectInputStream<Replica |
| 178 | 179 | |
| 179 | 180 | void deleteUserGroup(UserGroup userGroup) throws UserGroupManagementException; |
| 180 | 181 | |
| 182 | + /** |
|
| 183 | + * Releases the lock applied on IP due to user creation abuse. |
|
| 184 | + */ |
|
| 185 | + void releaseUserCreationLockOnIp(String ip); |
|
| 186 | + |
|
| 187 | + /** |
|
| 188 | + * Releases the lock applied on IP due to user creation abuse. |
|
| 189 | + */ |
|
| 190 | + void releaseBearerTokenLockOnIp(String ip); |
|
| 191 | + |
|
| 181 | 192 | Iterable<User> getUserList(); |
| 182 | 193 | |
| 183 | 194 | User getUserByName(String username); |
| ... | ... | @@ -221,6 +232,8 @@ public interface SecurityService extends ReplicableWithObjectInputStream<Replica |
| 221 | 232 | void updateSimpleUserEmail(String username, String newEmail, String validationBaseURL) throws UserManagementException; |
| 222 | 233 | |
| 223 | 234 | void updateUserProperties(String username, String fullName, String company, Locale locale) throws UserManagementException; |
| 235 | + |
|
| 236 | + void resetUserTimedLock(String username) throws UserManagementException; |
|
| 224 | 237 | |
| 225 | 238 | void deleteUser(String username) throws UserManagementException; |
| 226 | 239 | |
| ... | ... | @@ -873,7 +886,7 @@ public interface SecurityService extends ReplicableWithObjectInputStream<Replica |
| 873 | 886 | |
| 874 | 887 | Pair<Boolean, Set<String>> getCORSFilterConfiguration(String serverName); |
| 875 | 888 | |
| 876 | - LockingAndBanning failedPasswordAuthentication(User user); |
|
| 889 | + TimedLock failedPasswordAuthentication(User user); |
|
| 877 | 890 | |
| 878 | 891 | void successfulPasswordAuthentication(User user); |
| 879 | 892 | |
| ... | ... | @@ -890,7 +903,7 @@ public interface SecurityService extends ReplicableWithObjectInputStream<Replica |
| 890 | 903 | * {@code userAgent}, the locking record including its last locking duration is expunged from the internal data |
| 891 | 904 | * structures, avoiding garbage piling up. |
| 892 | 905 | */ |
| 893 | - LockingAndBanning failedBearerTokenAuthentication(String clientIP); |
|
| 906 | + TimedLock failedBearerTokenAuthentication(String clientIP); |
|
| 894 | 907 | |
| 895 | 908 | /** |
| 896 | 909 | * Call this when the combination of {@code clientIP} and {@code userAgent} was not |
| ... | ... | @@ -905,12 +918,15 @@ public interface SecurityService extends ReplicableWithObjectInputStream<Replica |
| 905 | 918 | * Used in conjunction with {@link #failedBearerTokenAuthentication(String)} and |
| 906 | 919 | * {@link #successfulBearerTokenAuthentication(String)}. If and only if a locking state for the combination |
| 907 | 920 | * of {@code clientIP} and {@code userAgent} is known and still locked, {@code true} is returned. Unlocking |
| 908 | - * will bappen by calling {@link #successfulBearerTokenAuthentication(String)} with an equal combination |
|
| 909 | - * of {@code clientIP} and {@code userAgent}. Invoking {@link #failedBearerTokenAuthentication(String)} |
|
| 910 | - * will establish (if not yet locked) or extend the locking duration for the combination. |
|
| 921 | + * will happen by calling {@link #successfulBearerTokenAuthentication(String)} with an equal combination of |
|
| 922 | + * {@code clientIP} and {@code userAgent} or by calling {@link releaseBearerTokenLockOnIp(String)}. Invoking |
|
| 923 | + * {@link #failedBearerTokenAuthentication(String)} will establish (if not yet locked) or extend the locking |
|
| 924 | + * duration for the combination. |
|
| 911 | 925 | */ |
| 912 | 926 | boolean isClientIPLockedForBearerTokenAuthentication(String clientIP); |
| 913 | 927 | |
| 928 | + boolean isUserCreationLockedForClientIP(String clientIP); |
|
| 929 | + |
|
| 914 | 930 | void fileTakedownNotice(TakedownNoticeRequestContext takedownNoticeRequestContext) throws MailException; |
| 915 | 931 | |
| 916 | 932 | /** |
| ... | ... | @@ -928,4 +944,8 @@ public interface SecurityService extends ReplicableWithObjectInputStream<Replica |
| 928 | 944 | */ |
| 929 | 945 | Iterable<User> getUsersToInformAboutReplicaSet(String serverName, |
| 930 | 946 | Optional<com.sap.sse.security.shared.HasPermissions.Action> alsoSendToAllUsersWithThisPermissionOnReplicaSet); |
| 947 | + |
|
| 948 | + HashMap<String, TimedLock> getClientIPBasedTimedLocksForUserCreation(); |
|
| 949 | + |
|
| 950 | + HashMap<String, TimedLock> getClientIPBasedTimedLocksForBearerTokenAbuse(); |
|
| 931 | 951 | } |
java/com.sap.sse.security/src/com/sap/sse/security/UsernamePasswordRealm.java
| ... | ... | @@ -50,8 +50,8 @@ public class UsernamePasswordRealm extends AbstractCompositeAuthorizingRealm { |
| 50 | 50 | logger.warning("Rejecting authentication attempt for non-existing user "+username); |
| 51 | 51 | return null; |
| 52 | 52 | } |
| 53 | - if (user.getLockingAndBanning().isAuthenticationLocked()) { |
|
| 54 | - logger.warning("Rejected attempt to authenticate user "+username+" because it is locked: "+user.getLockingAndBanning()); |
|
| 53 | + if (user.getTimedLock().isLocked()) { |
|
| 54 | + logger.warning("Rejected attempt to authenticate user "+username+" because it is locked: "+user.getTimedLock()); |
|
| 55 | 55 | throw new LockedAccountException("Password authentication for user "+username+" is currently locked"); |
| 56 | 56 | } |
| 57 | 57 | final UsernamePasswordAccount upa = (UsernamePasswordAccount) user.getAccount(AccountType.USERNAME_PASSWORD); |
java/com.sap.sse.security/src/com/sap/sse/security/impl/ReplicableSecurityService.java
| ... | ... | @@ -8,6 +8,7 @@ import java.util.UUID; |
| 8 | 8 | |
| 9 | 9 | import org.apache.shiro.session.Session; |
| 10 | 10 | |
| 11 | +import com.sap.sse.common.TimedLock; |
|
| 11 | 12 | import com.sap.sse.security.SecurityService; |
| 12 | 13 | import com.sap.sse.security.shared.Account; |
| 13 | 14 | import com.sap.sse.security.shared.QualifiedObjectIdentifier; |
| ... | ... | @@ -15,7 +16,6 @@ import com.sap.sse.security.shared.RoleDefinition; |
| 15 | 16 | import com.sap.sse.security.shared.UserGroupManagementException; |
| 16 | 17 | import com.sap.sse.security.shared.UserManagementException; |
| 17 | 18 | import com.sap.sse.security.shared.WildcardPermission; |
| 18 | -import com.sap.sse.security.shared.impl.LockingAndBanning; |
|
| 19 | 19 | import com.sap.sse.security.shared.impl.Ownership; |
| 20 | 20 | import com.sap.sse.security.shared.impl.User; |
| 21 | 21 | import com.sap.sse.security.shared.subscription.Subscription; |
| ... | ... | @@ -69,6 +69,8 @@ public interface ReplicableSecurityService extends SecurityService { |
| 69 | 69 | |
| 70 | 70 | Void internalUpdateUserProperties(String username, String fullName, String company, Locale locale); |
| 71 | 71 | |
| 72 | + Void internalResetUserTimedLock(String username); |
|
| 73 | + |
|
| 72 | 74 | Boolean internalValidateEmail(String username, String validationSecret); |
| 73 | 75 | |
| 74 | 76 | Void internalSetPreference(String username, String key, String value); |
| ... | ... | @@ -122,13 +124,17 @@ public interface ReplicableSecurityService extends SecurityService { |
| 122 | 124 | |
| 123 | 125 | Void internalSetCORSFilterConfigurationAllowedOrigins(String serverName, String... allowedOrigins); |
| 124 | 126 | |
| 125 | - LockingAndBanning internalFailedPasswordAuthentication(String username); |
|
| 127 | + TimedLock internalFailedPasswordAuthentication(String username); |
|
| 126 | 128 | |
| 127 | 129 | Boolean internalSuccessfulPasswordAuthentication(String username); |
| 128 | 130 | |
| 129 | 131 | Boolean internalSuccessfulBearerTokenAuthentication(String clientIP); |
| 130 | 132 | |
| 131 | - LockingAndBanning internalFailedBearerTokenAuthentication(String clientIP); |
|
| 133 | + TimedLock internalFailedBearerTokenAuthentication(String clientIP); |
|
| 134 | + |
|
| 135 | + TimedLock internalRecordUserCreationFromClientIP(String clientIP); |
|
| 136 | + |
|
| 137 | + void internalReleaseUserCreationLockOnIp(String ip); |
|
| 132 | 138 | |
| 133 | - LockingAndBanning internalRecordUserCreationFromClientIP(String clientIP); |
|
| 139 | + void internalReleaseBearerTokenLockOnIp(String ip); |
|
| 134 | 140 | } |
java/com.sap.sse.security/src/com/sap/sse/security/impl/SecurityServiceImpl.java
| ... | ... | @@ -95,9 +95,11 @@ import com.sap.sse.ServerInfo; |
| 95 | 95 | import com.sap.sse.branding.BrandingConfigurationService; |
| 96 | 96 | import com.sap.sse.common.Duration; |
| 97 | 97 | import com.sap.sse.common.TimePoint; |
| 98 | +import com.sap.sse.common.TimedLock; |
|
| 98 | 99 | import com.sap.sse.common.Util; |
| 99 | 100 | import com.sap.sse.common.Util.Pair; |
| 100 | 101 | import com.sap.sse.common.http.HttpHeaderUtil; |
| 102 | +import com.sap.sse.common.impl.TimedLockImpl; |
|
| 101 | 103 | import com.sap.sse.common.mail.MailException; |
| 102 | 104 | import com.sap.sse.common.media.TakedownNoticeRequestContext; |
| 103 | 105 | import com.sap.sse.concurrent.LockUtil; |
| ... | ... | @@ -140,12 +142,15 @@ import com.sap.sse.security.operations.DeleteRoleDefinitionOperation; |
| 140 | 142 | import com.sap.sse.security.operations.DeleteUserGroupOperation; |
| 141 | 143 | import com.sap.sse.security.operations.DeleteUserOperation; |
| 142 | 144 | import com.sap.sse.security.operations.PutRoleDefinitionToUserGroupOperation; |
| 145 | +import com.sap.sse.security.operations.ReleaseBearerTokenLockOnIpOperation; |
|
| 146 | +import com.sap.sse.security.operations.ReleaseUserCreationLockOnIpOperation; |
|
| 143 | 147 | import com.sap.sse.security.operations.RemoveAccessTokenOperation; |
| 144 | 148 | import com.sap.sse.security.operations.RemovePermissionForUserOperation; |
| 145 | 149 | import com.sap.sse.security.operations.RemoveRoleDefinitionFromUserGroupOperation; |
| 146 | 150 | import com.sap.sse.security.operations.RemoveRoleFromUserOperation; |
| 147 | 151 | import com.sap.sse.security.operations.RemoveUserFromUserGroupOperation; |
| 148 | 152 | import com.sap.sse.security.operations.ResetPasswordOperation; |
| 153 | +import com.sap.sse.security.operations.ResetUserLockOperation; |
|
| 149 | 154 | import com.sap.sse.security.operations.SecurityOperation; |
| 150 | 155 | import com.sap.sse.security.operations.SetAccessTokenOperation; |
| 151 | 156 | import com.sap.sse.security.operations.SetDefaultTenantForServerForUserOperation; |
| ... | ... | @@ -186,8 +191,6 @@ import com.sap.sse.security.shared.UsernamePasswordAccount; |
| 186 | 191 | import com.sap.sse.security.shared.WildcardPermission; |
| 187 | 192 | import com.sap.sse.security.shared.WithQualifiedObjectIdentifier; |
| 188 | 193 | import com.sap.sse.security.shared.impl.AccessControlList; |
| 189 | -import com.sap.sse.security.shared.impl.LockingAndBanning; |
|
| 190 | -import com.sap.sse.security.shared.impl.LockingAndBanningImpl; |
|
| 191 | 194 | import com.sap.sse.security.shared.impl.Ownership; |
| 192 | 195 | import com.sap.sse.security.shared.impl.PermissionAndRoleAssociation; |
| 193 | 196 | import com.sap.sse.security.shared.impl.Role; |
| ... | ... | @@ -281,7 +284,7 @@ implements ReplicableSecurityService, ClearStateTestSupport { |
| 281 | 284 | * @see #successfulBearerTokenAuthentication(String) |
| 282 | 285 | * @see #isClientIPLockedForBearerTokenAuthentication(String) |
| 283 | 286 | */ |
| 284 | - private final ConcurrentMap<String, LockingAndBanning> clientIPBasedLockingAndBanningForBearerTokenAuthentication; |
|
| 287 | + private final ConcurrentMap<String, TimedLock> clientIPBasedTimedLocksForBearerTokenAuthentication; |
|
| 285 | 288 | private final static String CLIENT_IP_NULL_ESCAPE = UUID.randomUUID().toString(); |
| 286 | 289 | |
| 287 | 290 | /** |
| ... | ... | @@ -293,7 +296,7 @@ implements ReplicableSecurityService, ClearStateTestSupport { |
| 293 | 296 | * When entering values into this map, the method entering it is responsible for also scheduling a background |
| 294 | 297 | * task that a while after lock expiry the record is expunged again from the map to avoid garbage piling up. |
| 295 | 298 | */ |
| 296 | - private final ConcurrentMap<String, LockingAndBanning> clientIPBasedLockingAndBanningForUserCreation; |
|
| 299 | + private final ConcurrentMap<String, TimedLock> clientIPBasedTimedLocksForUserCreation; |
|
| 297 | 300 | |
| 298 | 301 | private final Zxcvbn passwordValidator; |
| 299 | 302 | |
| ... | ... | @@ -347,8 +350,8 @@ implements ReplicableSecurityService, ClearStateTestSupport { |
| 347 | 350 | throw new IllegalArgumentException("No HasPermissionsProvider defined"); |
| 348 | 351 | } |
| 349 | 352 | logger.info("Initializing Security Service with user store " + userStore); |
| 350 | - this.clientIPBasedLockingAndBanningForBearerTokenAuthentication = new ConcurrentHashMap<>(); |
|
| 351 | - this.clientIPBasedLockingAndBanningForUserCreation = new ConcurrentHashMap<>(); |
|
| 353 | + this.clientIPBasedTimedLocksForBearerTokenAuthentication = new ConcurrentHashMap<>(); |
|
| 354 | + this.clientIPBasedTimedLocksForUserCreation = new ConcurrentHashMap<>(); |
|
| 352 | 355 | this.permissionChangeListeners = new PermissionChangeListeners(this); |
| 353 | 356 | this.sharedAcrossSubdomainsOf = sharedAcrossSubdomainsOf; |
| 354 | 357 | this.subscriptionPlanProvider = subscriptionPlanProvider; |
| ... | ... | @@ -1000,6 +1003,18 @@ implements ReplicableSecurityService, ClearStateTestSupport { |
| 1000 | 1003 | } |
| 1001 | 1004 | |
| 1002 | 1005 | @Override |
| 1006 | + public void releaseUserCreationLockOnIp(String ip) { |
|
| 1007 | + logger.info("Releasing timed lock for user creation at IP "+ip); |
|
| 1008 | + apply(new ReleaseUserCreationLockOnIpOperation(ip)); |
|
| 1009 | + } |
|
| 1010 | + |
|
| 1011 | + @Override |
|
| 1012 | + public void releaseBearerTokenLockOnIp(String ip) { |
|
| 1013 | + logger.info("Releasing timed lock for bearer token abuse at IP "+ip); |
|
| 1014 | + apply(new ReleaseBearerTokenLockOnIpOperation(ip)); |
|
| 1015 | + } |
|
| 1016 | + |
|
| 1017 | + @Override |
|
| 1003 | 1018 | public Void internalDeleteUserGroup(UUID groupId) throws UserGroupManagementException { |
| 1004 | 1019 | final UserGroup userGroup = getUserGroup(groupId); |
| 1005 | 1020 | if (userGroup == null) { |
| ... | ... | @@ -1105,6 +1120,16 @@ implements ReplicableSecurityService, ClearStateTestSupport { |
| 1105 | 1120 | } |
| 1106 | 1121 | |
| 1107 | 1122 | @Override |
| 1123 | + public HashMap<String,TimedLock> getClientIPBasedTimedLocksForUserCreation() { |
|
| 1124 | + return new HashMap<String, TimedLock>(clientIPBasedTimedLocksForUserCreation); |
|
| 1125 | + } |
|
| 1126 | + |
|
| 1127 | + @Override |
|
| 1128 | + public HashMap<String,TimedLock> getClientIPBasedTimedLocksForBearerTokenAbuse() { |
|
| 1129 | + return new HashMap<String, TimedLock>(clientIPBasedTimedLocksForBearerTokenAuthentication); |
|
| 1130 | + } |
|
| 1131 | + |
|
| 1132 | + @Override |
|
| 1108 | 1133 | public User createSimpleUser(final String username, final String email, String password, String fullName, |
| 1109 | 1134 | String company, Locale locale, final String validationBaseURL, UserGroup groupOwningUser, |
| 1110 | 1135 | String requestClientIP, boolean enforceStrongPassword) throws UserManagementException, MailException, UserGroupManagementException { |
| ... | ... | @@ -1160,22 +1185,29 @@ implements ReplicableSecurityService, ClearStateTestSupport { |
| 1160 | 1185 | // synchronize to ensure that no two threads can enter values into the map concurrently; |
| 1161 | 1186 | // still the use of a ConcurrentMap is justified because there may be concurrent write access |
| 1162 | 1187 | // through replication |
| 1163 | - synchronized (clientIPBasedLockingAndBanningForUserCreation) { |
|
| 1164 | - final LockingAndBanning lockingAndBanning = clientIPBasedLockingAndBanningForUserCreation.get(clientIP); |
|
| 1165 | - if (lockingAndBanning == null || !lockingAndBanning.isAuthenticationLocked()) { |
|
| 1188 | + synchronized (clientIPBasedTimedLocksForUserCreation) { |
|
| 1189 | + final TimedLock timedLock = clientIPBasedTimedLocksForUserCreation.get(clientIP); |
|
| 1190 | + if (timedLock == null || !timedLock.isLocked()) { |
|
| 1166 | 1191 | apply(s->s.internalRecordUserCreationFromClientIP(clientIP)); |
| 1167 | 1192 | } else { |
| 1168 | - throw new UserManagementException(UserManagementException.CLIENT_CURRENTLY_LOCKED_FOR_USER_CREATION); |
|
| 1193 | + timedLock.extendLockDuration(); |
|
| 1194 | + throw new UserManagementException("Client IP "+clientIP+" locked for user creation: "+timedLock); |
|
| 1169 | 1195 | } |
| 1170 | 1196 | } |
| 1171 | 1197 | } |
| 1172 | 1198 | |
| 1173 | 1199 | @Override |
| 1174 | - public LockingAndBanning internalRecordUserCreationFromClientIP(String clientIP) { |
|
| 1175 | - final LockingAndBanning result = new LockingAndBanningImpl(TimePoint.now().plus(DEFAULT_CLIENT_IP_BASED_USER_CREATION_LOCKING_DURATION), |
|
| 1200 | + public boolean isUserCreationLockedForClientIP(String clientIP) { |
|
| 1201 | + final TimedLock timedLock = clientIPBasedTimedLocksForUserCreation.get(escapeNullClientIP(clientIP)); |
|
| 1202 | + return timedLock != null && timedLock.isLocked(); |
|
| 1203 | + } |
|
| 1204 | + |
|
| 1205 | + @Override |
|
| 1206 | + public TimedLock internalRecordUserCreationFromClientIP(String clientIP) { |
|
| 1207 | + final TimedLock result = new TimedLockImpl(TimePoint.now().plus(DEFAULT_CLIENT_IP_BASED_USER_CREATION_LOCKING_DURATION), |
|
| 1176 | 1208 | DEFAULT_CLIENT_IP_BASED_USER_CREATION_LOCKING_DURATION); |
| 1177 | - clientIPBasedLockingAndBanningForUserCreation.put(clientIP, result); |
|
| 1178 | - scheduleCleanUpTask(clientIP, result, clientIPBasedLockingAndBanningForUserCreation, |
|
| 1209 | + clientIPBasedTimedLocksForUserCreation.put(clientIP, result); |
|
| 1210 | + scheduleCleanUpTask(clientIP, result, clientIPBasedTimedLocksForUserCreation, |
|
| 1179 | 1211 | "client IPs locked for user creation"); |
| 1180 | 1212 | return result; |
| 1181 | 1213 | } |
| ... | ... | @@ -1215,7 +1247,7 @@ implements ReplicableSecurityService, ClearStateTestSupport { |
| 1215 | 1247 | |
| 1216 | 1248 | @Override |
| 1217 | 1249 | public User internalCreateUser(String username, String email, Account... accounts) throws UserManagementException { |
| 1218 | - final User result = store.createUser(username, email, new LockingAndBanningImpl(), accounts); |
|
| 1250 | + final User result = store.createUser(username, email, new TimedLockImpl(), accounts); |
|
| 1219 | 1251 | return result; |
| 1220 | 1252 | } |
| 1221 | 1253 | |
| ... | ... | @@ -1275,12 +1307,29 @@ implements ReplicableSecurityService, ClearStateTestSupport { |
| 1275 | 1307 | } |
| 1276 | 1308 | |
| 1277 | 1309 | @Override |
| 1310 | + public void resetUserTimedLock(String username) throws UserManagementException { |
|
| 1311 | + final User user = store.getUserByName(username); |
|
| 1312 | + if (user == null) { |
|
| 1313 | + throw new UserManagementException(UserManagementException.USER_DOES_NOT_EXIST); |
|
| 1314 | + } |
|
| 1315 | + apply(new ResetUserLockOperation(username, user.getTimedLock())); |
|
| 1316 | + } |
|
| 1317 | + |
|
| 1318 | + @Override |
|
| 1319 | + public Void internalResetUserTimedLock(String username) { |
|
| 1320 | + final User user = store.getUserByName(username); |
|
| 1321 | + user.getTimedLock().resetLock(); |
|
| 1322 | + store.updateUser(user); |
|
| 1323 | + return null; |
|
| 1324 | + } |
|
| 1325 | + |
|
| 1326 | + @Override |
|
| 1278 | 1327 | public boolean checkPassword(String username, String password) throws UserManagementException { |
| 1279 | 1328 | final User user = store.getUserByName(username); |
| 1280 | 1329 | if (user == null) { |
| 1281 | 1330 | throw new UserManagementException(UserManagementException.USER_DOES_NOT_EXIST); |
| 1282 | 1331 | } |
| 1283 | - if (user.getLockingAndBanning().isAuthenticationLocked()) { |
|
| 1332 | + if (user.getTimedLock().isLocked()) { |
|
| 1284 | 1333 | throw new UserManagementException(UserManagementException.PASSWORD_AUTHENTICATION_CURRENTLY_LOCKED_FOR_USER); |
| 1285 | 1334 | } |
| 1286 | 1335 | final UsernamePasswordAccount account = (UsernamePasswordAccount) user.getAccount(AccountType.USERNAME_PASSWORD); |
| ... | ... | @@ -1296,23 +1345,23 @@ implements ReplicableSecurityService, ClearStateTestSupport { |
| 1296 | 1345 | } |
| 1297 | 1346 | |
| 1298 | 1347 | @Override |
| 1299 | - public LockingAndBanning failedPasswordAuthentication(User user) { |
|
| 1348 | + public TimedLock failedPasswordAuthentication(User user) { |
|
| 1300 | 1349 | return apply(s->s.internalFailedPasswordAuthentication(user.getName())); |
| 1301 | 1350 | } |
| 1302 | 1351 | |
| 1303 | 1352 | @Override |
| 1304 | - public LockingAndBanning internalFailedPasswordAuthentication(String username) { |
|
| 1353 | + public TimedLock internalFailedPasswordAuthentication(String username) { |
|
| 1305 | 1354 | final User user = getUserByName(username); |
| 1306 | - final LockingAndBanning lockingAndBanning; |
|
| 1355 | + final TimedLock timedLock; |
|
| 1307 | 1356 | if (user != null) { |
| 1308 | - lockingAndBanning = user.getLockingAndBanning(); |
|
| 1309 | - lockingAndBanning.failedPasswordAuthentication(); |
|
| 1357 | + timedLock = user.getTimedLock(); |
|
| 1358 | + timedLock.extendLockDuration(); |
|
| 1310 | 1359 | store.updateUser(user); |
| 1311 | - logger.info("failed password authentication for user "+username+"; locking: "+lockingAndBanning); |
|
| 1360 | + logger.info("failed password authentication for user "+username+"; locking: "+timedLock); |
|
| 1312 | 1361 | } else { |
| 1313 | - lockingAndBanning = null; |
|
| 1362 | + timedLock = null; |
|
| 1314 | 1363 | } |
| 1315 | - return lockingAndBanning; |
|
| 1364 | + return timedLock; |
|
| 1316 | 1365 | } |
| 1317 | 1366 | |
| 1318 | 1367 | @Override |
| ... | ... | @@ -1328,7 +1377,7 @@ implements ReplicableSecurityService, ClearStateTestSupport { |
| 1328 | 1377 | final boolean changed; |
| 1329 | 1378 | final User user = getUserByName(username); |
| 1330 | 1379 | if (user != null) { |
| 1331 | - changed = user.getLockingAndBanning().successfulPasswordAuthentication(); |
|
| 1380 | + changed = user.getTimedLock().resetLock(); |
|
| 1332 | 1381 | if (changed) { |
| 1333 | 1382 | store.updateUser(user); |
| 1334 | 1383 | } |
| ... | ... | @@ -1339,8 +1388,8 @@ implements ReplicableSecurityService, ClearStateTestSupport { |
| 1339 | 1388 | } |
| 1340 | 1389 | |
| 1341 | 1390 | @Override |
| 1342 | - public LockingAndBanning failedBearerTokenAuthentication(String clientIP) { |
|
| 1343 | - final LockingAndBanning result; |
|
| 1391 | + public TimedLock failedBearerTokenAuthentication(String clientIP) { |
|
| 1392 | + final TimedLock result; |
|
| 1344 | 1393 | final ReplicationService replicationService = getReplicationService(); |
| 1345 | 1394 | if (replicationService == null || !replicationService.isReplicationStarting()) { |
| 1346 | 1395 | result = apply(s->s.internalFailedBearerTokenAuthentication(clientIP)); |
| ... | ... | @@ -1352,22 +1401,22 @@ implements ReplicableSecurityService, ClearStateTestSupport { |
| 1352 | 1401 | } |
| 1353 | 1402 | |
| 1354 | 1403 | @Override |
| 1355 | - public LockingAndBanning internalFailedBearerTokenAuthentication(String clientIP) { |
|
| 1356 | - final LockingAndBanning lockingAndBanning = clientIPBasedLockingAndBanningForBearerTokenAuthentication.computeIfAbsent(escapeNullClientIP(clientIP), key->new LockingAndBanningImpl()); |
|
| 1357 | - lockingAndBanning.failedPasswordAuthentication(); |
|
| 1358 | - logger.info("failed bearer token authentication from client IP "+clientIP+"; locking: "+lockingAndBanning); |
|
| 1359 | - scheduleCleanUpTask(clientIP, lockingAndBanning, clientIPBasedLockingAndBanningForBearerTokenAuthentication, |
|
| 1404 | + public TimedLock internalFailedBearerTokenAuthentication(String clientIP) { |
|
| 1405 | + final TimedLock timedLock = clientIPBasedTimedLocksForBearerTokenAuthentication.computeIfAbsent(escapeNullClientIP(clientIP), key->new TimedLockImpl()); |
|
| 1406 | + timedLock.extendLockDuration(); |
|
| 1407 | + logger.info("failed bearer token authentication from client IP "+clientIP+"; locking: "+timedLock); |
|
| 1408 | + scheduleCleanUpTask(clientIP, timedLock, clientIPBasedTimedLocksForBearerTokenAuthentication, |
|
| 1360 | 1409 | "client IPs locked for bearer token authentication"); |
| 1361 | - return lockingAndBanning; |
|
| 1410 | + return timedLock; |
|
| 1362 | 1411 | } |
| 1363 | 1412 | |
| 1364 | 1413 | /** |
| 1365 | - * Schedule a clean-up task to avoid leaking memory for the LockingAndBanning objects; schedule it in two times the |
|
| 1366 | - * locking expiry of {@code lockingAndBanning}, but at least one hour, because if no authentication failure occurs |
|
| 1367 | - * for that IP/user agent combination, we will entirely remove the {@link LockingAndBanning} from the map, |
|
| 1414 | + * Schedule a clean-up task to avoid leaking memory for the TimedLock objects; schedule it in two times the |
|
| 1415 | + * locking expiry of {@code timedLock}, but at least one hour, because if no authentication failure occurs |
|
| 1416 | + * for that IP/user agent combination, we will entirely remove the {@link TimedLock} from the map, |
|
| 1368 | 1417 | * effectively resetting that IP to a short default locking duration again; this way, if during the double |
| 1369 | 1418 | * expiration time another failed attempt is registered, we can still grow the locking duration because we have kept |
| 1370 | - * the {@link LockingAndBanning} object available for a bit longer. Furthermore, for authentication requests, the |
|
| 1419 | + * the {@link TimedLock} object available for a bit longer. Furthermore, for authentication requests, the |
|
| 1371 | 1420 | * responsible {@link Realm} will let authentication requests get to here only if not locked, so if we were to |
| 1372 | 1421 | * expunge entries immediately as they unlock, the locking duration could never grow.<p> |
| 1373 | 1422 | * |
| ... | ... | @@ -1375,16 +1424,16 @@ implements ReplicableSecurityService, ClearStateTestSupport { |
| 1375 | 1424 | * expiry duration. |
| 1376 | 1425 | */ |
| 1377 | 1426 | private void scheduleCleanUpTask(final String clientIPOrNull, |
| 1378 | - final LockingAndBanning lockingAndBanning, |
|
| 1379 | - final ConcurrentMap<String, LockingAndBanning> mapToRemoveFrom, |
|
| 1427 | + final TimedLock timedLock, |
|
| 1428 | + final ConcurrentMap<String, TimedLock> mapToRemoveFrom, |
|
| 1380 | 1429 | final String nameOfMapForLog) { |
| 1381 | 1430 | final long millisUntilLockingExpiry = Math.max( |
| 1382 | - 2*ApproximateTime.approximateNow().until(lockingAndBanning.getLockedUntil()).asMillis(), |
|
| 1431 | + 2*ApproximateTime.approximateNow().until(timedLock.getLockedUntil()).asMillis(), |
|
| 1383 | 1432 | Duration.ONE_HOUR.asMillis()); |
| 1384 | 1433 | ThreadPoolUtil.INSTANCE.getDefaultBackgroundTaskThreadPoolExecutor().schedule( |
| 1385 | 1434 | ()->{ |
| 1386 | - final LockingAndBanning lab = mapToRemoveFrom.get(escapeNullClientIP(clientIPOrNull)); |
|
| 1387 | - if (lab != null && !lab.isAuthenticationLocked()) { |
|
| 1435 | + final TimedLock lab = mapToRemoveFrom.get(escapeNullClientIP(clientIPOrNull)); |
|
| 1436 | + if (lab != null && !lab.isLocked()) { |
|
| 1388 | 1437 | mapToRemoveFrom.remove(escapeNullClientIP(clientIPOrNull)); |
| 1389 | 1438 | logger.info("Removed "+clientIPOrNull+" from "+nameOfMapForLog+"; " |
| 1390 | 1439 | +mapToRemoveFrom.size() |
| ... | ... | @@ -1409,9 +1458,9 @@ implements ReplicableSecurityService, ClearStateTestSupport { |
| 1409 | 1458 | @Override |
| 1410 | 1459 | public Boolean internalSuccessfulBearerTokenAuthentication(String clientIP) { |
| 1411 | 1460 | final boolean changed; |
| 1412 | - final LockingAndBanning lockingAndBanning = clientIPBasedLockingAndBanningForBearerTokenAuthentication.remove(escapeNullClientIP(clientIP)); |
|
| 1413 | - if (lockingAndBanning != null) { |
|
| 1414 | - logger.info("Unlocked bearer token authentication from "+clientIP+"; last locking state was "+lockingAndBanning); |
|
| 1461 | + final TimedLock timedLock = clientIPBasedTimedLocksForBearerTokenAuthentication.remove(escapeNullClientIP(clientIP)); |
|
| 1462 | + if (timedLock != null) { |
|
| 1463 | + logger.info("Unlocked bearer token authentication from "+clientIP+"; last locking state was "+timedLock); |
|
| 1415 | 1464 | changed = true; |
| 1416 | 1465 | } else { |
| 1417 | 1466 | changed = false; |
| ... | ... | @@ -1421,8 +1470,8 @@ implements ReplicableSecurityService, ClearStateTestSupport { |
| 1421 | 1470 | |
| 1422 | 1471 | @Override |
| 1423 | 1472 | public boolean isClientIPLockedForBearerTokenAuthentication(String clientIP) { |
| 1424 | - final LockingAndBanning lockingAndBanning = clientIPBasedLockingAndBanningForBearerTokenAuthentication.get(escapeNullClientIP(clientIP)); |
|
| 1425 | - return lockingAndBanning != null && lockingAndBanning.isAuthenticationLocked(); |
|
| 1473 | + final TimedLock timedLock = clientIPBasedTimedLocksForBearerTokenAuthentication.get(escapeNullClientIP(clientIP)); |
|
| 1474 | + return timedLock != null && timedLock.isLocked(); |
|
| 1426 | 1475 | } |
| 1427 | 1476 | |
| 1428 | 1477 | @Override |
| ... | ... | @@ -2521,8 +2570,8 @@ implements ReplicableSecurityService, ClearStateTestSupport { |
| 2521 | 2570 | store.clear(); |
| 2522 | 2571 | accessControlStore.clear(); |
| 2523 | 2572 | corsFilterConfigurationsByReplicaSetName.clear(); |
| 2524 | - clientIPBasedLockingAndBanningForBearerTokenAuthentication.clear(); |
|
| 2525 | - clientIPBasedLockingAndBanningForUserCreation.clear(); |
|
| 2573 | + clientIPBasedTimedLocksForBearerTokenAuthentication.clear(); |
|
| 2574 | + clientIPBasedTimedLocksForUserCreation.clear(); |
|
| 2526 | 2575 | } |
| 2527 | 2576 | |
| 2528 | 2577 | @Override |
| ... | ... | @@ -2604,13 +2653,13 @@ implements ReplicableSecurityService, ClearStateTestSupport { |
| 2604 | 2653 | final SecurityServiceInitialLoadExtensionsDTO initialLoadExtensions = (SecurityServiceInitialLoadExtensionsDTO) is.readObject(); |
| 2605 | 2654 | final ConcurrentMap<String, Pair<Boolean, Set<String>>> newCORSFilterConfigurations = initialLoadExtensions.getCorsFilterConfigurationsByReplicaSetName(); |
| 2606 | 2655 | corsFilterConfigurationsByReplicaSetName.putAll(newCORSFilterConfigurations); |
| 2607 | - if (initialLoadExtensions.getClientIPBasedLockingAndBanningForBearerTokenAuthentication() != null) { |
|
| 2656 | + if (initialLoadExtensions.getClientIPBasedTimedLocksForBearerTokenAuthentication() != null) { |
|
| 2608 | 2657 | // checking for null for backward compatibility; an older primary/master may not have known this field yet |
| 2609 | - clientIPBasedLockingAndBanningForBearerTokenAuthentication.putAll(initialLoadExtensions.getClientIPBasedLockingAndBanningForBearerTokenAuthentication()); |
|
| 2658 | + clientIPBasedTimedLocksForBearerTokenAuthentication.putAll(initialLoadExtensions.getClientIPBasedTimedLocksForBearerTokenAuthentication()); |
|
| 2610 | 2659 | } |
| 2611 | - if (initialLoadExtensions.getClientIPBasedLockingAndBanningForUserCreation() != null) { |
|
| 2660 | + if (initialLoadExtensions.getClientIPBasedTimedLocksForUserCreation() != null) { |
|
| 2612 | 2661 | // checking for null for backward compatibility; an older primary/master may not have known this field yet |
| 2613 | - clientIPBasedLockingAndBanningForUserCreation.putAll(initialLoadExtensions.getClientIPBasedLockingAndBanningForUserCreation()); |
|
| 2662 | + clientIPBasedTimedLocksForUserCreation.putAll(initialLoadExtensions.getClientIPBasedTimedLocksForUserCreation()); |
|
| 2614 | 2663 | } |
| 2615 | 2664 | logger.info("Triggering SecurityInitializationCustomizers upon replication ..."); |
| 2616 | 2665 | customizers.forEach(c -> c.customizeSecurityService(this)); |
| ... | ... | @@ -2626,8 +2675,8 @@ implements ReplicableSecurityService, ClearStateTestSupport { |
| 2626 | 2675 | objectOutputStream.writeObject(baseUrlForCrossDomainStorage); |
| 2627 | 2676 | objectOutputStream.writeObject(new SecurityServiceInitialLoadExtensionsDTO( |
| 2628 | 2677 | corsFilterConfigurationsByReplicaSetName, |
| 2629 | - clientIPBasedLockingAndBanningForBearerTokenAuthentication, |
|
| 2630 | - clientIPBasedLockingAndBanningForUserCreation)); |
|
| 2678 | + clientIPBasedTimedLocksForBearerTokenAuthentication, |
|
| 2679 | + clientIPBasedTimedLocksForUserCreation)); |
|
| 2631 | 2680 | } |
| 2632 | 2681 | |
| 2633 | 2682 | @Override |
| ... | ... | @@ -2934,8 +2983,8 @@ implements ReplicableSecurityService, ClearStateTestSupport { |
| 2934 | 2983 | // See com.sap.sse.security.impl.Activator.clearState(), moved due to required reinitialisation sequence for |
| 2935 | 2984 | // permission-vertical |
| 2936 | 2985 | public void clearState() throws Exception { |
| 2937 | - clientIPBasedLockingAndBanningForBearerTokenAuthentication.clear(); |
|
| 2938 | - clientIPBasedLockingAndBanningForUserCreation.clear(); |
|
| 2986 | + clientIPBasedTimedLocksForBearerTokenAuthentication.clear(); |
|
| 2987 | + clientIPBasedTimedLocksForUserCreation.clear(); |
|
| 2939 | 2988 | } |
| 2940 | 2989 | |
| 2941 | 2990 | @Override |
| ... | ... | @@ -3499,4 +3548,18 @@ implements ReplicableSecurityService, ClearStateTestSupport { |
| 3499 | 3548 | .forEach(usersToSendMailTo::add)); |
| 3500 | 3549 | return usersToSendMailTo; |
| 3501 | 3550 | } |
| 3551 | + |
|
| 3552 | + @Override |
|
| 3553 | + public void internalReleaseUserCreationLockOnIp(String ip) { |
|
| 3554 | + if(clientIPBasedTimedLocksForUserCreation.containsKey(ip)) { |
|
| 3555 | + clientIPBasedTimedLocksForUserCreation.remove(ip); |
|
| 3556 | + } |
|
| 3557 | + } |
|
| 3558 | + |
|
| 3559 | + @Override |
|
| 3560 | + public void internalReleaseBearerTokenLockOnIp(String ip) { |
|
| 3561 | + if(clientIPBasedTimedLocksForBearerTokenAuthentication.containsKey(ip)) { |
|
| 3562 | + clientIPBasedTimedLocksForBearerTokenAuthentication.remove(ip); |
|
| 3563 | + } |
|
| 3564 | + } |
|
| 3502 | 3565 | } |
java/com.sap.sse.security/src/com/sap/sse/security/impl/SecurityServiceInitialLoadExtensionsDTO.java
| ... | ... | @@ -6,10 +6,10 @@ import java.io.Serializable; |
| 6 | 6 | import java.util.Set; |
| 7 | 7 | import java.util.concurrent.ConcurrentMap; |
| 8 | 8 | |
| 9 | +import com.sap.sse.common.TimedLock; |
|
| 9 | 10 | import com.sap.sse.common.Util.Pair; |
| 10 | 11 | import com.sap.sse.replication.Replicable; |
| 11 | 12 | import com.sap.sse.security.SecurityService; |
| 12 | -import com.sap.sse.security.shared.impl.LockingAndBanning; |
|
| 13 | 13 | |
| 14 | 14 | /** |
| 15 | 15 | * Starting with the CORS filter configurations, this and future extensions of the {@link SecurityService}'s |
| ... | ... | @@ -30,29 +30,30 @@ public class SecurityServiceInitialLoadExtensionsDTO implements Serializable { |
| 30 | 30 | |
| 31 | 31 | private final ConcurrentMap<String, Pair<Boolean, Set<String>>> corsFilterConfigurationsByReplicaSetName; |
| 32 | 32 | |
| 33 | - private final ConcurrentMap<String, LockingAndBanning> clientIPBasedLockingAndBanningForBearerTokenAuthentication; |
|
| 33 | + private final ConcurrentMap<String, TimedLock> clientIPBasedTimedLocksForBearerTokenAuthentication; |
|
| 34 | 34 | |
| 35 | - private final ConcurrentMap<String, LockingAndBanning> clientIPBasedLockingAndBanningForUserCreation; |
|
| 35 | + private final ConcurrentMap<String, TimedLock> clientIPBasedTimedLocksForUserCreation; |
|
| 36 | 36 | |
| 37 | 37 | public SecurityServiceInitialLoadExtensionsDTO( |
| 38 | 38 | ConcurrentMap<String, Pair<Boolean, Set<String>>> corsFilterConfigurationsByReplicaSetName, |
| 39 | - ConcurrentMap<String, LockingAndBanning> clientIPBasedLockingAndBanningForBearerTokenAuthentication, |
|
| 40 | - ConcurrentMap<String, LockingAndBanning> clientIPBasedLockingAndBanningForUserCreation) { |
|
| 39 | + ConcurrentMap<String, TimedLock> clientIPBasedTimedLockForBearerTokenAuthentication, |
|
| 40 | + ConcurrentMap<String, TimedLock> clientIPBasedTimedLocksForUserCreation) { |
|
| 41 | 41 | super(); |
| 42 | 42 | this.corsFilterConfigurationsByReplicaSetName = corsFilterConfigurationsByReplicaSetName; |
| 43 | - this.clientIPBasedLockingAndBanningForBearerTokenAuthentication = clientIPBasedLockingAndBanningForBearerTokenAuthentication; |
|
| 44 | - this.clientIPBasedLockingAndBanningForUserCreation = clientIPBasedLockingAndBanningForUserCreation; |
|
| 43 | + this.clientIPBasedTimedLocksForBearerTokenAuthentication = clientIPBasedTimedLockForBearerTokenAuthentication; |
|
| 44 | + this.clientIPBasedTimedLocksForUserCreation = clientIPBasedTimedLocksForUserCreation; |
|
| 45 | 45 | } |
| 46 | + |
|
| 46 | 47 | |
| 47 | 48 | ConcurrentMap<String, Pair<Boolean, Set<String>>> getCorsFilterConfigurationsByReplicaSetName() { |
| 48 | 49 | return corsFilterConfigurationsByReplicaSetName; |
| 49 | 50 | } |
| 50 | 51 | |
| 51 | - ConcurrentMap<String, LockingAndBanning> getClientIPBasedLockingAndBanningForBearerTokenAuthentication() { |
|
| 52 | - return clientIPBasedLockingAndBanningForBearerTokenAuthentication; |
|
| 52 | + ConcurrentMap<String, TimedLock> getClientIPBasedTimedLocksForBearerTokenAuthentication() { |
|
| 53 | + return clientIPBasedTimedLocksForBearerTokenAuthentication; |
|
| 53 | 54 | } |
| 54 | 55 | |
| 55 | - ConcurrentMap<String, LockingAndBanning> getClientIPBasedLockingAndBanningForUserCreation() { |
|
| 56 | - return clientIPBasedLockingAndBanningForUserCreation; |
|
| 56 | + ConcurrentMap<String, TimedLock> getClientIPBasedTimedLocksForUserCreation() { |
|
| 57 | + return clientIPBasedTimedLocksForUserCreation; |
|
| 57 | 58 | } |
| 58 | 59 | } |
java/com.sap.sse.security/src/com/sap/sse/security/operations/ReleaseBearerTokenLockOnIpOperation.java
| ... | ... | @@ -0,0 +1,18 @@ |
| 1 | +package com.sap.sse.security.operations; |
|
| 2 | + |
|
| 3 | +import com.sap.sse.security.impl.ReplicableSecurityService; |
|
| 4 | + |
|
| 5 | +public class ReleaseBearerTokenLockOnIpOperation implements SecurityOperation<Void> { |
|
| 6 | + private static final long serialVersionUID = 5839571828359473821L; |
|
| 7 | + protected final String ip; |
|
| 8 | + |
|
| 9 | + public ReleaseBearerTokenLockOnIpOperation(final String ip) { |
|
| 10 | + this.ip = ip; |
|
| 11 | + } |
|
| 12 | + |
|
| 13 | + @Override |
|
| 14 | + public Void internalApplyTo(ReplicableSecurityService toState) throws Exception { |
|
| 15 | + toState.internalReleaseBearerTokenLockOnIp(ip); |
|
| 16 | + return null; |
|
| 17 | + } |
|
| 18 | +} |
java/com.sap.sse.security/src/com/sap/sse/security/operations/ReleaseUserCreationLockOnIpOperation.java
| ... | ... | @@ -0,0 +1,18 @@ |
| 1 | +package com.sap.sse.security.operations; |
|
| 2 | + |
|
| 3 | +import com.sap.sse.security.impl.ReplicableSecurityService; |
|
| 4 | + |
|
| 5 | +public class ReleaseUserCreationLockOnIpOperation implements SecurityOperation<Void> { |
|
| 6 | + private static final long serialVersionUID = 8729427754960969395L; |
|
| 7 | + protected final String ip; |
|
| 8 | + |
|
| 9 | + public ReleaseUserCreationLockOnIpOperation(final String ip) { |
|
| 10 | + this.ip = ip; |
|
| 11 | + } |
|
| 12 | + |
|
| 13 | + @Override |
|
| 14 | + public Void internalApplyTo(ReplicableSecurityService toState) throws Exception { |
|
| 15 | + toState.internalReleaseUserCreationLockOnIp(ip); |
|
| 16 | + return null; |
|
| 17 | + } |
|
| 18 | +} |
java/com.sap.sse.security/src/com/sap/sse/security/operations/ResetUserLockOperation.java
| ... | ... | @@ -0,0 +1,22 @@ |
| 1 | +package com.sap.sse.security.operations; |
|
| 2 | + |
|
| 3 | +import com.sap.sse.common.TimedLock; |
|
| 4 | +import com.sap.sse.security.impl.ReplicableSecurityService; |
|
| 5 | + |
|
| 6 | +public class ResetUserLockOperation implements SecurityOperation<Void> { |
|
| 7 | + private static final long serialVersionUID = -6267523788529623080L; |
|
| 8 | + protected final TimedLock timedLock; |
|
| 9 | + protected final String username; |
|
| 10 | + |
|
| 11 | + public ResetUserLockOperation(String username, TimedLock timedLock) { |
|
| 12 | + this.username = username; |
|
| 13 | + this.timedLock = timedLock; |
|
| 14 | + } |
|
| 15 | + |
|
| 16 | + @Override |
|
| 17 | + public Void internalApplyTo(ReplicableSecurityService toState) throws Exception { |
|
| 18 | + toState.internalResetUserTimedLock(username); |
|
| 19 | + return null; |
|
| 20 | + } |
|
| 21 | + |
|
| 22 | +} |
java/com.sap.sse.threadmanager/resources/shiro.ini
| ... | ... | @@ -26,7 +26,7 @@ securityManager.subjectDAO = $subjectDAO |
| 26 | 26 | securityManager.sessionManager.globalSessionTimeout = 31536000000 |
| 27 | 27 | cacheManager = com.sap.sse.security.SessionCacheManager |
| 28 | 28 | securityManager.cacheManager = $cacheManager |
| 29 | -authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithLockingAndBanning |
|
| 29 | +authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithTimedLocks |
|
| 30 | 30 | securityManager.authenticator.authenticationStrategy = $authenticationStrategy |
| 31 | 31 | |
| 32 | 32 | # Support for anonymous user permissions |
scripts/assume_unchanged_for_mac.sh
| ... | ... | @@ -0,0 +1,25 @@ |
| 1 | +#!/bin/bash |
|
| 2 | + |
|
| 3 | +# Description: |
|
| 4 | +# This script finds all *.prefs and *.launch files tracked by Git |
|
| 5 | +# and sets them to "assume-unchanged", so local changes won't show |
|
| 6 | +# up in 'git status' or be accidentally committed. |
|
| 7 | + |
|
| 8 | +# Find and apply the assume-unchanged flag |
|
| 9 | +echo "Scanning for tracked *.prefs and *.launch files..." |
|
| 10 | + |
|
| 11 | +files=$(git ls-files | grep -E '\.prefs$|\.launch$') |
|
| 12 | + |
|
| 13 | +if [ -z "$files" ]; then |
|
| 14 | + echo "No .prefs or .launch files found in the tracked file list." |
|
| 15 | + exit 0 |
|
| 16 | +fi |
|
| 17 | + |
|
| 18 | +echo "$files" | while read -r file; do |
|
| 19 | + git update-index --assume-unchanged "$file" |
|
| 20 | + echo "Marked as assume-unchanged: $file" |
|
| 21 | +done |
|
| 22 | + |
|
| 23 | +echo "" |
|
| 24 | +echo "Git will now ignore local changes to these files (but they will still update from upstream if you clear this flag before pulling)." |
|
| 25 | + |
wiki/howto/onboarding.md
| ... | ... | @@ -10,7 +10,9 @@ First of all, make sure you've looked at [http://www.amazon.de/Patterns-Elements |
| 10 | 10 | |
| 11 | 11 | 1. Git Account |
| 12 | 12 | |
| 13 | - - The primary Git repository for the project is hosted on Github (see [https://github.com/SAP/sailing-analytics](https://github.com/SAP/sailing-analytics)). To clone, use ``git@github.com:SAP/sailing-analytics.git``. To gain write access you have to become member of the [sailing-analytics-team](https://github.com/orgs/SAP/teams/sailing-analytics-team) organization. For that you need to [link your Github user to the Github SAP organization](https://wiki.one.int.sap/wiki/display/ospodocs/Self-Service+for+Joining+an+SAP+GitHub+Organization). For that to work, your Github account needs to have your @sap.com e-mail address assigned and verified. We still have a shadow repository around that, e.g., powers our Wiki at [https://wiki.sapsailing.com](https://wiki.sapsailing.com) and which lives at ``ssh://trac@sapsailing.com/home/trac/git``. |
|
| 13 | + - The primary Git repository for the project is hosted on Github (see [https://github.com/SAP/sailing-analytics](https://github.com/SAP/sailing-analytics)). To clone, use ``git@github.com:SAP/sailing-analytics.git``. |
|
| 14 | + - If you are on Windows, keep in mind you may run into the following problem. By default, the filesystem in Windows enforces a 260 character limit on paths. The longest path length for a file in this project, if the drive name is included, is 263 characters. A possible solution is to pass a single character name for the project folder in the git clone command, and clone the project on drive root, which may bring the longest file path down to compatible length. Alternatively, Windows 10 and 11 offer settings to enable a much much longer maximum file path that requires additional configuration. You may check that out at your own will. |
|
| 15 | + - To gain write access you have to become member of the [sailing-analytics-team](https://github.com/orgs/SAP/teams/sailing-analytics-team) organization. For that you need to [link your Github user to the Github SAP organization](https://wiki.one.int.sap/wiki/display/ospodocs/Self-Service+for+Joining+an+SAP+GitHub+Organization). For that to work, your Github account needs to have your @sap.com e-mail address assigned and verified. We still have a shadow repository around that, e.g., powers our Wiki at [https://wiki.sapsailing.com](https://wiki.sapsailing.com) and which lives at ``ssh://trac@sapsailing.com/home/trac/git``. |
|
| 14 | 16 | |
| 15 | 17 | - In case you'd like to get access to the external git at `ssh://trac@sapsailing.com/home/trac/git` please send your SSH public key to one of the project maintainers, requesting git access. Make sure to NOT generate the key using Putty. Putty keys don't work reliably under Linux and on Windows/Cygwin environments. Use ssh-keygen in a Cygwin or Linux or MacOS/X environment instead. For further instructions for generating an ssh-key see [GitHub](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent). |
| 16 | 18 | Note: If you want to use the ssh-key in the context of our solution, it can be an RSA or ED25519 format. Example for creating a key: `ssh-keygen -t ed25519 -b 512 -C "test@test.com"`. Make sure to set a non-empty password for your key. |
| ... | ... | @@ -189,6 +191,9 @@ If git is not in the Path system environment variable, the gradle build will not |
| 189 | 191 | ### Build for deployment |
| 190 | 192 | Open a shell (preferrably a git bash or a cygwin bash), cd to the git workspace's root folder and issue "./configuration/buildAndUpdateProduct.sh build". This should build the software and run all the tests. If you want to avoid the tests being executed, use the -t option. If you only want to build one GWT permutation (Chrome/English), use the -b option. When inside the SAP VPN, add the -p option for proxy use. Run the build script without arguments to get usage hints. |
| 191 | 193 | |
| 194 | +### Development Environment |
|
| 195 | +Read [[here|wiki/info/landscape/development-environment]] and importantly for [[pushing to main|https://wiki.sapsailing.com/wiki/info/landscape/development-environment.md#git-bugzilla-and-our-branches]]. |
|
| 196 | + |
|
| 192 | 197 | ### Steps to consider for using other GWT modules |
| 193 | 198 | |
| 194 | 199 | 1. For Eclipse Build |
wiki/info/landscape/development-environment.md
| ... | ... | @@ -18,7 +18,7 @@ Everything else should follow the pattern |
| 18 | 18 | - when the workflow has finished, it triggers your Hudson job which collects the [build and test results](https://hudson.sapsailing.com/job/bug12345) |
| 19 | 19 | - be verbose and document your changes, progress, hold-ups and problems on your Bugzilla issue |
| 20 | 20 | - when build is "green," suggest your branch for review; so far we do this informally by assigning the Bugzilla issue to the reviewer and in a comment asking for review; in the future, we may want to use Github Pull Requests for this |
| 21 | -- after your branch has been merged into ``main``, disable your Hudson build job for your branch |
|
| 21 | +- after your branch has been merged into ``main``, disable your Hudson build job for your branch, comment about the merge in Bugzilla and resolve the Bugzilla item, usually as "FIXED". |
|
| 22 | 22 | - the ``main`` branch will then build a new release that you can roll out into the production landscape |
| 23 | 23 | - in case of changes to i18n-related message properties files, merge ``main`` into ``translation`` which triggers the translation process; the completed translations will arrive as pushes to the ``translations`` branch, triggering another ``release`` workflow, and---if successful---an automated merge into ``main`` with the corresponding build/release process happens, based on the [translation Hudson job](https://hudson.sapsailing.com/job/translation/configure)'s special logic |
| 24 | 24 | - a successful ``main`` build (still on Java 8) will lead to an automatic merge into one or more branches for newer Java releases (such as ``docker-24``) with the corresponding build/release process |