Shiro LDAP Authorization config - java

Could you please help me with the following situation?
Background information:
I'm using the Vaadin framework.
I'm using the Java security framework Shiro
I'm using ssl.
Authentication works.
Username syntax = pietj#.lcl , jank#.lcl
memberOf field is being used as role.
shiro.ini
[main]
contextFactory = org.apache.shiro.realm.ldap.JndiLdapContextFactory
contextFactory.url = ldaps://<SERVER>:636
contextFactory.systemUsername = <USERNAME>#<COMPANY>
contextFactory.systemPassword = <PASSWORD>
contextFactory.environment[java.naming.security.protocol] = ssl
realm = org.apache.shiro.realm.activedirectory.ActiveDirectoryRealm
realm.ldapContextFactory = $contextFactory
realm.searchBase = "OU=<APPDIR>,DC=<COMPANY>,DC=lcl"
realm.groupRolesMap = "CN=<ROLE>,OU=<APPDIR>,DC=<COMPANY>,DC=lcl":"Admin"
[roles]
# 'Admin' role has permissions *
Admin = *
Goal
Authorization mapping based on the memberOf field from the currentUser.
Problem
currentUser.hasRole("Admin") always return false.
Questions
Is the above shiro.ini correct?
How do I fix the problem?

I ran into a similar issue using Shiro 1.2.4. Your Shiro configuration is probably OK and the problem lies in ActiveDirectory configuration.
In my setup some users had the userPrincipalName attribute set, while other users hadn't. You can check your in AD server with Sysinternals Active Directory Explorer for example.
This attribute is the one used by Shiro to search for a particular user, then it looks for groups defined in the memberOf attribute.
Take a look at ActiveDirectoryRealm.java source code, method Set<String> getRoleNamesForUser(String username, LdapContext ldapContext) the exact query used is
String searchFilter = "(&(objectClass=*)(userPrincipalName={0}))";
So you have two solutions:
Set userPrincipalName attribute on every user
Change how Shiro searches for users
I went for the second solution. Changing the search query is harder than it should be: you have to customize queryForAuthorizationInfo and getRoleNamesForUser (because its private) methods of ActiveDirectoryRealm class. This is how I did it:
public class CustomActiveDirectoryRealm extends ActiveDirectoryRealm {
#Override
protected AuthorizationInfo queryForAuthorizationInfo(PrincipalCollection principals, LdapContextFactory ldapContextFactory) throws NamingException {
String username = (String) getAvailablePrincipal(principals);
// Perform context search
LdapContext ldapContext = ldapContextFactory.getSystemLdapContext();
Set<String> roleNames = null;
try {
roleNames = getRoleNamesForUser(username, ldapContext);
} finally {
LdapUtils.closeContext(ldapContext);
}
return buildAuthorizationInfo(roleNames);
}
// Customize your search query here
private static final String USER_SEARCH_FILTER = "(&(objectClass=*)(sn={0}))";
private Set<String> getRoleNamesForUser(String username, LdapContext ldapContext) throws NamingException {
Set<String> roleNames;
roleNames = new LinkedHashSet<String>();
SearchControls searchCtls = new SearchControls();
searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
String userPrincipalName = username.replace("acegas\\", "");
if (principalSuffix != null) {
userPrincipalName += principalSuffix;
}
Object[] searchArguments = new Object[]{userPrincipalName};
NamingEnumeration answer = ldapContext.search(searchBase, USER_SEARCH_FILTER, searchArguments, searchCtls);
while (answer.hasMoreElements()) {
SearchResult sr = (SearchResult) answer.next();
Attributes attrs = sr.getAttributes();
if (attrs != null) {
NamingEnumeration ae = attrs.getAll();
while (ae.hasMore()) {
Attribute attr = (Attribute) ae.next();
if (attr.getID().equals("memberOf")) {
Collection<String> groupNames = LdapUtils.getAllAttributeValues(attr);
Collection<String> rolesForGroups = getRoleNamesForGroups(groupNames);
roleNames.addAll(rolesForGroups);
}
}
}
}
return roleNames;
}
}
And then of course use this class as Realm in shiro.ini
[main]
realm = your.package.CustomActiveDirectoryRealm
realm.ldapContextFactory = $contextFactory
realm.searchBase = "OU=<APPDIR>,DC=<COMPANY>,DC=lcl"
realm.groupRolesMap = "CN=<ROLE>,OU=<APPDIR>,DC=<COMPANY>,DC=lcl":"Admin"

