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.
Related
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
So basically I need to use embedded signing feature to get the URL and embed into my application, and then my customer can sign the document from my side. Apart from that, after my customer signed on the doc, he needs to ask his debtor to sign on the same doc as well.
So on DocuSign UI, I found that I can set a signing order, which means the second recipient receives the email right after the first recipient signed (perfect match my requirement).
setting on UI
However, the second recipient can not receive the email after the first signer signed even though on UI it says sent.
public Envelope embeddedSigning(Long debtorId, String signerEmail, String signerName, String templateId) throws ApiException, IOException {
// create an envelop
EnvelopeDefinition envelope = makeEnvelope(debtorId, signerEmail, signerName, templateId);
ApiClient apiClient = baseRestApiClient();
apiClient.addDefaultHeader("Authorization", "Bearer " + getToken());
EnvelopesApi envelopesApi = new EnvelopesApi(apiClient);
EnvelopeSummary summary = envelopesApi.createEnvelope(accountId, envelope);
RecipientViewRequest viewRequest = makeRecipientViewRequest(debtorId, signerEmail, signerName);
ViewUrl viewUrl = envelopesApi.createRecipientView(accountId, summary.getEnvelopeId(), viewRequest);
// #formatter:off
return Envelope.builder()
.envelopId(summary.getEnvelopeId())
.redirectUrl(viewUrl.getUrl()).build();
// #formatter:on
}
private EnvelopeDefinition makeEnvelope(Long debtorId, String signerEmail, String signerName, String templateId) throws IOException {
EnvelopeDefinition envelopeDefinition = new EnvelopeDefinition();
envelopeDefinition.setEmailSubject("Please sign this document");
envelopeDefinition.setTemplateId(templateId);
TemplateRole signer = new TemplateRole();
signer.setEmail(signerEmail);
signer.setName(signerName);
signer.clientUserId(String.valueOf(debtorId));
signer.setRoleName("signer0");
signer.setRoutingOrder("1");
TemplateRole signer1 = new TemplateRole();
signer1.setEmail("xxx");
signer1.setName("xxx");
signer1.clientUserId(String.valueOf(xxx));
signer1.setRoleName("signer1");
signer1.setRoutingOrder("2");
envelopeDefinition.setTemplateRoles(Arrays.asList(signer, signer1));
envelopeDefinition.setStatus("sent");
return envelopeDefinition;
}
You are setting signer1.clientUserId(String.valueOf(xxx)); which means you are making signer an embedded signer. By Default, DocuSign does not send email to the embedded signer. By making a signer as embedded signer, you are telling DocuSign that calling App will take care of deciding when to host signing ceremony for this signer so DocuSign will not send an email as they will not be doing signing from the email, instead it be your App which will be generating Signing URL when that signer is on your App. So if you remove signer1.clientUserId(String.valueOf(xxx)); code then you will see that signer1 will get an email from DocuSign.
Docs has more details about embedded signing.
Typically routing order starts at 1. so it should be 1 and 2, not 0 and 1.
Apart from that, "Sent" is the status for the entire envelope. The envelope goes to routing order 1 first. Then, when all recipients of routing order 1 finished signing, it goes to 2 etc. I'm not sure if you actually have an issue here, but please confirm after you changed to 1 and 2 what exactly do you see that you don't expect.
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
This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 6 years ago.
I have a little problem with Attributes. I am currently working on a project that parses emails from an LDAP server into the Java application that will be doing some interesting stuff with emails in the future.
I am currently having this code that takes emails from users on LDAP and it needs to put emails in the User class as seen in my code:
[some code here, TRY CATCH is also included]
LdapContext ctx = new InitialLdapContext(env, null);
ctx.setRequestControls(null);
NamingEnumeration<?> namingEnum2 = ctx.search("[path to the server]", "(objectClass=user)", getSimpleSearchControls());
System.out.println("Test: print emails from whole DIRECOTRY: \n");
while (namingEnum2.hasMore()) {
SearchResult result = (SearchResult) namingEnum2.next();
Attributes attrs = result.getAttributes();
System.out.println(attrs.get("mail"));
/** This line above works fine, but every time there is no email
in User info, it prints "null" in some cases when there is
no email, which is not perfect. But this is just here to see
if everything works and indeed it does.**/
/**User Class accepts a String parameter, checks if it's empty
and all that, does some checking and etc... BUT! attrs.get("mail")
is an Attribute, NOT a String. And I need to somehow "cast" this
attribute to a String, so I can work with it.**/
User user = new User(attrs.get("mail")); //error yet,because the parameter is not a String.
User user = new User(attrs.get("mail").toString());//gives an expeption.
/** And I know that there is a toString() method in Attribute Class,
but it doesn't work, it gives an "java.lang.NullPointerException"
exception when I use it as attrs.get("mail").toString() **/
}
Here is User class's constructor:
public User(String mail){
eMail = "NO EMAIL!";
if (mail != null && !mail.isEmpty()){
eMail = mail;
}
else
{
eMail = "NO EMAIL!";
}
}
try this
User user = new User(attrs.get("mail")!=null?attrs.get("mail").toString():null);
First you need to check that given attribute exists by using != null (e.g. using Objects.toString which does that inside, or manual if) check, and then use either toString on the Attribute, just like println does inside:
User user = new User(Objects.toString(attrs.get("mail")));
Or you can also use (to retrieve a single value in the attribute, if you have many you need to use getAll):
Object mail = null;
if (attrs.get("mail") != null) {
mail = attrs.get("mail").get();
}
User user = new User(mail.toString());
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 :-)