java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/security/SecuredDomainType.java
... ...
@@ -52,6 +52,25 @@ public class SecuredDomainType extends HasPermissionsImpl {
52 52
public static final HasPermissions TRACKED_RACE = new SecuredDomainType("TRACKED_RACE",
53 53
TrackedRaceActions.ALL_ACTIONS);
54 54
55
+ public static final HasPermissions IP_BLOCKLIST_FOR_BEARER_TOKEN_ABUSE = new SecuredDomainType(
56
+ "IP_BLOCKLIST_FOR_BEARER_TOKEN_ABUSE", IpBlocklistForBearerTokenAbuseActions.ALL_ACTIONS);
57
+
58
+ public static final HasPermissions IP_BLOCKLIST_FOR_USER_CREATION_ABUSE = new SecuredDomainType(
59
+ "IP_BLOCKLIST_FOR_USER_CREATION_ABUSE", IpBlocklistForUserCreationAbuseActions.ALL_ACTIONS);
60
+
61
+ public static enum IpBlocklistForBearerTokenAbuseActions implements Action {
62
+ GET, UNLOCK;
63
+
64
+ private static final Action[] ALL_ACTIONS = DefaultActions.plus(IpBlocklistForBearerTokenAbuseActions.values());
65
+ }
66
+
67
+
68
+ public static enum IpBlocklistForUserCreationAbuseActions implements Action {
69
+ GET, UNLOCK;
70
+
71
+ private static final Action[] ALL_ACTIONS = DefaultActions.plus(IpBlocklistForUserCreationAbuseActions.values());
72
+ }
73
+
55 74
public static enum EventActions implements Action {
56 75
UPLOAD_MEDIA
57 76
}
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/IPBlocklistTableWrapper.java
... ...
@@ -0,0 +1,151 @@
1
+package com.sap.sailing.gwt.ui.adminconsole;
2
+
3
+import java.util.ArrayList;
4
+import java.util.Comparator;
5
+import java.util.HashMap;
6
+import java.util.List;
7
+import java.util.Map.Entry;
8
+
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.Command;
12
+import com.google.gwt.user.client.rpc.AsyncCallback;
13
+import com.google.gwt.user.client.ui.Button;
14
+import com.google.gwt.user.client.ui.CheckBox;
15
+import com.google.gwt.user.client.ui.Label;
16
+import com.sap.sailing.gwt.ui.client.SailingServiceWriteAsync;
17
+import com.sap.sailing.gwt.ui.client.StringMessages;
18
+import com.sap.sse.common.TimedLock;
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;
24
+import com.sap.sse.security.ui.client.UserService;
25
+import com.sap.sse.security.ui.client.component.AccessControlledButtonPanel;
26
+
27
+abstract class IPBlocklistTableWrapper
28
+ extends TableWrapper<IpToTimedLockDTO, RefreshableSelectionModel<IpToTimedLockDTO>> {
29
+ private final UserService userService;
30
+ private final LabeledAbstractFilterablePanel<IpToTimedLockDTO> filterField;
31
+ private final HasPermissions securedDomainType;
32
+ private final String errorMessageOnDataFailureString;
33
+
34
+ protected abstract void fetchData(AsyncCallback<HashMap<String, TimedLock>> callback);
35
+
36
+ protected abstract void unlockIP(String ip, AsyncCallback<Void> asyncCallback);
37
+
38
+ public IPBlocklistTableWrapper(final SailingServiceWriteAsync sailingServiceWrite, final UserService userService,
39
+ final HasPermissions securedDomainType, final String errorMessageOnDataFailureString,
40
+ final StringMessages stringMessages, final ErrorReporter errorReporter) {
41
+ super(sailingServiceWrite, stringMessages, errorReporter, true, true,
42
+ new EntityIdentityComparator<IpToTimedLockDTO>() {
43
+ @Override
44
+ public boolean representSameEntity(IpToTimedLockDTO dto1, IpToTimedLockDTO dto2) {
45
+ return dto1.ip.equals(dto2.ip);
46
+ }
47
+
48
+ @Override
49
+ public int hashCode(IpToTimedLockDTO t) {
50
+ return t.ip.hashCode();
51
+ }
52
+ });
53
+ this.securedDomainType = securedDomainType;
54
+ this.userService = userService;
55
+ this.errorMessageOnDataFailureString = errorMessageOnDataFailureString;
56
+ this.asWidget().ensureDebugId("wrappedTable");
57
+ this.table.ensureDebugId("cellTable");
58
+ filterField = composeFilterField();
59
+ mainPanel.insert(filterField.asWidget(), 0);
60
+ mainPanel.insert(composeButtonPanel(), 1);
61
+ configureColumns();
62
+ loadDataAndPopulateTable();
63
+ }
64
+
65
+ private AccessControlledButtonPanel composeButtonPanel() {
66
+ final AccessControlledButtonPanel buttonPanel = new AccessControlledButtonPanel(userService, securedDomainType);
67
+ final Button refreshbutton = buttonPanel.addAction(getStringMessages().refresh(), () -> true, new Command() {
68
+ @Override
69
+ public void execute() {
70
+ loadDataAndPopulateTable();
71
+ }
72
+ });
73
+ refreshbutton.ensureDebugId("refreshButton");
74
+ final Button unlockbutton = buttonPanel.addAction(getStringMessages().unlock(), () -> true, new Command() {
75
+ @Override
76
+ public void execute() {
77
+ for (IpToTimedLockDTO e : getSelectionModel().getSelectedSet()) {
78
+ unlockIP(e.ip, new AsyncCallback<Void>() {
79
+ @Override
80
+ public void onFailure(Throwable caught) {
81
+ errorReporter.reportError(errorMessageOnDataFailureString);
82
+ }
83
+
84
+ @Override
85
+ public void onSuccess(Void result) {
86
+ filterField.remove(e);
87
+ }
88
+ });
89
+ }
90
+ }
91
+ });
92
+ unlockbutton.ensureDebugId("unlockButton");
93
+ return buttonPanel;
94
+ }
95
+
96
+ private void loadDataAndPopulateTable() {
97
+ final AsyncCallback<HashMap<String, TimedLock>> dataInitializationCallback = new AsyncCallback<HashMap<String, TimedLock>>() {
98
+ @Override
99
+ public void onFailure(Throwable caught) {
100
+ errorReporter.reportError(errorMessageOnDataFailureString);
101
+ }
102
+
103
+ @Override
104
+ public void onSuccess(HashMap<String, TimedLock> result) {
105
+ filterField.clear();
106
+ clear();
107
+ final ArrayList<IpToTimedLockDTO> iterable = new ArrayList<IpToTimedLockDTO>();
108
+ for (Entry<String, TimedLock> e : result.entrySet()) {
109
+ if (e.getValue().isLocked()) {
110
+ iterable.add(new IpToTimedLockDTO(e.getKey(), e.getValue()));
111
+ }
112
+ }
113
+ filterField.addAll(iterable);
114
+ }
115
+ };
116
+ fetchData(dataInitializationCallback);
117
+ }
118
+
119
+ private void configureColumns() {
120
+ final ListHandler<IpToTimedLockDTO> columnListHandler = getColumnSortHandler();
121
+ addColumn(record -> record.ip, getStringMessages().ipAddress());
122
+ final Comparator<IpToTimedLockDTO> expiryComparator = (o1, o2) -> {
123
+ return o1.timedLock.getLockedUntil().compareTo(o2.timedLock.getLockedUntil());
124
+ };
125
+ addColumn(record -> record.timedLock.getLockedUntil().toString(), getStringMessages().lockedUntil(),
126
+ expiryComparator);
127
+ table.addColumnSortHandler(columnListHandler);
128
+ }
129
+
130
+ private LabeledAbstractFilterablePanel<IpToTimedLockDTO> composeFilterField() {
131
+ final LabeledAbstractFilterablePanel<IpToTimedLockDTO> filterField = new LabeledAbstractFilterablePanel<IpToTimedLockDTO>(
132
+ new Label(getStringMessages().filterIpAddresses()), new ArrayList<>(), getDataProvider(),
133
+ getStringMessages()) {
134
+ @Override
135
+ public Iterable<String> getSearchableStrings(IpToTimedLockDTO dto) {
136
+ List<String> string = new ArrayList<String>();
137
+ string.add(dto.ip);
138
+ return string;
139
+ }
140
+
141
+ @Override
142
+ public AbstractCellTable<IpToTimedLockDTO> getCellTable() {
143
+ return table;
144
+ }
145
+ };
146
+ final CheckBox filterCheckbox = new CheckBox(getStringMessages().filterIpAddresses());
147
+ filterCheckbox.addValueChangeHandler(checked -> filterField.filter());
148
+ registerSelectionModelOnNewDataProvider(filterField.getAllListDataProvider());
149
+ return filterField;
150
+ }
151
+}
... ...
\ No newline at end of file
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/IpToTimedLockDTO.java
... ...
@@ -0,0 +1,14 @@
1
+package com.sap.sailing.gwt.ui.adminconsole;
2
+
3
+import com.sap.sse.common.TimedLock;
4
+
5
+public class IpToTimedLockDTO {
6
+ public final String ip;
7
+ public final TimedLock timedLock;
8
+
9
+ public IpToTimedLockDTO(final String ip, final TimedLock timedLock) {
10
+ this.ip = ip;
11
+ this.timedLock = timedLock;
12
+ }
13
+
14
+}
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LocalServerManagementPanel.java
... ...
@@ -5,6 +5,7 @@ import static com.sap.sse.security.shared.HasPermissions.DefaultActions.CHANGE_O
5 5
6 6
import java.util.ArrayList;
7 7
import java.util.Collections;
8
+import java.util.HashMap;
8 9
import java.util.function.Consumer;
9 10
import java.util.function.Predicate;
10 11
... ...
@@ -24,12 +25,14 @@ import com.google.gwt.user.client.ui.SimplePanel;
24 25
import com.google.gwt.user.client.ui.SuggestBox;
25 26
import com.google.gwt.user.client.ui.VerticalPanel;
26 27
import com.google.gwt.user.client.ui.Widget;
28
+import com.sap.sailing.domain.common.security.SecuredDomainType;
27 29
import com.sap.sailing.gwt.ui.adminconsole.places.AdminConsoleView.Presenter;
28 30
import com.sap.sailing.gwt.ui.adminconsole.places.advanced.UserGroupManagementPlace;
29 31
import com.sap.sailing.gwt.ui.adminconsole.places.advanced.UserManagementPlace;
30 32
import com.sap.sailing.gwt.ui.client.SailingServiceWriteAsync;
31 33
import com.sap.sailing.gwt.ui.client.StringMessages;
32 34
import com.sap.sailing.gwt.ui.shared.ServerConfigurationDTO;
35
+import com.sap.sse.common.TimedLock;
33 36
import com.sap.sse.common.Util;
34 37
import com.sap.sse.common.Util.Pair;
35 38
import com.sap.sse.common.http.HttpHeaderUtil;
... ...
@@ -86,7 +89,8 @@ public class LocalServerManagementPanel extends SimplePanel {
86 89
mainPanel.add(this.buttonPanel = createServerActionsUi(userService));
87 90
mainPanel.add(createServerInfoUI());
88 91
mainPanel.add(createServerConfigurationUI());
89
- mainPanel.add(createIPsLockedForBearerTokenAbuseUI());
92
+ mainPanel.add(createBearerTokenAbusePanel());
93
+ mainPanel.add(createUserCreationAbusePanel());
90 94
refreshServerConfiguration();
91 95
if (userService.hasServerPermission(ServerActions.CONFIGURE_CORS_FILTER)) {
92 96
mainPanel.add(createCORSFilterConfigurationUI());
... ...
@@ -144,9 +148,44 @@ public class LocalServerManagementPanel extends SimplePanel {
144 148
return captionPanel;
145 149
}
146 150
147
- private Widget createIPsLockedForBearerTokenAbuseUI() {
148
- final ServerDataCaptionPanel captionPanel = new ServerDataCaptionPanel(stringMessages.ipsLockedForBearerTokenAbuse(), 3);
149
- return captionPanel;
151
+ private Widget createBearerTokenAbusePanel() {
152
+ final ServerDataCaptionPanel panel = new ServerDataCaptionPanel(stringMessages.ipsLockedForBearerTokenAbuse(), 3);
153
+ panel.ensureDebugId("bearerTokenAbusePanel");
154
+ final IPBlocklistTableWrapper table = new IPBlocklistTableWrapper(sailingService, userService,
155
+ SecuredDomainType.IP_BLOCKLIST_FOR_BEARER_TOKEN_ABUSE,
156
+ stringMessages.unableToLoadIpsBlockedForBearerTokenAbuse(), stringMessages, errorReporter) {
157
+ @Override
158
+ protected void fetchData(AsyncCallback<HashMap<String, TimedLock>> callback) {
159
+ sailingServiceWrite.getClientIPBasedTimedLocksForBearerTokenAbuse(callback);
160
+ }
161
+
162
+ @Override
163
+ protected void unlockIP(String ip, AsyncCallback<Void> asyncCallback) {
164
+ sailingService.releaseBearerTokenLockOnIp(ip, asyncCallback);
165
+ }
166
+ };
167
+ panel.setContentWidget(table.asWidget());
168
+ return panel;
169
+ }
170
+
171
+ private Widget createUserCreationAbusePanel() {
172
+ final ServerDataCaptionPanel panel = new ServerDataCaptionPanel(stringMessages.ipsLockedForUserCreationAbuse(), 3);
173
+ panel.ensureDebugId("userCreationAbusePanel");
174
+ final IPBlocklistTableWrapper table = new IPBlocklistTableWrapper(sailingService, userService,
175
+ SecuredDomainType.IP_BLOCKLIST_FOR_USER_CREATION_ABUSE,
176
+ stringMessages.unableToLoadIpsBlockedForUserCreationAbuse(), stringMessages, errorReporter) {
177
+ @Override
178
+ protected void fetchData(AsyncCallback<HashMap<String, TimedLock>> callback) {
179
+ sailingServiceWrite.getClientIPBasedTimedLocksForUserCreation(callback);
180
+ }
181
+
182
+ @Override
183
+ protected void unlockIP(String ip, AsyncCallback<Void> asyncCallback) {
184
+ sailingService.releaseUserCreationLockOnIp(ip, asyncCallback);
185
+ }
186
+ };
187
+ panel.setContentWidget(table.asWidget());
188
+ return panel;
150 189
}
151 190
152 191
private Widget createCORSFilterConfigurationUI() {
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java
... ...
@@ -2562,4 +2562,10 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages,
2562 2562
String successfullyCopiedPairings();
2563 2563
String selectFromRaceColumn();
2564 2564
String selectToRaceColumn();
2565
+ String unableToLoadIpsBlockedForBearerTokenAbuse();
2566
+ String ipAddress();
2567
+ String filterIpAddresses();
2568
+ String unlock();
2569
+ String ipsLockedForUserCreationAbuse();
2570
+ String unableToLoadIpsBlockedForUserCreationAbuse();
2565 2571
}
... ...
\ No newline at end of file
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties
... ...
@@ -2599,4 +2599,10 @@ selectRaceColumnsWhosePairingsToCopy=Race columns whose pairings to copy
2599 2599
errorCopyingPairings=Error copying pairings: {0}
2600 2600
successfullyCopiedPairings=Successfully copied pairings
2601 2601
selectFromRaceColumn=Select race column from where to start copying pairings
2602
-selectToRaceColumn=Select race colunm to where to copy pairings
... ...
\ No newline at end of file
0
+selectToRaceColumn=Select race colunm to where to copy pairings
1
+unableToLoadIpsBlockedForBearerTokenAbuse=Unable to load IPs blocked for bearer token abuse
2
+ipAddress=IP Address
3
+filterIpAddresses=Filter IP Addresses
4
+unlock=Unlock
5
+ipsLockedForUserCreationAbuse=IPs Locked for User Creation Abuse
6
+unableToLoadIpsBlockedForUserCreationAbuse=Unable to load IPs Blocked for User Creation Abuse
... ...
\ No newline at end of file
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties
... ...
@@ -2593,4 +2593,10 @@ errorCopyingPairings=Fehler beim Kopieren der Zuordnungen: {0}
2593 2593
successfullyCopiedPairings=Zuordnungen erfolgreich kopiert
2594 2594
selectFromRaceColumn=Spalte auswählen, ab der die Zuordnungen kopiert werden sollen
2595 2595
selectToRaceColumn=Spalte auswählen, bis zu der die Zuordnungen kopiert werden sollen
2596
-ipsLockedForBearerTokenAbuse=IPs wegen Missbrauchs von Bearer-Token gesperrt
... ...
\ No newline at end of file
0
+unableToLoadIpsBlockedForBearerTokenAbuse=Aufgrund von Missbrauch des Inhabertokens blockierte IPs können nicht geladen werden
1
+ipsLockedForBearerTokenAbuse=IPs wegen Missbrauchs von Bearer-Token gesperrt
2
+ipAddress=IP-Addresse
3
+filterIpAddresses=IP-Adressen filtern
4
+unlock=Entsperren
5
+ipsLockedForUserCreationAbuse=Wegen Missbrauchs bei der Benutzererstellung gesperrte IPs
6
+unableToLoadIpsBlockedForUserCreationAbuse=Wegen Missbrauchs der Benutzererstellung blockierte IPs können nicht geladen werden
... ...
\ No newline at end of file
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/pages/adminconsole/advanced/IpBlocklistPanelPO.java
... ...
@@ -0,0 +1,78 @@
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 TablePO extends CellTablePO<IPLockEntry> {
14
+ public TablePO(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 = driver.findElement(new BySeleniumId("cellTable"));
50
+ this.cellTable = new TablePO(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 TablePO cellTable;
60
+
61
+ public void refresh() {
62
+ refreshButton.click();
63
+ }
64
+
65
+ /**
66
+ * @return true if IP was found, false if not found
67
+ */
68
+ public boolean expectIpInTable(final String ip) {
69
+ final IPLockEntry entry = cellTable.getEntry(ip);
70
+ final boolean wasFound = entry != null;
71
+ return wasFound;
72
+ }
73
+
74
+ public void unblockIP(String ip) {
75
+ cellTable.getEntry(ip).select();
76
+ unlockButton.click();
77
+ }
78
+}
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/test/adminconsole/TestIpAbuse.java
... ...
@@ -1,32 +0,0 @@
1
-package com.sap.sailing.selenium.test.adminconsole;
2
-
3
-import static org.junit.jupiter.api.Assertions.assertTrue;
4
-
5
-import org.junit.jupiter.api.BeforeEach;
6
-import org.junit.jupiter.api.Test;
7
-
8
-import com.sap.sailing.selenium.core.SeleniumTestCase;
9
-import com.sap.sailing.selenium.pages.adminconsole.AdminConsolePage;
10
-import com.sap.sailing.selenium.pages.adminconsole.advanced.LocalServerPO;
11
-import com.sap.sailing.selenium.test.AbstractSeleniumTest;
12
-
13
-public class TestIpAbuse extends AbstractSeleniumTest {
14
- @Override
15
- @BeforeEach
16
- public void setUp() {
17
- clearState(getContextRoot());
18
- super.setUp();
19
- }
20
-
21
- @SeleniumTestCase
22
- public void testUnlockIpBannedForBearerTokenAbuse() {
23
- final AdminConsolePage adminConsole = AdminConsolePage.goToPage(getWebDriver(), getContextRoot());
24
- final LocalServerPO localServerPanel = adminConsole.goToLocalServerPanel();
25
- assertTrue(true);
26
- }
27
-
28
- @Test
29
- public void testTest() {
30
- assertTrue(true);
31
- }
32
-}
java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/test/adminconsole/TestIpLocking.java
... ...
@@ -0,0 +1,106 @@
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.test.AbstractSeleniumTest;
19
+
20
+public class TestIpLocking extends AbstractSeleniumTest {
21
+ @Override
22
+ @BeforeEach
23
+ public void setUp() {
24
+ clearState(getContextRoot());
25
+ super.setUp();
26
+ }
27
+
28
+ @SeleniumTestCase
29
+ public void testUnlockingForBearerTokenAbuser() throws InterruptedException {
30
+ final AdminConsolePage adminConsole = AdminConsolePage.goToPage(getWebDriver(), getContextRoot());
31
+ final IpBlocklistPanelPO tablePO = adminConsole.goToLocalServerPanel().getBearerTokenAbusePO();
32
+ attemptBearerTokenAbuse(5);
33
+ tablePO.refresh();
34
+ final String ip = "127.0.0.1";
35
+ assertTrue(tablePO.expectIpInTable(ip));
36
+ tablePO.unblockIP(ip);
37
+ assertFalse(tablePO.expectIpInTable(ip));
38
+ attemptValidBearerTokenUse();
39
+ }
40
+
41
+ private void attemptValidBearerTokenUse() {
42
+ // prepare api
43
+ final ApiContext ctx = ApiContext.createAdminApiContext(getContextRoot(), ApiContext.SECURITY_CONTEXT);
44
+ final Map<String, String> prefObjectAttr = new HashMap<String, String>();
45
+ prefObjectAttr.put("key1", "value1");
46
+ PreferencesApi preferencesApi = new PreferencesApi();
47
+ preferencesApi.createPreference(ctx, "pref1", prefObjectAttr);
48
+ }
49
+
50
+ private void attemptBearerTokenAbuse(final int attempts) throws InterruptedException {
51
+ // prepare api
52
+ final ApiContext wrongCtx = ApiContext.createApiContextWithInvalidToken(getContextRoot(),
53
+ ApiContext.SECURITY_CONTEXT);
54
+ final Map<String, String> prefObjectAttr = new HashMap<String, String>();
55
+ prefObjectAttr.put("key1", "value1");
56
+ final PreferencesApi preferencesApi = new PreferencesApi();
57
+ for (int i = 0; i < attempts; i++) {
58
+ // call api
59
+ try {
60
+ preferencesApi.createPreference(wrongCtx, "pref1", prefObjectAttr);
61
+ } catch (Unauthorized e) {
62
+ // do nothing as this is expected
63
+ }
64
+ // wait for lock to expire
65
+ long lockDuration = (long) Math.pow(2, i) * 1000;
66
+ boolean isFinalAttempt = i == (attempts - 1);
67
+ if (!isFinalAttempt) {
68
+ Thread.sleep(lockDuration);
69
+ }
70
+ }
71
+ }
72
+
73
+ @SeleniumTestCase
74
+ public void testUnlockingForUserCreationAbuser() throws InterruptedException {
75
+ final AdminConsolePage adminConsole = AdminConsolePage.goToPage(getWebDriver(), getContextRoot());
76
+ final IpBlocklistPanelPO tablePO = adminConsole.goToLocalServerPanel().getUserCreationAbusePO();
77
+ attemptUserCreationAbuse(5);
78
+ tablePO.refresh();
79
+ final String ip = "127.0.0.1";
80
+ assertTrue(tablePO.expectIpInTable(ip));
81
+ tablePO.unblockIP(ip);
82
+ assertFalse(tablePO.expectIpInTable(ip));
83
+ attemptValidBearerTokenUse();
84
+ }
85
+
86
+ private void attemptUserCreationAbuse(final int attempts) throws InterruptedException {
87
+ for (int i = 0; i < attempts; i++) {
88
+ attemptUserCreation(String.valueOf(i));
89
+ // wait for lock to expire
90
+ long lockDuration = (long) Math.pow(2, i) * 1000;
91
+ boolean isFinalAttempt = i == (attempts - 1);
92
+ if (!isFinalAttempt) {
93
+ Thread.sleep(lockDuration);
94
+ }
95
+ }
96
+ }
97
+
98
+ private boolean attemptUserCreation(String seed) {
99
+ try {
100
+ SecurityApi.createUser("USERNAME" + seed, "PASSWORD").run();
101
+ return true;
102
+ } catch (Exception e) {
103
+ return false;
104
+ }
105
+ }
106
+}
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;
... ...
@@ -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/src/com/sap/sse/security/SecurityService.java
... ...
@@ -918,12 +918,15 @@ public interface SecurityService extends ReplicableWithObjectInputStream<Replica
918 918
* Used in conjunction with {@link #failedBearerTokenAuthentication(String)} and
919 919
* {@link #successfulBearerTokenAuthentication(String)}. If and only if a locking state for the combination
920 920
* of {@code clientIP} and {@code userAgent} is known and still locked, {@code true} is returned. Unlocking
921
- * will bappen by calling {@link #successfulBearerTokenAuthentication(String)} with an equal combination
922
- * of {@code clientIP} and {@code userAgent}. Invoking {@link #failedBearerTokenAuthentication(String)}
923
- * 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.
924 925
*/
925 926
boolean isClientIPLockedForBearerTokenAuthentication(String clientIP);
926 927
928
+ boolean isUserCreationLockedForClientIP(String clientIP);
929
+
927 930
void fileTakedownNotice(TakedownNoticeRequestContext takedownNoticeRequestContext) throws MailException;
928 931
929 932
/**
... ...
@@ -941,8 +944,8 @@ public interface SecurityService extends ReplicableWithObjectInputStream<Replica
941 944
*/
942 945
Iterable<User> getUsersToInformAboutReplicaSet(String serverName,
943 946
Optional<com.sap.sse.security.shared.HasPermissions.Action> alsoSendToAllUsersWithThisPermissionOnReplicaSet);
944
-
947
+
945 948
HashMap<String, TimedLock> getClientIPBasedTimedLocksForUserCreation();
946
-
949
+
947 950
HashMap<String, TimedLock> getClientIPBasedTimedLocksForBearerTokenAbuse();
948 951
}
java/com.sap.sse.security/src/com/sap/sse/security/impl/SecurityServiceImpl.java
... ...
@@ -1006,7 +1006,7 @@ implements ReplicableSecurityService, ClearStateTestSupport {
1006 1006
1007 1007
@Override
1008 1008
public void releaseBearerTokenLockOnIp(String ip) {
1009
- logger.info("Releasing timed lock for user creation at IP "+ip);
1009
+ logger.info("Releasing timed lock for bearer token abuse at IP "+ip);
1010 1010
apply(new ReleaseBearerTokenLockOnIpOperation(ip));
1011 1011
}
1012 1012
... ...
@@ -1192,6 +1192,12 @@ implements ReplicableSecurityService, ClearStateTestSupport {
1192 1192
}
1193 1193
1194 1194
@Override
1195
+ public boolean isUserCreationLockedForClientIP(String clientIP) {
1196
+ final TimedLock timedLock = clientIPBasedTimedLocksForUserCreation.get(escapeNullClientIP(clientIP));
1197
+ return timedLock != null && timedLock.isLocked();
1198
+ }
1199
+
1200
+ @Override
1195 1201
public TimedLock internalRecordUserCreationFromClientIP(String clientIP) {
1196 1202
final TimedLock result = new TimedLockImpl(TimePoint.now().plus(DEFAULT_CLIENT_IP_BASED_USER_CREATION_LOCKING_DURATION),
1197 1203
DEFAULT_CLIENT_IP_BASED_USER_CREATION_LOCKING_DURATION);