Related

how to use java to query LDAP's ROOTDSE

in c#, only two line need to achieve this:
DirectoryEntry rootDSE = new DirectoryEntry(string.Format("LDAP://{0}/RootDSE", dnsDomainName));
string configurationNamingContext = rootDSE.Properties["configurationNamingContext"][0].ToString();
how to do this in java world?
figured it out by using Spring LDAP, the LDAP URL cannot add RootDSE postfix:
LDAP://{domain name}, then use wildcard search:
LdapTemplate template = new LdapTemplate(ldapContextSource);
template.setIgnorePartialResultException(true);
String returnedAtts[] = { "configurationNamingContext" };
SearchControls controls = new SearchControls(SearchControls.OBJECT_SCOPE,0,0,returnedAtts,false,false);
LikeFilter filter = new LikeFilter ("objectClass", "*");
List<String> result = template.search("", filter.encode(), controls, new AttributesMapper<String>() {
public String mapFromAttributes(Attributes attrs)
throws NamingException {
return attrs.get("configurationNamingContext").get().toString();
}
});
You can remove some of the extra boilerplate when using the Spring LDAP builder and a Java 8 lambda expression:
List<String> result = ldapTemplate.search(query()
.searchScope(SearchScope.OBJECT)
.where("objectclass").isPresent(),
(AttributesMapper<String>) attrs ->
attrs.get("configurationNamingContext").get().toString());
You'll also need this import:
import static org.springframework.ldap.query.LdapQueryBuilder.*;

how to retrieve the attribute "unicodePwd" in Active Directory through java programming

