Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update manifest and endpoints for the V3 of the appsec login events #3730

Merged
merged 2 commits into from
Jan 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 62 additions & 8 deletions manifests/java.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1030,24 +1030,78 @@ tests/:
Test_V2_Login_Events_Anon: irrelevant (v1.38.0, replaced by V3)
Test_V2_Login_Events_RC: irrelevant (v1.38.0, replaced by V3)
Test_V3_Auto_User_Instrum_Mode_Capability:
'*': missing_feature
spring-boot-3-native: flaky (APMAPI-979)
'*': v1.45.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
Test_V3_Login_Events:
'*': missing_feature
'*': v1.45.0
akka-http: missing_feature (login endpoints not implemented)
jersey-grizzly2: missing_feature (login endpoints not implemented)
play: missing_feature (login endpoints not implemented)
ratpack: missing_feature (login endpoints not implemented)
resteasy-netty3: missing_feature (login endpoints not implemented)
spring-boot-3-native: flaky (APMAPI-979)
spring-boot-openliberty: missing_feature (weblog returns error 500)
vertx3: missing_feature (login endpoints not implemented)
vertx4: missing_feature (login endpoints not implemented)
Test_V3_Login_Events_Anon:
'*': missing_feature
'*': v1.45.0
akka-http: missing_feature (login endpoints not implemented)
jersey-grizzly2: missing_feature (login endpoints not implemented)
play: missing_feature (login endpoints not implemented)
ratpack: missing_feature (login endpoints not implemented)
resteasy-netty3: missing_feature (login endpoints not implemented)
spring-boot-3-native: flaky (APMAPI-979)
spring-boot-openliberty: missing_feature (weblog returns error 500)
vertx3: missing_feature (login endpoints not implemented)
vertx4: missing_feature (login endpoints not implemented)
Test_V3_Login_Events_Blocking:
'*': missing_feature
'*': v1.45.0
akka-http: missing_feature (login endpoints not implemented)
jersey-grizzly2: missing_feature (login endpoints not implemented)
play: missing_feature (login endpoints not implemented)
ratpack: missing_feature (login endpoints not implemented)
resteasy-netty3: missing_feature (login endpoints not implemented)
spring-boot-3-native: flaky (APMAPI-979)
spring-boot-openliberty: missing_feature (weblog returns error 500)
spring-boot-payara: bug (APPSEC-54985)
vertx3: missing_feature (login endpoints not implemented)
vertx4: missing_feature (login endpoints not implemented)
Test_V3_Login_Events_RC:
'*': missing_feature
'*': v1.45.0
akka-http: missing_feature (login endpoints not implemented)
jersey-grizzly2: missing_feature (login endpoints not implemented)
play: missing_feature (login endpoints not implemented)
ratpack: missing_feature (login endpoints not implemented)
resteasy-netty3: missing_feature (login endpoints not implemented)
spring-boot-3-native: flaky (APMAPI-979)
spring-boot-openliberty: missing_feature (weblog returns error 500)
vertx3: missing_feature (login endpoints not implemented)
vertx4: missing_feature (login endpoints not implemented)
test_automated_user_and_session_tracking.py:
Test_Automated_Session_Blocking: missing_feature
Test_Automated_User_Blocking: missing_feature
Test_Automated_User_Tracking: missing_feature
Test_Automated_User_Blocking:
'*': v1.45.0
akka-http: missing_feature (login endpoints not implemented)
jersey-grizzly2: missing_feature (login endpoints not implemented)
play: missing_feature (login endpoints not implemented)
ratpack: missing_feature (login endpoints not implemented)
resteasy-netty3: missing_feature (login endpoints not implemented)
spring-boot-3-native: flaky (APMAPI-979)
spring-boot-openliberty: missing_feature (weblog returns error 500)
spring-boot-payara: bug (APPSEC-54985)
vertx3: missing_feature (login endpoints not implemented)
vertx4: missing_feature (login endpoints not implemented)
Test_Automated_User_Tracking:
'*': v1.45.0
akka-http: missing_feature (login endpoints not implemented)
jersey-grizzly2: missing_feature (login endpoints not implemented)
play: missing_feature (login endpoints not implemented)
ratpack: missing_feature (login endpoints not implemented)
resteasy-netty3: missing_feature (login endpoints not implemented)
spring-boot-3-native: flaky (APMAPI-979)
spring-boot-openliberty: missing_feature (weblog returns error 500)
vertx3: missing_feature (login endpoints not implemented)
vertx4: missing_feature (login endpoints not implemented)
test_blocking_addresses.py:
Test_BlockingGraphqlResolvers: missing_feature
Test_Blocking_client_ip:
Expand Down
11 changes: 6 additions & 5 deletions tests/appsec/test_automated_login_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -1221,9 +1221,9 @@ def validate_iden(meta):
self._assert_response(self.tests[2], validate_anon)


