I am trying to get a simulation going where each virtual user in Gatling has a unique login/auth and does a bunch of actions that I have recorded and coded up. But I'm struggling to setup the authorization properly. Here's my code
public class SampleSimulation extends Simulation {
static Random rand = new Random();
HttpProtocolBuilder httpProtocol = http
.baseUrl("http://base-url.com") // Here is the root for all relative URLs
.acceptHeader("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8") // Here are the common headers
.acceptEncodingHeader("gzip, deflate")
.acceptLanguageHeader("en-US,en;q=0.5")
.userAgentHeader("Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0");
// .basicAuth("username", "password");
static ActionBuilder addAuthentication(String name){
String username = "username"; // generate random username from valid ones
String pw = "password"; // generate random password that's valid for username
http.basicAuth(username, pw);
return http(name).get("/rest/activity/me");
}
static ActionBuilder getMe(String name){
return http(name)
.get("/rest/activity/me");
}
...
ScenarioBuilder scn = scenario("name")
.feed(Utils.getItemIdFeeder())
.exec(addAuthentication("n"))
.exec(getMe("n")).pause(Duration.ofMillis(300),Duration.ofMillis(750))
.exec(postUpdateLocale("n")).pause(Duration.ofMillis(0),Duration.ofMillis(4))
.exec(getBalance("n")).pause(Duration.ofMillis(500), Duration.ofMillis(2000))
.exec(getMe("n")).pause(Duration.ofMillis(0), Duration.ofMillis(15))
.exec(getInventory("n")).pause(Duration.ofMillis(100), Duration.ofMillis(250))
);
{
setUp(scn.injectOpen(nothingFor(4), // 1
atOnceUsers(10), // 2
rampUsers(10).during(5)) //, // 3
.protocols(httpProtocol));
}
}
So when I provide username and password in the static declaration (the .basicAuth line I have commented out) it works. However each virtual user created by gatling is the same user as far as the server is concerned. If I run the code that I have added here, I get http 401s for all of my requests, which is unauthorized.
Can anyone please explain to me what I'm doing wrong, what is the proper way of doing credentials in Gatling is? Note that I'm already using a feeder for itemIds.
Thank you!
http.basicAuth(username, pw);
return http(name).get("/rest/activity/me");
This does absolutely nothing as HttpProtocolBuilder is immutable.
I advice you invest some time to read the documentation and the Gatling Academy, you'll ultimately save a lot of time.
If you check basicAuth's documentation, you can see that it can also take Gatling EL expressions, so you could do something like:
.basicAuth("#{username}", "#{password}")
inject for each virtual user the desired key-value pairs, eg with a feeder or an exec block with a function
Related
From documentation for UnicodePwd:
If the Modify request contains a delete operation containing a value Vdel for unicodePwd followed by an add operation containing a value Vadd for unicodePwd, the server considers the request to be a request to change the password. The server decodes Vadd and Vdel using the password decoding procedure documented later in this section. Vdel is the old password, while Vadd is the new password.
If the Modify request contains a single replace operation containing a value Vrep for unicodePwd, the server considers the request to be an administrative reset of the password, that is, a password modification without knowledge of the old password. The server decodes Vrep using the password decoding procedure documented later in this section and uses it as the new password.
I have not yet found way to let a user do a "forget password -> confirm email -> set new password" procedure on a webserver that uses AD as the user store while verifying the password according to PwdHistoryLength's check for password reuse. How should I do it?
public class Test {
private static DirContext adminLdapContext;
public static void main(String[] args) throws Exception {
initLdapContext();
createNewUser("user.x");
assertThrows(() -> updateUserPasswordByAdmin("user.x", "X"), NamingException.class,
"X should fail MinPwdLength");
updateUserPasswordByAdmin("user.x", "good-Pwd$%^123");
updateUserPassword("user.x", "good-Pwd$%^123", "good-Pwd$%^1237");
updateUserPasswordByAdmin("user.x", "good-Pwd$%^1234");
updateUserPasswordByAdmin("user.x", "good-Pwd$%^12345");
updateUserPasswordByAdmin("user.x", "good-Pwd$%^123456");
updateUserPassword("user.x", "good-Pwd$%^123456", "good-Pwd$%^1234567");
assertThrows(() -> updateUserPassword("user.x", "good-Pwd$%^1234567", "good-Pwd$%^123456"),
NamingException.class, "the password with 123456 should be rejected for reuse");
assertThrows(() -> updateUserPasswordByAdmin("user.x", "good-Pwd$%^12345"), NamingException.class,
"the password with 12345 should be rejected for reuse");
}
// static void initLdapContext(): initialize adminLdapContext
// static void createNewUser(String uname): use adminLdapContext and create user with sAMAccountName = cn = uname
// static void updateUserPassword(String uname, String oldpass, String newpass): Use Vdel+Vadd on UnicodePwd
// static void updateUserPasswordByAdmin(String uname, String newpass): Use Vrep on UnicodePwd
// static void assertThrows(RunnableThrows r, Class<? extends Throwable> ex, String message) throws AssertionError: throw AssertionError if r.run() doesn't throw ex
// #FunctionalInterface static interface RunnableThrows: see https://stackoverflow.com/a/18198349/2185599
}
Output:
Exception in thread "main" java.lang.AssertionError: the password with 12345 should be rejected for reuse
at sketchbook.Main.assertThrows(Main.java:97)
at sketchbook.Main.main(Main.java:67)
Caused by: java.lang.AssertionError: r.run() didn't throw
at sketchbook.Main.assertThrows(Main.java:94)
... 1 more
Workaround:
Double the password history count and generate random UUID's to be set into AD.
Change Password: oldPassword --vdel vadd--> UUID --vdel vadd--> newPassword
Reset Password: oldPassword (unknown) --vrep--> UUID --vdel vadd--> newPassword
The downside is the the password history would contain half generated UUIDs.
Your question isn't entirely clear, but I if I understand correctly, you are expecting that this should not work:
updateUserPasswordByAdmin("user.x", "good-Pwd$%^12345")
I assume that updateUserPasswordByAdmin does an administrative reset rather than a change.
If that is the case, then that will always work, since password history requirements are not enforced when doing a reset. I suspect that is a security feature, because it would reveal past passwords to someone who didn't already know them.
And there is no way for you to get the list of old passwords.
The docs for cognito user pools can be found here:
http://docs.aws.amazon.com/cognito/latest/developerguide/how-to-manage-user-accounts.html
In this they do not say whether you can query users by the automatically generated sub attribute, which is a uuid. It explicitly says you can't search for users by custom attributes, but sub/uuid is not a custom attribute. Weirdly though, in the list of searchable attributes sub/uuid is not one of them. Surely though you can look up users by their UUID, how would this be done though??
You know, I have used COgnito but never needed to look up via sub (or other params other than the username). I looked into it because surely you can, but it is not very clear (like a lot of their documentation). Here is what I saw that you could try... hope it helps man.
// the imported ListUsersResult is...
import com.amazonaws.services.cognitoidp.model.ListUsersRequest;
import com.amazonaws.services.cognitoidp.model.ListUsersResult;
// class var
protected final AWSCognitoIdentityProviderClient identityUserPoolProviderClient;
// omitted stuff...
// initialize the Cognito Provider client. This is used to talk to the user pool
identityUserPoolProviderClient = new AWSCognitoIdentityProviderClient(new BasicAWSCredentials(AWS_ACCESS_KEY, AWS_SECRET_KEY)); // creds are loaded via variables that are supplied to my program dynamically
identityUserPoolProviderClient.setRegion(RegionUtils.getRegion(USER_POOL_REGION)); // var loaded
// ...some code omitted
ListUsersRequest listUsersRequest = new ListUsersRequest();
listUsersRequest.withUserPoolId(USER_POOL_ID); // id of the userpool, look this up in Cognito console
listUsersRequest.withFilter("sub=xyz"); // i THINK this is how the Filter works... the documentation is terribad
// get the results
ListUsersResult result = identityUserPoolProviderClient.listUsers(listUsersRequest);
List<UserType> userTypeList = result.getUsers();
// loop through them
for (UserType userType : userTypeList) {
List<AttributeType> attributeList = userType.getAttributes();
for (AttributeType attribute : attributeList) {
String attName = attribute.getName();
String attValue = attribute.getValue();
System.out.println(attName + ": " + attValue);
}
}
If you have the username you could get the user like this
// build the request
AdminGetUserRequest idRequest = new AdminGetUserRequest();
idRequest.withUserPoolId(USER_POOL_ID);
idRequest.withUsername(username);
// call cognito for the result
AdminGetUserResult result = identityUserPoolProviderClient.adminGetUser(idRequest);
// loop through results
I am trying to get amazon cognito to work. If I run the code to generate a login token from a standalone java program it works.
public class cognito extends HttpServlet
{
public static void main(String[] args) throws Exception {
AWSCredentials credentials = new BasicAWSCredentials("*******", "********");
AmazonCognitoIdentityClient client =
new AmazonCognitoIdentityClient(credentials);
client.setRegion(Region.getRegion(Regions.EU_WEST_1));
GetOpenIdTokenForDeveloperIdentityRequest tokenRequest =
new GetOpenIdTokenForDeveloperIdentityRequest();
tokenRequest.setIdentityPoolId("*************");
HashMap<String, String> map = new HashMap<String, String>();
//Key -> Developer Provider Name used when creating the identity pool
//Value -> Unique identifier of the user in your <u>backend</u>
map.put("test", "AmazonCognitoIdentity");
//Duration of the generated OpenID Connect Token
tokenRequest.setLogins(map);
tokenRequest.setTokenDuration(1000l);
GetOpenIdTokenForDeveloperIdentityResult result = client
.getOpenIdTokenForDeveloperIdentity(tokenRequest);
String identityId = result.getIdentityId();
String token = result.getToken();
System.out.println("id = " + identityId + " token = " + token);
}
}
However when I run this code from a servlet on a redhat linux server, it always times out.
Any suggestion would be helpful
map.put("test", "AmazonCognitoIdentity");
are you sure your developer provider name is "test"?
you can see it in your cognito identity pool edit page.
And "AmazonCognitoIdentity" should be your own unique user-id.
Without the actual exception, it is hard to tell what is the exact issue. It could be that something else running in your servlet engine is setting a much more aggressive socket timeout than the default when it runs from the command line. You might want to explicitly set the connection and socket timeouts using methods using this class http://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/ClientConfiguration.html and pass it in to the identity client constructor.
I'm implementing a client to a web service (and the guys maintaining the web service have been a litte unresponsive..) I've used axis and WSDL2Java to generate java classes and I can call their login-method on their authentication-service ok, and get a sessionId back (eg z4zojhiqkw40lj55kgtn1oya). However, it seems that i cannot use this sessionId as a parameter anywhere. Even a call to their hasSession()-method directly after login returned false. I managed to solve this by setting setMaintainSession(true) on the Locator-object for this service. But the problem is, that this first service, the Authentication-service, is only used for authentification. If I then call setMaintainSession(true) on eg ProductServiceLocator, and call some method on it, I will get an error because of unauthenticated session. I have to find a way to share the session between the services on the client side.
Looking on their php code example-it seeems like they are storing the session in a cookie. How can I mimic this behaviour in my java client?
php-code:
$authentication = new SoapClient ( "https://webservices.24sevenoffice.com/authenticate/authenticate.asmx?wsdl", $options );
// log into 24SevenOffice if we don't have any active session. No point doing this more than once.
$login = true;
if (!empty($_SESSION['ASP.NET_SessionId'])){
$authentication->__setCookie("ASP.NET_SessionId", $_SESSION['ASP.NET_SessionId']);
try{
$login = !($authentication->HasSession()->HasSessionResult);
}
catch ( SoapFault $fault ) {
$login = true;
}
}
if( $login ){
$result = ($temp = $authentication->Login($params));
// set the session id for next time we call this page
$_SESSION['ASP.NET_SessionId'] = $result->LoginResult;
// each seperate webservice need the cookie set
$authentication->__setCookie("ASP.NET_SessionId", $_SESSION['ASP.NET_SessionId']);
// throw an error if the login is unsuccessful
if($authentication->HasSession()->HasSessionResult == false)
throw new SoapFault("0", "Invalid credential information.");
}
My code is the following:
AuthenticateLocator al = new AuthenticateLocator();
al.setMaintainSession(true);
Credential c = new Credential(CredentialType.Community,username,password,guid);
AuthenticateSoap s = al.getAuthenticateSoap();
String sessionId = s.login(c);
System.out.println("Session id was: "+sessionId);
System.out.println("Has Session: "+s.hasSession()); //Hooray, now works after setMaintainSession(true)
//And now trying to call another Service
CompanyServiceLocator cl = new CompanyServiceLocator();
cl.setMaintainSession(true);
CompanyServiceSoap css = cl.getCompanyServiceSoap();
css.getCountryList(); //FAILS!
So what can I do to make this work?
Hooray, I finally solved it myself :-D
Thanx a lot to the excellent article at http://www.nsftools.com/stubby/ApacheAxisClientTips.htm
I had to do the following with my code to make it work:
CompanyServiceLocator cl = new CompanyServiceLocator();
cl.setMaintainSession(true);
CompanyServiceSoap css = cl.getCompanyServiceSoap();
((Stub)css)._setProperty(HTTPConstants.HEADER_COOKIE, "ASP.NET_SessionId="+sessionId); //New line that does the magic
css.getCountryList(); //SUCCESS :-D
Operating in the high-level abstraction of the autogenerated classes, it was unknown to me that casting the service classes to Stub would expose more methods and properties that could be set. Good to know for later I guess :-)
I am using Waffle for an SSO solution in my web-app.
Everything works fine but I would like to modify some functionality slightly:
Currently, if a user is not connected to the domain the SSO fails and opens a little authorization dialog:
The windows authorization requires the user name formatted like Domain\Username but most of my users will not know to add the domain in front of their username. So I would like to provide a default domain name if one is not specified.
I found a waffle function that I can override which will give me access to the decoded authentication token, I added a println to the waffle function and it shows the username in plain text (either with or without the domain depending on what is typed in the dialog):
public IWindowsSecurityContext acceptSecurityToken(String connectionId, byte[] token, String securityPackage) {
// I can see the passed username in the logs with this
System.out.println(new String(token));
// I don't understand any of the JNA stuff below this comment:
IWindowsCredentialsHandle serverCredential = new WindowsCredentialsHandleImpl(
null, Sspi.SECPKG_CRED_INBOUND, securityPackage);
serverCredential.initialize();
SecBufferDesc pbServerToken = new SecBufferDesc(Sspi.SECBUFFER_TOKEN, Sspi.MAX_TOKEN_SIZE);
SecBufferDesc pbClientToken = new SecBufferDesc(Sspi.SECBUFFER_TOKEN, token);
NativeLongByReference pfClientContextAttr = new NativeLongByReference();
CtxtHandle continueContext = _continueContexts.get(connectionId);
CtxtHandle phNewServerContext = new CtxtHandle();
int rc = Secur32.INSTANCE.AcceptSecurityContext(serverCredential.getHandle(),
continueContext, pbClientToken, new NativeLong(Sspi.ISC_REQ_CONNECTION),
new NativeLong(Sspi.SECURITY_NATIVE_DREP), phNewServerContext,
pbServerToken, pfClientContextAttr, null);
WindowsSecurityContextImpl sc = new WindowsSecurityContextImpl();
sc.setCredentialsHandle(serverCredential.getHandle());
sc.setSecurityPackage(securityPackage);
sc.setSecurityContext(phNewServerContext);
switch (rc)
{
case W32Errors.SEC_E_OK:
// the security context received from the client was accepted
_continueContexts.remove(connectionId);
// if an output token was generated by the function, it must be sent to the client process
if (pbServerToken != null
&& pbServerToken.pBuffers != null
&& pbServerToken.cBuffers.intValue() == 1
&& pbServerToken.pBuffers[0].cbBuffer.intValue() > 0) {
sc.setToken(pbServerToken.getBytes());
}
sc.setContinue(false);
break;
case W32Errors.SEC_I_CONTINUE_NEEDED:
// the server must send the output token to the client and wait for a returned token
_continueContexts.put(connectionId, phNewServerContext);
sc.setToken(pbServerToken.getBytes());
sc.setContinue(true);
break;
default:
sc.dispose();
WindowsSecurityContextImpl.dispose(continueContext);
_continueContexts.remove(connectionId);
throw new Win32Exception(rc);
}
return sc;
}
That whole function is from the Waffle API I only added the println at the beginning.
The passed username prints in plain text inside this token between a bunch of random byte chars (ÉsR=ÍtÍö?æ¸+Û-).
I am admittedly in very far over my head with JNA and java in general but I thought that because I can see the username here there must be a way to prepend the domain name to the username part of this token? I could be wrong.
My other idea was to add the domain to the pbClientToken that is created from the raw byte[] token this method is passed.
The pbClientToken is a JNA Structure object derivative. It has the Stucture method writeField which looked promising but I can't seem to figure out what field I should write. The Structure.getFields method doesn't seem to be available from pbClientToken.
I was hoping that this was a simple problem for someone more familiar with byte[] processing or JNA.
You cannot do this. What happens behind this dialog is a call to LogonUser on the user's machine, which gives you a ticket, which is then sent to the server. Unfortunately the server is not in the same domain, so even if you manage to extract the username it's completely useless.