First of all, I apology for my bad english. I'm brazilian, so if there is any mistakes at the text, please, just disconsidered.
I read a lot of articles here about retrieving the attribute "unicodePwd" in Active Directory, but none of then actually helped me out.
Well, why do I need that information? I'll explain:
I have here some java routines that unify user information from differents systems one to another.
This routines get the information needed in a main Oracle Database and set the information in another Databases (Oracle and MySQL, basically).
For example: We have a private cloud system, that runs in a CentOS Linux OS, that has it own MySQL Database. To unify the users informations, including the users passwords, we get the information from the main Oracle Database and set do this system's MySQL Database, to unify user details and login information.
All the routines that i have here are working and there's no problems, but now we have a new challenge.
We need to do the same unification with ours Active Directory users, getting the information needed in this main Oracle Database and then setting all the information into Active Directory users, including the users passwords.
I already updated the password succesfully in Active Directory users, but I don't want that the password get updated everytime that this java routine runs, but only when the password changes in the main Oracle Database.
Example: When one of the users change the password in the main Oracle Database, the java routine gets this user information to set then in the same user in Active Directory. To do that properly, the routine gets the same information in Active Diretory, then it compares both passwords (Oracle's password and Active Diretory's password) and finally, if the password is different, the routine will update it, but if the password is not different, the routine will do nothing.
That is why i need to retrieve the attribute "unicodePwd" in Active Directory.
Here is some of my code:
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.*;
import org.apache.commons.mail.EmailException;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;
public class ldapQuery {
String distinguishedName = "";
String department = "";
String physicalDeliveryOfficeName = "";
String telephoneNumber = "";
String mobile = "";
String title = "";
String sAMAccountName = "";
String unicodePwd = "";
public ldapQuery(String mail) {
try {
final Hashtable<String, String> env = new Hashtable<String, String>();
final String adminName = "CN=MY DOMAIN ADMIN,CN=MY DOMAIN ADMIN FOLDER LOCALIZATION,DC=MY DOMAIN,DC=MY DOMAIN,DC=MY DOMAIN";
final String adminPasswd = "MY DOMAIN ADMIN PASSWORD";
final String ldapUrl = "ldaps://MY ACTIVE DIRECTORY SERVER:636";
final String factory = "com.sun.jndi.ldap.LdapCtxFactory";
final String authType = "simple";
final String protocol = "ssl";
env.put(Context.INITIAL_CONTEXT_FACTORY, factory);
env.put(Context.SECURITY_AUTHENTICATION, authType);
env.put(Context.SECURITY_PRINCIPAL, adminName);
env.put(Context.SECURITY_CREDENTIALS, adminPasswd);
env.put(Context.SECURITY_PROTOCOL, protocol);
env.put(Context.PROVIDER_URL, ldapUrl);
DirContext ctx = new InitialLdapContext (env,null);
SearchControls searchCtls = new SearchControls();
String returnedAtts[] = {"sAMAccountName", "distinguishedName","department", "physicalDeliveryOfficeName", "telephoneNumber", "mobile", "title", "unicodePwd"};
searchCtls.setReturningAttributes(returnedAtts);
searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
String searchFilter = "(&(objectClass=user)(mail=" + mail +"))";
String searchBase = "DC=MY DOMAIN,DC=MY DOMAIN,DC=MY DOMAIN";
int totalResults = 0;
NamingEnumeration<SearchResult> answer =ctx.search(searchBase, searchFilter, searchCtls);
while (answer.hasMoreElements()) {
SearchResult sr = (SearchResult)answer.next();
totalResults++;
Attributes attrs = sr.getAttributes();
if (attrs != null) {
distinguishedName = (String) attrs.get("distinguishedName").get();
department = (String) attrs.get("department").get();
physicalDeliveryOfficeName = (String) attrs.get("physicalDeliveryOfficeName").get();
telephoneNumber = (String) attrs.get("telephoneNumber").get();
mobile = (String) attrs.get("mobile").get();
title = (String) attrs.get("title").get();
sAMAccountName = (String) attrs.get("sAMAccountName").get();
Attribute passwd = attrs.get("unicodePwd");
unicodePwd = unicodePwd + passwd;
if (department == null) {
department = "";
}
if (physicalDeliveryOfficeName == null) {
physicalDeliveryOfficeName = "";
}
if (telephoneNumber == null) {
telephoneNumber = "";
}
if (mobile == null) {
mobile = "";
}
if (title == null) {
title = "";
}
}
}
}
catch (NamingException e){
System.err.println("FAIL MESSAGE: " + e);
}
}
public String ldapSearchResultDistinguishedName() {
return distinguishedName;
}
public String ldapSearchResultDepartment() {
return department;
}
public String ldapSearchResultPhysicalDeliveryOfficeName() {
return physicalDeliveryOfficeName;
}
public String ldapSearchResultTelephoneNumber() {
return telephoneNumber;
}
public String ldapSearchResultMobile() {
return mobile;
}
public String ldapSearchResultTitle() {
return title;
}
public String ldapSearchResultUnicodePwd() {
return unicodePwd;
}
public String ldapSearchResultSAMAccountName() {
return sAMAccountName;
}
}
After running the code, all the variables return the correct information but the variable "unicodePwd", that returns "null", even though the user has a password.
I know about the byte UTF-16LE thing and that the "unicodePwd" field in Active Directory is encrypted, but, as I explained earlier, i need that information decrypted in a String variable.
Any ideias?
Thank you!
I know this is an old question but I stumbled across it as I was also looking for an answer to the same question. I found the answer and thought it might help anybody else who lands here.
According to Microsoft Documentation it would appear that the unicodePwd attribute is NEVER returned by an LDAP search.
In my case, I need to validate that the credentials received are correct. So my plan is to use the username/password received and create a custom LdapContextFactory on the fly with those credentials. If I can contact the server successfully by doing an LdapContextFactory.get and get back an LdapContext then I can be certain that the password supplied was correct. If you don't get it back then you know it's wrong and can take it from there.

Websphere Application Server SAML Token processing