libs_without_user_id = []
libs_without_user_exist = ["nodejs"]
libs_without_user_id_on_failure = ["nodejs"]
libs_without_user_id = ["java"]
libs_without_user_exist = ["nodejs", "java"]
libs_without_user_id_on_failure = ["nodejs", "java"]


@rfc("https://docs.google.com/document/d/1RT38U6dTTcB-8muiYV4-aVDCsT_XrliyakjtAPyjUpw")
Expand Down Expand Up @@ -1932,8 +1932,9 @@ def test_login_event_blocking_auto_id(self):

assert self.config_state_2[rc.RC_STATE] == rc.ApplyState.ACKNOWLEDGED
assert self.config_state_3[rc.RC_STATE] == rc.ApplyState.ACKNOWLEDGED
interfaces.library.assert_waf_attack(self.r_login_blocked, rule="block-user-id")
assert self.r_login_blocked.status_code == 403
if context.library not in libs_without_user_id:
interfaces.library.assert_waf_attack(self.r_login_blocked, rule="block-user-id")
assert self.r_login_blocked.status_code == 403

def setup_login_event_blocking_auto_login(self):
rc.rc_state.reset().apply()
Expand Down
23 changes: 19 additions & 4 deletions tests/appsec/test_automated_user_and_session_tracking.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
UUID_USER = "testuuid"
PASSWORD = "1234"

libs_without_user_id = ["java"]


