I am trying to setup User Federation from a LDAP server to Keycloak. I managed to import all the users and groups respectively from LDAP server, however, the user-group (group tab in Users section) doesn’t show the actual mapped groups, although I can see those users presenting in the groups listed in Members tab in Groups section…
I went through all article in Keycloak forum/Jira ticket/Mail list and I did find a ticket describing the exact issue that I am experiencing now (https://lists.jboss.org/pipermail/keycloak-user/2018-February/013076.html) and Marek has also replied to that as well, however, I still couldn't figure out what configuration I set incorrectly just by the information provided in the post.
Could anyone please help me out? Thanks ahead!
User-Group
Group
User Configuration
Group Mapper Configuration
Thanks,
Chance
Looks like the issue was in LDAP server. The problem only exists when I import the users from FreeIPA DB. However, when I try to federate to an AD server, the user-group information just comes along with the users without any additional modification!
Below is the configuration I have used in the successful case. Hopefully it will help others who encounter with a similar issue. Thanks everyone for the attention.
[User Federation Provider Settings]
Enabled: ON (Default)
Console Display Name :
Priority: 0 (Default)
Import Users : ON (Default)
Edit Mode : READ_ONLY
Sync Registrations : OFF (Default)
Vendor : Active Directory (This is important. Once I switch to AD, instead of FreeIPA, the issue is gone)
Username LDAP attribute: sAMAccountName
RDN LDAP attribute : cn
UUID LDAP attribute : objectGUID
User Object Classes : person, organizationalPerson, user (You should check what Object Class the server is currently configured and adjust accordingly)
Connection URL : ldap://:389 (If you are using ldaps, the port is 636)
Users DN : <the scope includes all your users you would like to import, e.g. OU=User,DC=example,DC=com)
Bind Type: simple
Enable StartTLS: OFF (Default)
Bind DN:
Bind Credential:
Custom User LDAP Filter: <You can leave it blank if you don't want to filter. However, if you would like to filter something, for example, users from a specific group, you can run a filter such as (&(objectClass=user)(memberof:1.2.840.113556.1.4.1941:=CN=,OU=,DC=example,DC=com)) >
Search Scope: Subtree (It the users after under one level of Users DN, you can choose "One level" option)
Validate Password Policy: OFF (Default)
Trust Email: OFF (Default)
Use Truststore SPI: Only for ldaps
Connection Pooling: On
The rest of setting leave it blank.
You need to configure a group-ldap-mapper as well
[Group Mapper]
Name:
Mapper Type: group-ldap-mapper
LDAP Groups DN : <Where are the groups of this tree saved. For example, OU=Group,DC=example,DC=com>
Group Name LDAP Attribute : cn
Group Object Classes : group
Preserve Group Inheritance: ON
Ignore Missing Groups: OFF (Default)
Membership LDAP Attribute : member
Membership Attribute Type: DN
Membership User LDAP Attribute: sAMAccountName
LDAP Filter : <You can leave it blank if you don't want to filter any group>
Mode: READ_ONLY
User Groups Retrieve Strategy: LOAD_GROUPS_BY_MEMBER_ATTRIBUTE
Member-Of LDAP Attribute: memberOf
Mapped Group Attributes:
Drop non-existing groups during sync: ON
Related
I am using Streamset Data Collector version 3.19.1, currently am trying to integrate Streamset with LDAP server for authentication, I am successful with the integration however we are facing difficulties in configuring the roles and groups like the way it is in File based.
i.e in file based you have three things to configure a)user b)role c) group
<user name>: MD5:<md5-text>, user, <role> [, <additional role>, <additional role>...] [, group:<group>, group:<additional group>...]
if you look at above syntax, there is user name which can be user defined, then there is user which is fixed value, then we have role it can be one of (Admin,Manager,Creator and guest) and last we have group which us again user defined this can be used for Multitenancy.
however now comparing to LDAP configuration provided by streamset we have only following attributes to configure.
<ldap group>:<SDC role>,<additional SDC role>,<additional SDC role>);<ldap group>:<SDC role>,<additional SDC role>...
from above syntax we can see can be configured which is user defined and SDC role which can be one of the following(Admin,Manager,Creator and guest) with this the configuration will look like
DEV:creator;OPS:manager;
how can I configure LDAP rule mapping for group and role, currently i can only map for role in LDAP, requesting anyone to help here, i tried all my best to resolve unfortunately couldnt find any solution.
You can use the same configuration for groups as well. Following from my environment.
http.authentication.ldap.role.mapping=operators:admin;
To get the groups ldap-login-conf needs to be configured like following:-
roleBaseDn="OU=operations,OU=test_groups,OU=groups,OU=sdc,DC=example,DC=COM"
roleNameAttribute="cn"
roleMemberAttribute="member"
roleObjectClass="group"
roleFilter="member={dn}";
I'm configuring the Pentaho 6.1 server to work with LDAP for the first time.
Which parameters do I need to change? And how?
I've tried to follow the instructions on this manual page:
https://help.pentaho.com/Documentation/6.1/0P0/150/010/030
After I change "provider=jackrabbit" to "provider=ldap" in the file: security.properties, when I try to enter on the Pentaho Login page with the administrator user, I get an error like the user doesn't exist.
This is my actual LDAP parameters:
Server host: ldap-sweft.ferint.kabal
Server port: 389
Use TLS connection: no
Bind DN: zenit
Bind Password: asddsa
Users base DN: dc=ferint,dc=kabal
Enable Group Retrieval: yes
Search scope: subtree
User authentication:
User search filter: (&(objectCategory=user)(sAMAccountName=$USER$))
Identity Attribute: sAMAccountName
Get all users filter: (objectCategory=user)
Group support:
Users of group filter: (&(objectCategory=user)(memberOf=$GROUP$))
Groups base DN: (vuoto)
Group Search Filter: (objectCategory=group)
Group Name Attribute: cn
Groups of user filter: (&(objectCategory=group)(member=$USER$))
This is how I changed the file: pentaho-solutions/system/applicationContext-security-ldap.properties
New file:
contextSource.providerUrl=ldap://ldap-sweft.ferint.kabal:389/
contextSource.userDn=dc\=ferint\\zenit
contextSource.password=asddsa
userSearch.searchBase=ou\=Utenti,dc\=ferint,dc\=kabal
userSearch.searchFilter=(sAMAccountName\={0})
populator.convertToUpperCase=false
populator.groupRoleAttribute=cn
populator.groupSearchBase=ou\=Gruppi,dc\=ferint,dc\=kabal
populator.groupSearchFilter=(member:1.2.840.113556.1.4.1941:={0})
populator.rolePrefix=
populator.searchSubtree=true
allAuthoritiesSearch.roleAttribute=cn
allAuthoritiesSearch.searchBase=ou\=Gruppi,dc\=ferint,dc\=kabal
allAuthoritiesSearch.searchFilter=(objectClass\=organizationalRole)
allUsernamesSearch.usernameAttribute=sAMAccountName
allUsernamesSearch.searchBase=ou\=Utenti,dc\=ferint,dc\=kabal
allUsernamesSearch.searchFilter=objectClass\=person
adminRole=cn\=Administrator,ou\=roles
adminUser=cn\=zenit,ou\=Chiavi\ esoteriche,dc\=ferint,dc\=kabal
Original file:
contextSource.providerUrl=ldap\://localhost\:10389/ou\=system
contextSource.userDn=uid\=admin,ou\=system
contextSource.password=secret
userSearch.searchBase=ou\=users
userSearch.searchFilter=(cn\={0})
populator.convertToUpperCase=false
populator.groupRoleAttribute=cn
populator.groupSearchBase=ou\=roles
populator.groupSearchFilter=(roleOccupant\={0})
populator.rolePrefix=
populator.searchSubtree=false
allAuthoritiesSearch.roleAttribute=cn
allAuthoritiesSearch.searchBase=ou\=roles
allAuthoritiesSearch.searchFilter=(objectClass\=organizationalRole)
allUsernamesSearch.usernameAttribute=uid
allUsernamesSearch.searchBase=ou\=users
allUsernamesSearch.searchFilter=objectClass\=Person
adminRole=cn\=Administrator,ou\=roles
What parameters should I change?
pentaho-solutions/system/data-access/settings.xml
pentaho-solutions/system/pentaho.xml
pentaho-solutions/system/repository.spring.properties
pentaho-solutions/system/applicationContext-spring-security.xml
I connected our active directory to keycloak (4.0.0.Beta1) and imported the users - this works fine.
But the username should be filled from sAMAccountName. So i changed the Username LDAP attribute to that.
But after clicking Synchronize all users i am getting this error in the console window:
8:20:13,372 ERROR [org.keycloak.storage.ldap.LDAPStorageProviderFactory] (default task-119) Failed during import user from LDAP: org.keycloak.models.ModelException: User returned from LDAP has null username! Check configuration of your LDA
mappings. Mapped username LDAP attribute: cn, user DN: CN=Mustermann Max,OU=Normung,OU=Mech,OU=Konstruktion,OU=Abteilungen,DC=company,DC=org, attributes from LDAP: {whenChanged=[2017037125253.0Z], whenCreated=[20140520092805.0
], mail=[Max.Mustermann#company.org], givenName=[Max], sn=[Mustermann], userAccountControl=[66048], pwdLastSet=[130750516258418527]}
at org.keycloak.storage.ldap.LDAPUtils.getUsername(LDAPUtils.java:113)
at org.keycloak.storage.ldap.LDAPStorageProviderFactory$3.run(LDAPStorageProviderFactory.java:521)
at org.keycloak.models.utils.KeycloakModelUtils.runJobInTransaction(KeycloakModelUtils.java:227)
at org.keycloak.storage.ldap.LDAPStorageProviderFactory.importLdapUsers(LDAPStorageProviderFactory.java:514)
at org.keycloak.storage.ldap.LDAPStorageProviderFactory.syncImpl(LDAPStorageProviderFactory.java:469)
at org.keycloak.storage.ldap.LDAPStorageProviderFactory.sync(LDAPStorageProviderFactory.java:407)
...
I tried some mappers (especially username) but with no luck. It seems that there are only a few attributes read from the ldap server (see attributes from LDAP:... in the output).
Namely: whenChanged, whenCreated, mail, givenName, sn, userAccountControl, pwdLastSet.
How can i get the sAMAcountName attribute as username?
I have just tested it in 4.1.0.Final and there it works when you change the Username LDAP attribute to sAMAccountName and additionally the
LDAP Attribute in the username mapper also to sAMAccountName.
I tried some mappers (especially username) but with no luck.
Your question suggest, that you already tried doing something in the username mappers. So you were definetly on the right track. Either there was a bug in your version, or the two fields didn't match correctly.
For me, it worked. Please find the below process:
1) In User Federation -> LDAP Provider -> There is a setting called "UserName LDAP Attribute". (as shown here ldap provider settings)
2) Select Mappers tab and edit "username" mapper and change to following settings: username mapper settings
How to delete a user from a role or disable the user from that particular role in JasperReports Server? But I need the role in the main tenant. It should only be removed from the role (Group)
What I am doing now is a goof up like:
Method: DELETE
URL: http://localhost:8080/reportservice/rest/user/username|TenantID
Payload:
<user>
<tenantId>tenantID</tenantId>
<username>{username}</username>
<emailAddress>{emailAddress}</emailAddress>
<enabled>false</enabled>
<externallyDefined>true</externallyDefined>
<fullName>{fullName}</fullName>
<roles>
<externallyDefined>false</externallyDefined>
<roleName>ROLE_USER</roleName>
</roles>
</user>
From the REST API Reference Manual (6.3.0):
To modify the properties of a user account, put all desired information in a user descriptor, and include it in a
PUT request to the users service, with the existing user ID (username) specified in the URL.
In the community edition of the server, or commercial editions without organizations, use the first form of
the URL.
In commercial editions with organizations, use the second URL to specify the user’s organization. When
specifying the organization, use its unique ID, not its path. When logged in as the system admin
(superuser), use the first URL to modify users of the root organization.
To modify a user, the user ID in the URL must already exist on the server or in the organization. If the user ID
doesn’t exist, a user account will be created, as described in 21.3, “Creating a User,” on page 155.
Method: PUT
URL:
http://:/jasperserver[-pro]/rest_v2/users/userID
http://:/jasperserver[-pro]/rest_v2/organizations/orgID/users/userID
Content-Type:
application/xml
application/json
Content:
A user descriptor that includes the properties you want to change. Do not
specify the following properties:
username – Specified in the URL and cannot be modified in the descriptor.
tenantID – Specified in the URL and cannot be modified in the descriptor.
externallyDefined – Computed automatically by the server.
previousPasswordChangeTime – Computed automatically by the server.
Return Value on Success:
200 OK – The user properties were successfully
updated.
Typical Return Values on Failure:
404 Not Found – When the organization ID cannot be
resolved.
To add a role to the user, specify the entire list of roles with the desired role added. To remove a role from a
user, specify the entire list of roles with the desired role removed. The following example shows the descriptor
in JSON format:
{
"enabled":true,
"password":"newPassword",
"roles":[
{"name":"ROLE_USER"},
{"name":"ROLE_STOREMANAGER", "tenantId":"organization_1"}]
}
In case of LDAP authenticaion, what are the parameters that are generally used for authentication. I guess using DN would be a headache for users logging in via ldap because it is too large to remember.
How is the option of using uid or sAMAccountName for authentication where in my implementation, I retrieve the dn of the corresponding uid or sAMAccountName and proceed to authentication.
Am I going the right track?
In LDAP, a connection or session can be authenticated. When an LDAP client makes a new connection to an LDAP directory server, the connection has an authorization state of anonymous. The LDAP client can request that the authorization state be changed by using the BIND request.
A BIND request has two forms: simple and SASL. Simple uses a distinguished name and a password, SASL uses one of a choice of mechanisms, for example, PLAIN, LOGIN, CRAM-MD5, DIGEST-MD5, GSSAPI, and EXTERNAL - all of which except for GSSAPI and EXTERNAL are too weak to use in production scenarios or mission-critical areas.
To Use the simple BIND, construct a BIND request and transmit it to the LDAP directory server. The LDAP directory server will respond with a BIND response in which is contained a result code. The result code is an integer, anything other zero indicates that the BIND request failed. If the result code is zero, the BIND request succeeded and the session authorization state has been changed to that of the distinguished name used in the BIND request.
Each subsequent BIND request on the same connection/session causes the authorization state to be set to anonymous and each successive successful BIND request on the same connection/session causes the authorization state to be set to the authorization state associated with the authentication ID, which is the distinguished name in the case of the simple BIND, but might be something else entirely where SASL is used - modern professional quality servers can map the incoming names to different DNs.
Whichever language is used, construct a BIND request, transmit it to the server, and interpret the response.
Update:
If the distinguished name is not known, or is too cumbersome (often the case with web application users who don't know how they are authenticated and would not care if they did know), the LDAP application should search the directory for the user. A successful search response always contains the distinguished name, which is then used in a simple BIND.
The search contains at a minimum, the following:
base object: a distinguished name superior to the user, for example, dc=example,dc=com
a scope: base level, one level below base, or subtree below base. For example, if users are located subordinate to ou=people,dc=example,dc=com, use base object ou=people,dc=example,dc=com and a scope of one-level. These search parameters find entries like: uid=user1,ou=people,dc=example,dc=com
a filter: narrows down the possible search results returned to the client, for example (objectClass=inetOrgPerson)
a list of requested attributes: the attributes from an entry to return to the client. In this case, use 1.1, which means no attributes and returns on the DN (distinguished name), which is all that is required for the simple BIND.
see also
the links in the about section here
LDAP servers only understand LDAP queries; they don't have "usernames" like you and I are used to.
For LDAP, to authenticate someone, you need to send a distinguished name of that person's (or entity's) entry in LDAP; along with their password.
Since you mentioned sAMAccountName I am assuming you are working with Active Directory. Active Directory allows anonymous binds - this means you can connect to it without providing any credentials; but cannot do any lookups without providing credentials.
If you are using python-ldap and Cython (and not IronPython which has access to the various .NET APIs that make this process very easy); then you follow these steps.
Typically you use a pre-set user that has appropriate rights to the tree, and connect to the directory with that user first, and then use that user's access for the rest of the authentication process; which generally goes like this:
Connect to AD with the pre-set user.
Query active directory with the pre-set user's credentials and search for the distinguished name based on the sAMAccountName that the user will enter as their "username" in your form.
Attempt to connect again to Active Directory using the distinguished name from step 2, and the password that the user entered in their form.
If this connection is successful, then the user is authenticated.
So you need two main things:
The login attribute (this is the "username" that LDAP understands)
A LDAP query that fetches information for your users
Following is some rough code that can do this for you:
AD_USER = 'your super user'
AD_PASSWORD = 'your super user password'
AD_BIND_ATTR = 'userPrincipalName' # this is the "login" for AD
AD_URL = "ldap://your-ad-server"
AD_DN = "DC=DOMAIN,DC=COM"
AD_LOGIN_ATTR = 'sAMAccountName' # this is what you user will enter in the form
# as their "login" name,
# this is what they use to login to Windows
# A listing of attributes you want to fetch for the user
AD_ATTR_SEARCH = ['cn',
'userPrincipalName',
'distinguishedName',
'mail',
'telephoneNumber','sAMAccountName']
def _getbinduser(user):
""" This method returns the bind user string for the user"""
user_dn = AD_DN
login_attr = '(%s=%s)' % (AD_LOGIN_ATTR,user)
attr_search = AD_ATTR_SEARCH
conn = ldap.initialize(AD_URL)
conn.set_option(ldap.OPT_REFERRALS,0)
conn.set_option(ldap.OPT_PROTOCOL_VERSION,3)
try:
conn.bind(AD_USER,AD_PASSWORD)
conn.result()
except:
exceptionType, exceptionValue, exceptionTraceback = sys.exc_info()
# Exit the script and print an error telling what happened.
sys.exit("LDAP Error (Bind Super User)\n ->%s" % exceptionValue)
try:
result = conn.search_s(user_dn,
ldap.SCOPE_SUBTREE,
login_attr, attr_search)
except:
exceptionType, exceptionValue, exceptionTraceback = sys.exc_info()
# Exit the script and print an error telling what happened.
sys.exit("LDAP Error (Search)\n ->%s" % exceptionValue)
# Return the user's entry from AD, which includes
# their 'distinguished name'
# we use this to authenticate the credentials the
# user has entered in the form
return result[0][1]
def authenticate(user,password):
bind_attr = AD_BIND_ATTR
user_dn = AD_DN
login_attr = '(%s=%s)' % (AD_LOGIN_ATTR,user)
data = _getbinduser(user)
if len(data) == 1:
return None
# Information we want to return from the directory
# for each user, season to taste.
info = {}
info['name'] = data['cn'][0]
info['email'] = data['mail'][0]
try:
info['phone'] = data['telephoneNumber'][0]
except KeyError:
info['phone'] = 'Not Available'
conn = ldap.initialize(Config.AD_URL)
conn.set_option(ldap.OPT_REFERRALS,0)
conn.set_option(ldap.OPT_PROTOCOL_VERSION,3)
try:
# Now we have the "bind attribute" (LDAP username) for our user
# we try and connect to see if LDAP will authenticate
conn.bind(data[bind_attr][0],password)
conn.search(user_dn,ldap.SCOPE_SUBTREE,login_attr,None)
conn.result()
return info
except (ldap.INVALID_CREDENTIALS,ldap.OPERATIONS_ERROR):
return None
One small expansion on Terry's excellent comment. If you store all your users in the same part of the DIT, and use the same attribute to identify them, you can programmatically construct the DN, rather than searching for it.