I'm able to successfully setup websphere to authenticate with an IdP and access the web resource as expected. But now my application needs the claims/assertions/attributes available in the SAML token/response to proceed further. What is best option available to access the SAML response/attributes inside my java application?
I want to add to the previous answer.For Websphere Application Server, if you are using already available WebsphereSamlSP application as SP then you can use following code inside handleRedirect() method of IBMWebpshereSamlACSListenerServlet to get saml attributes. Or use this in your custom SP code.
SAMLToken samlToken = (SAMLToken) AccessController
.doPrivileged(new java.security.PrivilegedExceptionAction() {
public Object run() throws java.lang.Exception {
final java.util.Iterator authIterator = subject
.getPrivateCredentials(SAMLToken.class)
.iterator();
if (authIterator.hasNext()) {
final SAMLToken token = (SAMLToken) authIterator
.next();
return token;
}
return null;
}
});
// Log attribute name and values
List<SAMLAttribute> attributes = samlToken.getSAMLAttributes();
if (attributes != null && !attributes.isEmpty()) {
for (SAMLAttribute attr : attributes) {
logger.debug(attr.getName());
if (attr.getStringAttributeValue() != null) {
for (int i = 0; i < attr.getStringAttributeValue().length; i++) {
logger.debug(attr.getStringAttributeValue()[i]);
}
}
}
}
For WebSphere Liberty profile:
get com.ibm.websphere.security.saml2.Saml20Token from RunAsSubject:
Saml20Token samlToken = null;
Subject subject = WSSubject.getRunAsSubject();
Iterator authIterator = subject.getPrivateCredentials(Saml20Token.class).iterator();
if (authIterator.hasNext()) {
samlToken = (Saml20Token) authIterator.next();
}
You can get List of com.ibm.websphere.security.saml2.Saml20Attribute from Saml20Token
samlToken.getSAMLAttributes();
You can also get most SAML assertions from Saml20Token. For example
samlToken.getSAMLIssuerName();
For classic WebSphere:
Subject subject = WSSubject.getRunAsSubject();
SAMLToken samlToken = (SAMLToken) AccessController.doPrivileged(
new java.security.PrivilegedExceptionAction() {
public Object run() throws java.lang.Exception
{
final java.util.Iterator authIterator = subject.getPrivateCredentials(SAMLToken.class).iterator();
if ( authIterator.hasNext() ) {
final SAMLToken token = (SAMLToken) authIterator.next();
return token;
}
return null;
}
});
SAMLNameID = samlToken.getSAMLNameID();
List<SAMLAttribute> attributes = samlToken.getSAMLAttributes();

How to incorporate Environments with JClouds-Chef API?