def login_data(context, user, password):
"""In Rails the parameters are group by scope. In the case of the test the scope is user.
Expand All @@ -55,8 +57,13 @@ def test_user_tracking_auto(self):
assert self.r_home.status_code == 200
for _, _, span in interfaces.library.get_spans(request=self.r_home):
meta = span.get("meta", {})
assert meta["usr.id"] == "social-security-id"
assert meta["_dd.appsec.usr.id"] == "social-security-id"
if context.library in libs_without_user_id:
assert meta["usr.id"] == USER
assert meta["_dd.appsec.usr.id"] == USER
else:
assert meta["usr.id"] == "social-security-id"
assert meta["_dd.appsec.usr.id"] == "social-security-id"

assert meta["_dd.appsec.user.collection_mode"] == "identification"

def setup_user_tracking_sdk_overwrite(self):
Expand All @@ -69,7 +76,11 @@ def test_user_tracking_sdk_overwrite(self):
for _, _, span in interfaces.library.get_spans(request=self.r_login):
meta = span.get("meta", {})
assert meta["usr.id"] == "sdkUser"
assert meta["_dd.appsec.usr.id"] == "social-security-id"
if context.library in libs_without_user_id:
assert meta["_dd.appsec.usr.id"] == USER
else:
assert meta["_dd.appsec.usr.id"] == "social-security-id"

assert meta["_dd.appsec.user.collection_mode"] == "sdk"


Expand Down Expand Up @@ -108,7 +119,11 @@ def test_user_tracking_sdk_overwrite(self):
{
"id": "blocked_users",
"type": "data_with_expiration",
"data": [{"value": "social-security-id", "expiration": 0}, {"value": "sdkUser", "expiration": 0}],
"data": [
{"value": "test", "expiration": 0},
{"value": "social-security-id", "expiration": 0},
{"value": "sdkUser", "expiration": 0},
],
},
],
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.datadoghq.system_tests.springboot.security.AppSecAuthenticationFilter;
import com.datadoghq.system_tests.springboot.security.AppSecAuthenticationProvider;
import com.datadoghq.system_tests.springboot.security.AppSecUserDetailsManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
Expand All @@ -12,6 +13,7 @@
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.provisioning.UserDetailsManager;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

Expand All @@ -25,7 +27,12 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter{

@Bean
public AuthenticationManager authenticationManager() throws Exception {
return new ProviderManager(new AppSecAuthenticationProvider());
return new ProviderManager(new AppSecAuthenticationProvider(userDetailsManager()));
}

@Bean
public UserDetailsManager userDetailsManager() {
return new AppSecUserDetailsManager();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@ public Authentication attemptAuthentication(HttpServletRequest request, HttpServ
if (sdkEvent != null) {
String sdkUser = request.getParameter("sdk_user");
boolean sdkUserExists = Boolean.parseBoolean(request.getParameter("sdk_user_exists"));
authentication = new AppSecSdkToken(username, password, sdkEvent, sdkUser, sdkUserExists);
authentication = new AppSecToken(username, password, sdkEvent, sdkUser, sdkUserExists);
} else {
authentication = new AppSecSdkToken(username, password);
authentication = new AppSecToken(username, password);
}
return this.getAuthenticationManager().authenticate(authentication);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,60 +6,57 @@
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.provisioning.UserDetailsManager;

import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public class AppSecAuthenticationProvider implements AuthenticationProvider {

private static final Map<String, AppSecUser> USERS = new HashMap<>();
private final UserDetailsManager userDetailsManager;

static {
Arrays.asList(
new AppSecUser("social-security-id", "test", "1234", "[email protected]"),
new AppSecUser("591dc126-8431-4d0f-9509-b23318d3dce4", "testuuid", "1234", "[email protected]")
).forEach(user -> USERS.put(user.getUsername(), user));
public AppSecAuthenticationProvider(final UserDetailsManager userDetailsManager) {
this.userDetailsManager = userDetailsManager;
}

@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
AppSecSdkToken token = (AppSecSdkToken) authentication;
AppSecToken token = (AppSecToken) authentication;
if (token.getSdkEvent() == null) {
return loginUserPassword(token);
} else {
return loginSdk(token);
}
}

private Authentication loginUserPassword(final AppSecSdkToken auth) {
private Authentication loginUserPassword(final AppSecToken auth) {
String username = auth.getName();
if (!USERS.containsKey(username)) {
if (!userDetailsManager.userExists(username)) {
throw new UsernameNotFoundException(username);
}
final AppSecUser user = USERS.get(username);
final AppSecUser user = (AppSecUser) userDetailsManager.loadUserByUsername(username);
if (!user.getPassword().equals(auth.getCredentials())) {
throw new BadCredentialsException(username);
}
return new AppSecSdkToken(new AppSecUser(user), auth.getCredentials(), Collections.emptyList());
return new AppSecToken(new AppSecUser(user), auth.getCredentials(), Collections.emptyList());
}

private Authentication loginSdk(final AppSecSdkToken auth) {
String username = auth.getSdkUser();
private Authentication loginSdk(final AppSecToken auth) {
Map<String, String> metadata = new HashMap<>();
EventTracker tracker = GlobalTracer.getEventTracker();
switch (auth.getSdkEvent()) {
case "success":
tracker.trackLoginSuccessEvent(username, metadata);
return new AppSecSdkToken(username, auth.getCredentials(), Collections.emptyList());
tracker.trackLoginSuccessEvent(auth.getSdkUser(), metadata);
return new AppSecToken(auth.getName(), auth.getCredentials(), Collections.emptyList());
case "failure":
tracker.trackLoginFailureEvent(username, auth.isSdkUserExists(), metadata);
tracker.trackLoginFailureEvent(auth.getSdkUser(), auth.isSdkUserExists(), metadata);
if (auth.isSdkUserExists()) {
throw new BadCredentialsException(username);
throw new BadCredentialsException(auth.getSdkUser());
} else {
throw new UsernameNotFoundException(username);
throw new UsernameNotFoundException(auth.getSdkUser());
}
default:
throw new IllegalArgumentException("Invalid SDK event: " + auth.getSdkEvent());
Expand All @@ -69,7 +66,7 @@ private Authentication loginSdk(final AppSecSdkToken auth) {

@Override
public boolean supports(Class<?> authentication) {
return AppSecSdkToken.class.isAssignableFrom(authentication);
return AppSecToken.class.isAssignableFrom(authentication);
}


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.datadoghq.system_tests.springboot.security;

import org.springframework.http.ResponseEntity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.provisioning.UserDetailsManager;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class AppSecSecurityController {

private final UserDetailsManager userDetailsManager;

public AppSecSecurityController(final UserDetailsManager userDetailsManager) {
this.userDetailsManager = userDetailsManager;
}

@PostMapping("/signup")
public ResponseEntity<String> signUp(@RequestParam String username, @RequestParam String password) {
userDetailsManager.createUser(User.withUsername(username).password(password).roles("USER").build());
return ResponseEntity.ok("Signup successful");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,43 +8,30 @@
/**
* Token used to bypass appsec auto user instrumentation when using the SDK
*/
public class AppSecSdkToken extends UsernamePasswordAuthenticationToken {
public class AppSecToken extends UsernamePasswordAuthenticationToken {

private String sdkEvent;

private String sdkUser;

private boolean sdkUserExists;

public AppSecSdkToken(Object principal, Object credentials) {
public AppSecToken(Object principal, Object credentials) {
this(principal, credentials, null, null, false);
}

public AppSecSdkToken(Object principal, Object credentials, String sdkEvent, String sdkUser, boolean sdkUserExists) {
public AppSecToken(Object principal, Object credentials, String sdkEvent, String sdkUser, boolean sdkUserExists) {
super(principal, credentials);
this.sdkEvent = sdkEvent;
this.sdkUser = sdkUser;
this.sdkUserExists = sdkUserExists;
}

public AppSecSdkToken(Object principal, Object credentials,
Collection<? extends GrantedAuthority> authorities) {
public AppSecToken(Object principal, Object credentials,
Collection<? extends GrantedAuthority> authorities) {
super(principal, credentials, authorities);
}

@Override
public String getName() {
if (sdkEvent != null) {
// report the provided username
return sdkUser;
} else if (getPrincipal() instanceof AppSecUser) {
// report the id instead of the username
return ((AppSecUser) getPrincipal()).getId();
} else {
return super.getName();
}
}

public String getSdkEvent() {
return sdkEvent;
}
Expand Down
Loading
Loading