Does Java integration with native kerberos support impersonation? - java

I have the following setup (using java 11).
A user enters his browser, sends request to a java backend secured with kerberos.
Server responds with "Negotiate...", browser responds with user ticket.
Then java backend impersonates that user and goes to postgres pretending to be that user.
Impersonation is done with the following code:
((ExtendedGSSCredential) serviceCredentials).impersonate(gssName);
The code for impersonate() method (from jdk):
public GSSCredential impersonate(GSSName name) throws GSSException {
if (destroyed) {
throw new IllegalStateException("This credential is " +
"no longer valid");
}
Oid mech = tempCred.getMechanism();
GSSNameSpi nameElement = (name == null ? null :
((GSSNameImpl)name).getElement(mech));
GSSCredentialSpi cred = tempCred.impersonate(nameElement);
return (cred == null ?
null : GSSManagerImpl.wrap(new GSSCredentialImpl(gssManager, cred)));
}
The class of tempCred here SpNegoCredElement and it supports impersonate method.
This works with the default java kerberos implementation.
Now I want to use a native GSS-API kerberos (linux with libgssapi_krb5.so.2).
For that I start java process with the following options:
-Dsun.security.jgss.native=true -Djavax.security.auth.useSubjectCredsOnly=false
The code above doesn't work with this setup because now the variable tempCred is of type GSSCredElement and it has the following implementation of impersonate() method:
#Override
public GSSCredentialSpi impersonate(GSSNameSpi name) throws GSSException {
throw new GSSException(GSSException.FAILURE, -1,
"Not supported yet");
}
So it simply throws an exception.
Does anyone know why it doesn't support impersonation?
How can I make impersonation work with native GSS-API?

Related

Azure Select Account shown when user already logged in session

We've migrated from adal4j to msal4j in our java web applications.
All works well but the big difference is that when the user is already logged (maybe in other applications but same browser session) we always see the "select user" page and the user is not logged automatically and redirected to redirect uri as before with adal4j.
This is how we redirect to autentication page:
private static void redirectToAuthorizationEndpoint(IdentityContextAdapter contextAdapter) throws IOException {
final IdentityContextData context = contextAdapter.getContext();
final String state = UUID.randomUUID().toString();
final String nonce = UUID.randomUUID().toString();
context.setStateAndNonce(state, nonce);
contextAdapter.setContext(context);
final ConfidentialClientApplication client = getConfidentialClientInstance();
AuthorizationRequestUrlParameters parameters = AuthorizationRequestUrlParameters
.builder(props.getProperty("aad.redirectURI"), Collections.singleton(props.getProperty("aad.scopes"))).responseMode(ResponseMode.QUERY)
.prompt(Prompt.SELECT_ACCOUNT).state(state).nonce(nonce).build();
final String authorizeUrl = client.getAuthorizationRequestUrl(parameters).toString();
contextAdapter.redirectUser(authorizeUrl);
}
I've tried to remove .prompt(Prompt.SELECT_ACCOUNT)
but I receive an error
Any ideas?
• You might be getting the option for selecting the user account after switching to MSAL4J in your browser even after the SSO is enabled because either clearing the token cache is enabled in your code or MsalInteractionRequiredException option is thrown and specified accordingly due to which the application asks for a token interactively.
Thus, please check which accounts information is stored in the cache as below: -
ConfidentialClientApplication pca = new ConfidentialClientApplication.Builder(
labResponse.getAppId()).
authority(TestConstants.ORGANIZATIONS_AUTHORITY).
build();
Set<IAccount> accounts = pca.getAccounts().join(); ’
Then, from the above information, if you want to remove the accounts whose prompts you don’t want to see during the user account selection such that the default account should get selected and signed in automatically, execute the below code by modifying the required information: -
Set<IAccount> accounts = pca.getAccounts().join();
IAccount accountToBeRemoved = accounts.stream().filter(
x -> x.username().equalsIgnoreCase(
UPN_OF_USER_TO_BE_REMOVED)).findFirst().orElse(null);
pca.removeAccount(accountToBeRemoved).join();
• And for the MsalInteractiveRequiredException class in the code, kindly refer to the below official documentation link for the AcquireTokenSilently and other reasons responsible for the behaviour. Also, refer to the sample code given below for your reference regarding the same: -
https://learn.microsoft.com/en-us/azure/active-directory/develop/msal-error-handling-java#msalinteractionrequiredexception
IAuthenticationResult result;
try {
ConfidentialClientApplication application =
ConfidentialClientApplication
.builder("clientId")
.b2cAuthority("authority")
.build();
SilentParameters parameters = SilentParameters
.builder(Collections.singleton("scope"))
.build();
result = application.acquireTokenSilently(parameters).join();
}
catch (Exception ex){
if(ex instanceof MsalInteractionRequiredException){
// AcquireToken by either AuthorizationCodeParameters or DeviceCodeParameters
} else{
// Log and handle exception accordingly
}
}