I am using JClouds-Chef to:
Bootstrap a newly-provisioned Linux VM; and then
Run the chef-client on that node to configure it
It's important to note that all I'm currently configuring Chef with is a role (that's all it needs; everything else is set up on the Chef server for me):
public class ChefClient {
public configure() {
String vmIp = "myapp01.example.com";
String vmSshUsername = "myuser";
String vmSshPassword = "12345";
String endpoint = "https://mychefserver.example.com";
String client = "myuser";
String validator = "chef-validator";
String clientCredential = Files.toString(new File("C:\\Users\\myuser\\sandbox\\chef\\myuser.pem"), Charsets.UTF_8);
String validatorCredential = Files.toString(new File("C:\\Users\\myuser\\sandbox\\chef\\chef-validator.pem"), Charsets.UTF_8);
Properties props = new Properties();
props.put(ChefProperties.CHEF_VALIDATOR_NAME, validator);
props.put(ChefProperties.CHEF_VALIDATOR_CREDENTIAL, validatorCredential);
props.put(Constants.PROPERTY_RELAX_HOSTNAME, "true");
props.put(Constants.PROPERTY_TRUST_ALL_CERTS, "true");
ChefContext ctx = ContextBuilder.newBuilder("chef")
.endpoint(endpoint)
.credentials(client, clientCredential)
.overrides(props)
.modules(ImmutableSet.of(new SshjSshClientModule())) //
.buildView(ChefContext.class);
ChefService chef = ctx.getChefService();
List<String> runlist = new RunListBuilder().addRole("platformcontrol_dev").build();
ArrayList<String> runList2 = new ArrayList<String>();
for(String item : runlist) {
runList2.add(item);
}
BootstrapConfig bootstrapConfig = BootstrapConfig.builder().runList(runList2).build();
chef.updateBootstrapConfigForGroup("jclouds-chef", bootstrapConfig);
Statement bootstrap = chef.createBootstrapScriptForGroup("jclouds-chef");
SshClient.Factory sshFactory = ctx.unwrap().utils()
.injector().getInstance(Key.get(new TypeLiteral<SshClient.Factory>() {}));
SshClient ssh = sshFactory.create(HostAndPort.fromParts(vmIp, 22),
LoginCredentials.builder().user(vmSshUsername).password(vmSshPassword).build());
ssh.connect();
try {
StringBuilder rawScript = new StringBuilder();
Map<String, String> resolvedFunctions = ScriptBuilder.resolveFunctionDependenciesForStatements(
new HashMap<String, String>(), ImmutableSet.of(bootstrap), OsFamily.UNIX);
ScriptBuilder.writeFunctions(resolvedFunctions, OsFamily.UNIX, rawScript);
rawScript.append(bootstrap.render(OsFamily.UNIX));
ssh.put("/tmp/chef-bootstrap.sh", rawScript.toString());
ExecResponse result = ssh.exec("bash /tmp/chef-bootstrap.sh");
} catch(Throwable t) {
println "Exception: " + t.message
} finally {
ssh.disconnect();
}
}
}
Our in-house "Chef" (our devops guy) now wants to add the concept of Chef "environments" to all our recipes in addition to the existing roles. This is so that we can specify environment-specific roles for each node. My question: does the JClouds-Chef API handle environments? If so, how might I modify the code to incorporate environment-specific roles?
Is it just as simple as:
BootstrapConfig bootstrapConfig = BootstrapConfig.builder()
.environment("name-of-env-here?").runList(runList2).build();
Yes, it is that simple. That will tell the bootstrap script to register the node in the specified environment.
Take into account, though, that the environment must already exist in the Chef Server. If you want to create nodes in new environments, you can also create them programmatically as follows:
ChefApi api = ctx.unwrapApi(ChefApi.class);
if (api.getEnvironment("environment-name") == null) {
Environment env = Environment.builder()
.name("environment-name")
.description("Some description")
.build();
api.createEnvironment(env);
}

How to find the weblogic jmx attributes for a objectname

I am writing simple client application to connect to weblogic and list all the libraries that a webapp is depending on.However, I am having difficulty in finding the right attributes for a objectname. For example,
If you look at the below sample code given on oracle.com to connect MBeanServer
public static void initConnection(String hostname, String portString,
String username, String password) throws IOException,
MalformedURLException {
String protocol = "t3";
Integer portInteger = Integer.valueOf(portString);
int port = portInteger.intValue();
String jndiroot = "/jndi/";
String mserver = "weblogic.management.mbeanservers.edit";
JMXServiceURL serviceURL = new JMXServiceURL(protocol, hostname, port,
jndiroot + mserver);
Hashtable h = new Hashtable();
h.put(Context.SECURITY_PRINCIPAL, username);
h.put(Context.SECURITY_CREDENTIALS, password);
h.put(JMXConnectorFactory.PROTOCOL_PROVIDER_PACKAGES,
"weblogic.management.remote");
connector = JMXConnectorFactory.connect(serviceURL, h);
connection = connector.getMBeanServerConnection();
}
public ObjectName startEditSession() throws Exception {
// Get the object name for ConfigurationManagerMBean.
ObjectName cfgMgr = (ObjectName) connection.getAttribute(service,
"ConfigurationManager");
// Instruct MBeanServerConnection to invoke
// ConfigurationManager.startEdit(int waitTime int timeout).
// The startEdit operation returns a handle to DomainMBean, which is
// the root of the edit hierarchy.
ObjectName domainConfigRoot = (ObjectName)
connection.invoke(cfgMgr,"startEdit",
new Object[] { new Integer(60000),
new Integer(120000) }, new String[] { "java.lang.Integer",
"java.lang.Integer" });
if (domainConfigRoot == null) {
// Couldn't get the lock
throw new Exception("Somebody else is editing already");
}
return domainConfigRoot;
}
The line
ObjectName cfgMgr = (ObjectName) connection.getAttribute(service,
"ConfigurationManager");
Is referring to a JMX attribute ConfigurationManger. How can we find all the attributes that are under a given objectname in weblogic?
Thanks for your help!!
Nevermind! I found the solution.
You get attributes for a ObjectName by calling getBeanInfo on the ServerConnection!
Example:
MBeanAttributeInfo[] beanInfo = (connection.getMBeanInfo(objectName)).getAttributes();
for(MBeanAttributeInfo info:beanInfo)
System.out.println(info.getType()+" "+info.getName());
Maybe the WebLogic Classloader Analysis Tool (CAT) can give additional insight out of the box ...

Categories