user.permissionsBoundary returns NULL while retrieving information from AWS using Java SDK

I am using AWS Java SDK v2 to list users using the code defined here on the AWS GitHub repo.
public static void listAllUsers(IamClient iam) {
try {
boolean done = false;
String newMarker = null;
while (!done) {
ListUsersResponse response;
ListUsersRequest request;
if (newMarker == null) {
request = ListUsersRequest.builder().build();
} else {
request = ListUsersRequest.builder()
.marker(newMarker).build();
}
response = iam.listUsers(request);
for (User user : response.users()) {
System.out.format("\n Retrieved user %s", user.userName());
System.out.println("\nPermission Boundary: " + user.permissionsBoundary());
}
if (!response.isTruncated()) {
done = true;
} else {
newMarker = response.marker();
}
}
} catch (IamException e) {
System.err.println(e);
System.exit(1);
}
}
It returns NULL for user.permissionsBoundary(). Here is the output for print statements in the above code.
Retrieved user jamshaid
Permission Boundary: null
Retrieved user luminadmin
Permission Boundary: null
Retrieved user test
Permission Boundary: null
When I run the following command in AWS CloudShell on AWS console, it returns the PermissionBoundary for the users it is defined.
aws iam get-user --user-name test
Here is the sample output from AWS CloudShell.
I am using the same account to make both requests.
I have confirmed this behavior by setting a permission boundary on an IAM user in the AWS Management Console. I changed the ListUsers example to include this code:
for(User user : response.users()) {
System.out.format("\n Retrieved user %s", user.userName());
AttachedPermissionsBoundary permissionsBoundary = user.permissionsBoundary();
if (permissionsBoundary != null)
System.out.format("\n Permissions boundary details %s", permissionsBoundary.permissionsBoundaryTypeAsString());
}
...
The permissionsBoundary() method does return null - even though the permission is set. This is a bug.
My advice here is to log a Github issue here:
https://github.com/aws/aws-sdk-java-v2
I also tested this with Kotlin SDK. Same result.
suspend fun listAllUsers() {
IamClient { region = "AWS_GLOBAL" }.use { iamClient ->
val response = iamClient.listUsers(ListUsersRequest { })
response.users?.forEach { user ->
println("Retrieved user ${user.userName}")
val permissionsBoundary = user.permissionsBoundary
if (permissionsBoundary != null)
println("Permissions boundary details ${permissionsBoundary.permissionsBoundaryType.toString()}")
}
}
}
I do not think it is an issue, but the programmed behavior. From the API docs:
IAM resource-listing operations return a subset of the available attributes
for the resource. For example, this operation does not return tags, even
though they are an attribute of the returned object. To view all of the
information for a user, see GetUser.
This is stated as well in the API javadocs.
In the console you are using get-user, not list-users, and this is why the command is returning all the information about the user, PermissionsBoundary within it.
Please, try instead using:
aws iam list-users
and check the output, it should match the result you obtained with the Java SDK, it will not contain PermissionsBoundary either.
If you want to obtain the same results that you are currently getting with the command aws iam get-user --user-name test from Java code, you can use the getUser method in IamClient. Try Something like:
GetUserRequest request = GetUserRequest.builder()
.userName("test")
.build()
;
GetUserResponse response = iam.getUser(request);
User user = response.user();
System.out.println("\nPermission Boundary: " + user.permissionsBoundary());
The User class is reused in both operations, get and list, but only in the former all the fields are populated.

PASSWD_CANT_CHANGE flag not present in UserAccountControl attribute

I need to check through LDAP if an ActiveDirectory user has the PASSWD_CANT_CHANGE flag set. I found the UserAccountControl attribute (https://learn.microsoft.com/it-it/windows/desktop/ADSchema/a-useraccountcontrol): it works for all other flags but it doesn't work for this flag. I only need to read it, not to write.
I'm using Java with UnboundID LDAP SDK (https://ldap.com/unboundid-ldap-sdk-for-java/).
Here is my JUnit test code.
public static enum UACFlags {
SCRIPT(0x0001),
ACCOUNTDISABLE(0x0002),
HOMEDIR_REQUIRED(0x0008),
LOCKOUT(0x0010),
PASSWD_NOTREQD(0x0020),
PASSWD_CANT_CHANGE(0x0040),
ENCRYPTED_TEXT_PWD_ALLOWED(0x0080),
TEMP_DUPLICATE_ACCOUNT(0x0100),
NORMAL_ACCOUNT(0x0200),
INTERDOMAIN_TRUST_ACCOUNT(0x0800),
WORKSTATION_TRUST_ACCOUNT(0x1000),
SERVER_TRUST_ACCOUNT(0x2000),
DONT_EXPIRE_PASSWORD(0x10000),
MNS_LOGON_ACCOUNT(0x20000),
SMARTCARD_REQUIRED(0x40000),
TRUSTED_FOR_DELEGATION(0x80000),
NOT_DELEGATED(0x100000),
USE_DES_KEY_ONLY(0x200000),
DONT_REQ_PREAUTH(0x400000),
PASSWORD_EXPIRED(0x800000),
TRUSTED_TO_AUTH_FOR_DELEGATION(0x1000000);
private int flag;
private UACFlags(int flag) {
this.flag = flag;
}
}
#Test
public void testLDAP() throws LDAPException {
LDAPConnection connection = //GET CONNECTION
String username = "....";
String search = "(sAMAccountName=" + username + ")";
SearchRequest request = new SearchRequest("DC=....,DC=....", SearchScope.SUB, search, SearchRequest.ALL_USER_ATTRIBUTES);
SearchResult result = connection.search(request);
SearchResultEntry entry = result.getSearchEntries().get(0);
Attribute a = entry.getAttribute("userAccountControl");
int val = a.getValueAsInteger();
System.out.println(Integer.toHexString(val));
EnumSet<UACFlags> flags = EnumSet.noneOf(UACFlags.class);
for (UACFlags f : UACFlags.values()) {
if ((val & f.flag) == f.flag) {
flags.add(f);
}
}
System.out.println("FLAGS: " + flags);
}
I set up the flag on AD Users and Computers and it works as expected. I only want to check the flag programmatically, using Java and LDAP. Other solutions than UserAccountControl attribute are ok!
Thanks!!
That is, unfortunately, expected.
Microsoft uses the ADS_USER_FLAG_ENUM enumeration in a couple places:
The userAccountControl attribute when using LDAP, and
The userFlags property when using the WinNT provider.
The ADS_UF_PASSWD_CANT_CHANGE flag can only be used when using the WinNT provider, which I'm not sure you can do from Java.
When you click that 'User cannot change password' checkbox in AD Users and Computers, it doesn't actually change the userAccountControl attribute. In reality, it adds two permissions on the account:
Deny Change Password to 'Everyone'
Deny Change Password to 'SELF'
There is a description of how to look for those permissions here, but the examples are in C++ and VBScript. I don't know how to view the permissions in Java. It seems difficult and I can't find any real examples.
UPDATE
Appears from AD 2008 on that this is not a "real" value; but rather an ACE within the ACL of the entry.
THIS NO LONGER WORKS
As far as I can tell.
Microsoft Active Directory has a neat Extensible matching value that should work called LDAP_MATCHING_RULE_BIT_AND
So a simple LDAP Query Filter like:
(userAccountControl:1.2.840.113556.1.4.803:=64)
Should do the trick.

"Unsupported protocol element" when creating Interactions programmatically

I am attempting to create new Interactions programmatically on Genesys Platform SDK 8.5 for Java.
I use the example on the API reference
public void createInteraction(String ixnType, String ixnSubtype, String queue) throws Exception
{
RequestSubmit req = RequestSubmit.create();
req.setInteractionType(ixnType);
req.setInteractionSubtype(ixnSubtype);
req.setQueue(queue);
req.setMediaType("email");
Message response = mPMService.getProtocol("IxnSrv").request(req);
if(response == null || response.messageId() != EventAck.ID) {
// For this sample, no error handling is implemented
return;
}
EventAck event = (EventAck)response;
mInteractionId = event.getExtension().getString("InteractionId");
}
However, this gives me an Unsupported protocol element error.
'EventError' (126) attributes:
attr_error_desc [str] = "Unsupported protocol element"
attr_ref_id [int] = 2
attr_error_code [int] = 4
How do I create a new Interaction programmatically?
Interaction server should be connected with ClientType as either MediaServer or AgentApplication for this request(RequestSubmit).
First of all, you must open your protocol as Media Server. After that you must submit your interaction to interaction server.
Firstly your protocol config must be like this;
interactionServerConfiguration.ClientName = "TestClient";
interactionServerConfiguration.ClientType = InteractionClient.MediaServer;
// Register this connection configuration with Protocol Manager
protocolManagementService.Register(interactionServerConfiguration);
Note : You must have MediaServer type application definition on your Configuration Env., you must see it in CME.
After open you connection to ixn server. You can submit your interaction what you like. Even you can create new type interaction just like i do. I did for our coopate sms system. Its name is not important. We defined it on our bussiness attribute, so our agent can send coopate 3rd party sms system from their agent desktop. Without new extension or new license :) Just tricked it system. Also genesys allows it. i know it because we are genesys official support team in our country :) (But agent seat license may be required depends on agent head count).
RequestSubmit request = RequestSubmit.Create();
request.TenantId = 1;
request.MediaType = "email";
request.Queue = c_inboundQueue;
request.InteractionType = "Inbound";
request.InteractionSubtype = "InboundNew";
// Prepare the message to send. It is inserted in the request as UserData
KeyValueCollection userData =
new KeyValueCollection();
// Prepare the message to send
userData.Add("Subject", "subject goes here");
request.UserData = userData; protocolManagementService[c_interactionServerConfigurationIdentifier].Send(request);
Turns out I needed to set ClientType to InteractionClient.ReportingEngine.

Tomcat response mix-up

Have been seeing a very unique situation. Need some input.
Background: We have tomcat servers on ec2 instances with ELB. Our application is a spring application which does not use sessions cause we didn't want the load balancer to track sessions. We use cookies to identify the user. We use Memcache to store some basic session information. For the most part everything works great.
Problem: In some remote cases 0.01% of the time (we analyzed) the response is getting mixed up between the users. And the users are usually coming from the same clientIP. We know this because our application is being used extensively by schools. The client cookie and the cookie we read in the servlet matches. We are positive our application is not looking up the wrong info in the DB. But the response (the view) that is getting rendered is for another user. We know both users are logged in at the same time.
Is this something Tomcat related? Or Network related?
Controller Code:
#RequestMapping("/kids")
public ModelAndView kids(HttpServletRequest request, HttpServletResponse response) {
StudentUserSession studentUserSession = BaseController.getStudentUserSession(request);
if(studentUserSession != null) {
ModelAndView mv = new ModelAndView("kids");
mv.addObject("studentUserSession", studentUserSession);
return mv;
} else {
return new ModelAndView("redirect:/signin");
}
}
JSP Code:
var acookie = readCookie("sp_utkn");
var studentId = ${studentUserSession.studentId};
$.getJSON("/getStudentProfile", function(data){
var sId = -1;
$.each(data, function (key, value) {
if(key == 'studentId'){
sId = value;
}
});
dojo.xhrGet({
url:"/logStudentEvent?cookie=" + acookie+"&stuId="+studentId+"&aId="+sId+"&ref="+otherInfo.join(','),
});
});
The JSON Get Call is getting the same object . aId matches with the cookie but studentId doesn't. The other data on the page are also for another user. But the cookies are consistent and accurate.

Categories

Resources