📜 ⬆️ ⬇️

JIRA 4.1 integration with Active Directory

The challenge was to integrate JIRA 4.1 with Active Directory with the following conditions:
  1. Synchronization of users and groups (version 4.1 does not support synchronization)
  2. Transparent authentication (JIRA uses email addresses as logins)
  3. Filling user properties (phone number)
  4. Users who are not in AD should be flagged

And that's how I decided it:


The topic was published at the request of habrovchanin XoJIoD - so there are advantages to it.

JIRA Setup


JIRA 4.1 has a ready-made LDAP integration mechanism, but the only thing that it can do is to check passwords, i.e. About any synchronization of speech does not go. This step can be skipped, because in the future, we will entrust the verification of SPNEGO passwords.
')
So, go to JIRA as an administrator and go to the section Administration → System → LDAP and fill out the form:
image
where:


If you receive a PartialResultException error, try using the Global Catalog port (3268). If everything is filled out correctly, an LDAP Authentication successful message should appear and, just below, the contents of the XML file for integration. Copy it, stop the JIRA server and replace with this content what was in the $ JIRA_HOME / atlassian-jira / WEB-INF / classes / osuser.xml file . Taking this opportunity, we also enable the execution of Jelly scripts. To do this, in the $ JIRA_HOME / bin / setenv.sh file (for Linux), add the line -Djira.jelly.on = true to the JAVA_OPTS parameter.

After that, rerun JIRA and make sure that users from AD use passwords from there.

Creating a jelly script


I wrote the program for generating the script that creates users and groups at the very beginning, and then it was a pity to throw it out. Therefore, if you add groups to the authenticator or service (see below), you can also skip this step.

In the documentation on the Atlassian website, an open source utility was found that generates a Jelly script that creates LDAP users in JIRA. However, I also needed to create groups, therefore, having armed myself with documentation on Jelly tags , I added this utility to such a state:
import java.io.File; import java.io.FileReader; import java.util.Properties; import java.util.Random; import javax.naming.NamingEnumeration; import javax.naming.directory.*; import org.apache.commons.lang3.StringEscapeUtils; public class LDAPImporter { public static void main(String[] args) throws Exception { Properties properties = new Properties(); File path = new File(LDAPImporter.class.getProtectionDomain() .getCodeSource().getLocation().toURI()); if (!path.isDirectory()) { path = path.getParentFile(); } properties.load(new FileReader(new File(path, "LDAP.properties"))); properties.put("java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory"); DirContext groupContext = new InitialDirContext(properties); SearchControls groupControls = new SearchControls(); groupControls.setReturningAttributes(new String[] { (String) properties.get("groupNameAttribute"), (String) properties.get("groupDNAttribute") }); groupControls.setSearchScope(SearchControls.SUBTREE_SCOPE); System.out.println("<JiraJelly xmlns:jira=\"jelly:com.atlassian.jira.jelly.JiraTagLib\">\n"); NamingEnumeration results = groupContext.search( (String) properties.get("baseDN"), (String) properties.get("groupFilter"), groupControls); //    (Organisation Unit') while (results != null && results.hasMoreElements()) { SearchResult result = (SearchResult) results.next(); Attribute attribute = result.getAttributes().get( (String) properties.get("groupNameAttribute")); if (attribute == null) { continue; } String groupName = (String) attribute.get(); String groupDN = (String) result.getAttributes() .get((String) properties.get("groupDNAttribute")).get(); //   System.out.println("<jira:CreateGroup group-name=\"" + StringEscapeUtils.escapeXml("[AD] " + groupName) + "\"/>\n"); DirContext userContext = new InitialDirContext(properties); SearchControls userControls = new SearchControls(); userControls.setReturningAttributes(new String[] { (String) properties.get("userLoginAttribute"), (String) properties.get("userNameAttribute"), (String) properties.get("userMailAttribute") }); userControls.setSearchScope(SearchControls.ONELEVEL_SCOPE); NamingEnumeration users = userContext.search(groupDN, (String) properties.get("userFilter"), userControls); //      while (users != null && users.hasMoreElements()) { SearchResult user = (SearchResult) users.next(); attribute = user.getAttributes().get( (String) properties.get("userLoginAttribute")); if (attribute == null) { continue; } String userLogin = (String) attribute.get(); attribute = user.getAttributes().get( (String) properties.get("userNameAttribute")); String userName; if (attribute != null) { userName = (String) attribute.get(); } else { userName = userLogin; } attribute = user.getAttributes().get( (String) properties.get("userMailAttribute")); String userMail; if (attribute != null) { userMail = (String) attribute.get(); } else { userMail = userLogin.replace(' ', '.') + '@' + properties.get("userMailDomain"); } /* * ..     AD, *    */ String password = Integer.toHexString(new Random().nextInt()); //   System.out.println("<jira:CreateUser username=\"" + StringEscapeUtils.escapeXml(userLogin.toLowerCase()) + "\" password=\"" + password + "\" confirm=\"" + password + "\" fullname=\"" + StringEscapeUtils.escapeXml(userName) + "\" email=\"" + StringEscapeUtils.escapeXml(userMail) + "\"/>\n"); //     System.out.println("<jira:AddUserToGroup username=\"" + StringEscapeUtils.escapeXml(userLogin.toLowerCase()) + "\" group-name=\"" + StringEscapeUtils.escapeXml("[AD] " + groupName) + "\"/>\n"); } } System.out.println("</JiraJelly>"); } } 

Settings are taken from the file LDAP.properties, located in the same directory with the program, it looks like this:
 #   LDAP java.naming.provider.url=ldap://127.0.0.1:389 # DN    java.naming.security.principal=cn=test,ou=users,dc=local,dc=domain #   java.naming.security.credentials=password #  DN baseDN=ou=users,dc=local,dc=domain #     groupNameAttribute=name #   DN  groupDNAttribute=distinguishedName #   groupFilter=(objectclass=organizationalUnit) #     userLoginAttribute=mail #     (  ,  ) userNameAttribute=displayName #    e-mail  (  ,   + @ +  ) userMailAttribute=mail #   userMailDomain=local.domain #   userFilter=(objectclass=user) 

The script obtained in this way we feed Jira in the section Administration → Options & Settings → Jelly Runner and after some thought she creates users and groups.

Transparent Authentication


The idea of ​​transparent authentication is as follows: authenticates SPNEGO users and passes the login of the already logged in user to the JARA variable REMOTE USER. The downside is that with such a scheme, users who are not in AD will not be able to log into JIRA , but I didn’t think of anything better.

SPNEGO

First, we test the performance of SPNEGO. To do this, download the three files: krb5.conf , login.conf and HelloKDC.java .
In the krb5.conf file in the [libdefaults] section, one line is missing , do not forget to enter it. After changing the settings, the krb5.conf file should look like this:
 [libdefaults] default_realm = LOCAL.DOMAIN default_tkt_enctypes = aes128-cts rc4-hmac des3-cbc-sha1 des-cbc-md5 des-cbc-crc default_tgs_enctypes = aes128-cts rc4-hmac des3-cbc-sha1 des-cbc-md5 des-cbc-crc permitted_enctypes = aes128-cts rc4-hmac des3-cbc-sha1 des-cbc-md5 des-cbc-crc [realms] LOCAL.DOMAIN = { kdc = dc5.local.domain default_domain = LOCAL.DOMAIN } [domain_realm] .LOCAL.DOMAIN = LOCAL.DOMAIN 

HelloKDC.java:
 ... // Domain (pre-authentication) account final String username = "test"; // Password for the pre-auth acct. final String password = "password"; // Name of our krb5 config file final String krbfile = "krb5.conf"; // Name of our login config file final String loginfile = "login.conf"; // Name of our login module final String module = "spnego-client"; ... 

Compile and run the HelloKDC.java file, if everything went well, it will output a bunch of lines and at the end Connection test successful.

Next, download the Support Tools utility set, install them on the administrator's domain computer and execute the commands:
 setspn.exe -A HTTP/domain1 test setspn.exe -A HTTP/domain2 test ... setspn.exe -A HTTP/domainN test 

where

Download the SPNEGO distribution and put it in the $ JIRA_HOME / lib directory. Open the $ JIRA_HOME / conf / web.xml file and add to the end (but before closing the web-app tag) the following lines:
 <filter> <filter-name>SpnegoHttpFilter</filter-name> <filter-class>net.sourceforge.spnego.SpnegoHttpFilter</filter-class> <init-param> <param-name>spnego.allow.basic</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>spnego.allow.localhost</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>spnego.allow.unsecure.basic</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>spnego.login.client.module</param-name> <param-value>spnego-client</param-value> </init-param> <init-param> <param-name>spnego.krb5.conf</param-name> <param-value>krb5.conf</param-value> </init-param> <init-param> <param-name>spnego.login.conf</param-name> <param-value>login.conf</param-value> </init-param> <init-param> <param-name>spnego.preauth.username</param-name> <param-value>test</param-value> </init-param> <init-param> <param-name>spnego.preauth.password</param-name> <param-value>password</param-value> </init-param> <init-param> <param-name>spnego.login.server.module</param-name> <param-value>spnego-server</param-value> </init-param> <init-param> <param-name>spnego.prompt.ntlm</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>spnego.logger.level</param-name> <param-value>1</param-value> </init-param> </filter> <filter-mapping> <filter-name>SpnegoHttpFilter</filter-name> <url-pattern>*</url-pattern> </filter-mapping> 

Remember to replace your username and password with your own.

Authenticator

The time has come to write an authenticator whose task is to get the login of the logged-in user from SPNEGO, add the mail domain to this login (since the JIRA logs are postal addresses) and filling in the property containing the phone for users who do not yet have this property. . In the delivery of the SDK for JIRA 4.x plug-ins, there was no place for the JiraOsUserAuthenticator class, on the basis of which our authenticator will be written, since this class is already obsolete. Therefore, it must be manually added to the libraries when compiling; you can take it in $ JIRA_HOME / atlassian-jira / WEB-INF / classes / com / atlassian / jira / security / login / .

Actually, the authenticator code:
 import java.security.Principal; import java.util.Properties; import java.util.Random; import javax.naming.NamingEnumeration; import javax.naming.directory.Attribute; import javax.naming.directory.DirContext; import javax.naming.directory.InitialDirContext; import javax.naming.directory.SearchControls; import javax.naming.directory.SearchResult; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.atlassian.core.user.preferences.Preferences; import com.atlassian.jira.ComponentManager; import com.atlassian.jira.security.login.JiraOsUserAuthenticator; import com.atlassian.jira.user.preferences.UserPreferencesManager; import com.atlassian.jira.user.util.UserUtil; import com.opensymphony.user.Group; import com.opensymphony.user.User; @SuppressWarnings("deprecation") public class RemoteAuthenticator extends JiraOsUserAuthenticator { private static final long serialVersionUID = 1L; private UserPreferencesManager preferencesManager = ComponentManager .getInstance().getUserPreferencesManager(); //   LDAP private final String ADDRESS_NAME = "java.naming.provider.url"; private final String ADDRESS_VALUE = "ldap://127.0.0.1:389"; // DN    private final String LOGIN_NAME = "java.naming.security.principal"; private final String LOGIN_VALUE = "cn=test,ou=users,dc=local,dc=domain"; //   private final String PASSWORD_NAME = "java.naming.security.credentials"; private final String PASSWORD_VALUE = "password"; //  DN private final String BASEDN = "ou=users,dc=local,dc=domain"; //   private final String LOGIN_ATTRIBUTE = "mail"; //   (  ,  ) private final String NAME_ATTRIBUTE = "displayName"; //    (  , //   + @ +  ) private final String MAIL_ATTRIBUTE = "mail"; //   private final String PHONE_ATTRIBUTE = "telephoneNumber"; //   private final String DOMAIN = "local.domain"; @Override public Principal getUser(HttpServletRequest request, HttpServletResponse response) { Principal user = null; if (request.getSession() != null && request.getSession(). getAttribute(JiraOsUserAuthenticator.LOGGED_IN_KEY) != null) { //    user = (Principal) request.getSession().getAttribute( JiraOsUserAuthenticator.LOGGED_IN_KEY); } else { String remote = request.getRemoteUser(); if (remote != null) { //    SPNEGO if (!remote.endsWith('@' + DOMAIN)) { /* *     , *    . *      , *      */ remote += '@' + DOMAIN; } user = getUser(remote.toLowerCase()); if (user == null) { /* *       JIRA, *  */ try { user = createUser(remote.toLowerCase()); } catch (Exception exception) { exception.printStackTrace(System.err); } } else { Preferences preferences = preferencesManager .getPreferences((User) user); String phone = preferences .getString(UserUtil.META_PROPERTY_PREFIX + "mobile"); if (phone == null || phone.isEmpty()) { /* *       , *  */ try { phone = getPhone(remote.toLowerCase()); if (phone != null) { preferences.setString( UserUtil.META_PROPERTY_PREFIX + "mobile", phone); } } catch (Exception exception) { exception.printStackTrace(System.err); } } } request.getSession().setAttribute( JiraOsUserAuthenticator.LOGGED_IN_KEY, user); request.getSession().setAttribute( JiraOsUserAuthenticator.LOGGED_OUT_KEY, null); } } return user; } private User createUser(String login) throws Exception { DirContext context = null; try { Properties properties = new Properties(); properties.put(ADDRESS_NAME, ADDRESS_VALUE); properties.put(LOGIN_NAME, LOGIN_VALUE); properties.put(PASSWORD_NAME, PASSWORD_VALUE); properties.put("java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory"); ComponentManager componentManager = ComponentManager.getInstance(); UserUtil userUtil = componentManager.getUserUtil(); context = new InitialDirContext(properties); SearchControls controls = new SearchControls(); controls.setReturningAttributes(new String[] { "distinguishedName", NAME_ATTRIBUTE, MAIL_ATTRIBUTE, PHONE_ATTRIBUTE }); controls.setSearchScope(SearchControls.SUBTREE_SCOPE); NamingEnumeration users = context.search(BASEDN, '(' + LOGIN_ATTRIBUTE + '=' + login + ')', controls); if (users != null && users.hasMoreElements()) { /* *     ( ) *   AD */ SearchResult result = (SearchResult) users.next(); Attribute attribute = result.getAttributes() .get(NAME_ATTRIBUTE); String userName; if (attribute != null) { userName = (String) attribute.get(); } else { userName = login; } attribute = result.getAttributes().get(MAIL_ATTRIBUTE); String userMail; if (attribute != null) { userMail = (String) attribute.get(); } else { userMail = login.replace(' ', '.') + '@' + DOMAIN; } attribute = result.getAttributes().get(PHONE_ATTRIBUTE); String userPhone = null; if (attribute != null) { userPhone = ((String) attribute.get()).replace("(", "") .replace(")", ""); } String userDN = (String) result.getAttributes() .get("distinguishedName").get(); //    String userPassword = Integer.toHexString(new Random().nextInt()); //   User user = userUtil.createUserNoEvent(login, userPassword, userMail, userName); //   AD  ,   if (userPhone != null) { preferencesManager.getPreferences(user).setString( UserUtil.META_PROPERTY_PREFIX + "mobile", userPhone); } /* *     JIRA,   AD, *    */ int index = userDN.indexOf("OU=") + 3; String groupName = "[AD] " + userDN.substring(index, userDN.indexOf(',', index)); Group group = userUtil.getGroup(groupName); if (group != null) { userUtil.addUserToGroup(group, user); } return user; } else { return null; } } finally { if (context != null) { context.close(); } } } private String getPhone(String username) throws Exception { DirContext context = null; try { Properties properties = new Properties(); properties.put(ADDRESS_NAME, ADDRESS_VALUE); properties.put(LOGIN_NAME, LOGIN_VALUE); properties.put(PASSWORD_NAME, PASSWORD_VALUE); properties.put("java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory"); context = new InitialDirContext(properties); SearchControls controls = new SearchControls(); controls.setReturningAttributes(new String[] { PHONE_ATTRIBUTE }); controls.setSearchScope(SearchControls.SUBTREE_SCOPE); NamingEnumeration users = context.search(BASEDN, '(' + LOGIN_ATTRIBUTE + '=' + username + ')', controls); if (users != null && users.hasMoreElements()) { SearchResult result = (SearchResult) users.next(); Attribute attribute = result.getAttributes().get( PHONE_ATTRIBUTE); String userPhone = null; if (attribute != null) { userPhone = ((String) attribute.get()).replace("(", "") .replace(")", ""); } return userPhone; } else { return null; } } finally { if (context != null) { context.close(); } } } } 

Compile, put in $ JIRA_HOME / atlassian-jira / WEB-INF / classes , open the file $ JIRA_HOME / atlassian-jira / WEB-INF / classes / seraph-config.xml and change the line in it
 <authenticator class="com.atlassian.jira.security.login.JiraOsUserAuthenticator"/> 

on
 <authenticator class="RemoteAuthenticator"/> 


After that, you can restart JIRA, configure Kerberos authentication in your favorite browser, and test its functionality. For non-domain computers, a BASIC authorization window should appear, so that they can also log in (if they have AD accounts).

Service


In addition, a service was written that periodically synchronizes users in AD and JIRA and adds a text label to the names of those that are not in AD. Here is its code:
 import java.util.Map; import java.util.Properties; import java.util.Random; import javax.naming.NamingEnumeration; import javax.naming.directory.Attribute; import javax.naming.directory.DirContext; import javax.naming.directory.InitialDirContext; import javax.naming.directory.SearchControls; import javax.naming.directory.SearchResult; import com.atlassian.configurable.ObjectConfiguration; import com.atlassian.configurable.ObjectConfigurationException; import com.atlassian.core.user.preferences.Preferences; import com.atlassian.jira.ComponentManager; import com.atlassian.jira.event.user.UserEventType; import com.atlassian.jira.service.AbstractService; import com.atlassian.jira.user.preferences.UserPreferencesManager; import com.atlassian.jira.user.util.UserUtil; import com.opensymphony.module.propertyset.PropertySet; import com.opensymphony.user.Group; import com.opensymphony.user.User; @SuppressWarnings("deprecation") public class Importer extends AbstractService implements ObjectConfiguration { //   LDAP private final String ADDRESS_NAME = "java.naming.provider.url"; private String ADDRESS_VALUE = "ldap://127.0.0.1:389"; // DN    private final String LOGIN_NAME = "java.naming.security.principal"; private String LOGIN_VALUE = "cn=test,ou=users,dc=local,dc=domain"; //   private final String PASSWORD_NAME = "java.naming.security.credentials"; private String PASSWORD_VALUE = "password"; //  DN private final String BASE_NAME = "baseDN"; private String BASE_VALUE = "ou=users,dc=local,dc=domain"; //   private final String LOGIN_ATTR_NAME = "userLoginAttribute"; private String LOGIN_ATTR_VALUE = "mail"; //   (  ,  ) private final String NAME_ATTR_NAME = "userNameAttribute"; private String NAME_ATTR_VALUE = "displayName"; //    (  ,   + @ +  // ) private final String MAIL_ATTR_NAME = "userMailAttribute"; private String MAIL_ATTR_VALUE = "mail"; //   private final String PHONE_ATTR_NAME = "userPhoneAttribute"; private String PHONE_ATTR_VALUE = "telephoneNumber"; //   private final String DOMAIN_NAME = "userMailDomain"; private String DOMAIN_VALUE = "local.domain"; //   private final String FILTER_NAME = "userFilter"; private String FILTER_VALUE = "(objectClass=user)"; // ,     private final String FIRED_STRING_NAME = "firedString"; private String FIRED_STRING_VALUE = "z_fired"; @Override public void init(PropertySet properties) throws ObjectConfigurationException { super.init(properties); for (Object keyObject : properties.getKeys()) { String key = (String) keyObject; if (key.equals(ADDRESS_NAME)) { ADDRESS_VALUE = properties.getString(key); } else if (key.equals(LOGIN_NAME)) { LOGIN_VALUE = properties.getString(key); } else if (key.equals(PASSWORD_NAME)) { PASSWORD_VALUE = properties.getString(key); } else if (key.equals(BASE_NAME)) { BASE_VALUE = properties.getString(key); } else if (key.equals(NAME_ATTR_NAME)) { NAME_ATTR_VALUE = properties.getString(key); } else if (key.equals(LOGIN_ATTR_NAME)) { LOGIN_ATTR_VALUE = properties.getString(key); } else if (key.equals(MAIL_ATTR_NAME)) { MAIL_ATTR_VALUE = properties.getString(key); } else if (key.equals(PHONE_ATTR_NAME)) { PHONE_ATTR_VALUE = properties.getString(key); } else if (key.equals(DOMAIN_NAME)) { DOMAIN_VALUE = properties.getString(key); } else if (key.equals(FILTER_NAME)) { FILTER_VALUE = properties.getString(key); } else { FIRED_STRING_VALUE = properties.getString(key); } } } @Override public ObjectConfiguration getObjectConfiguration() throws ObjectConfigurationException { return this; } @Override public void run() { try { importUsers(); checkUsers(); } catch (Exception exception) { exception.printStackTrace(); } } //    AD   JIRA private void importUsers() throws Exception { DirContext context = null; try { Properties properties = new Properties(); properties.put(ADDRESS_NAME, ADDRESS_VALUE); properties.put(LOGIN_NAME, LOGIN_VALUE); properties.put(PASSWORD_NAME, PASSWORD_VALUE); properties.put("java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory"); ComponentManager componentManager = ComponentManager.getInstance(); UserPreferencesManager preferencesManager = componentManager .getUserPreferencesManager(); UserUtil userUtil = componentManager.getUserUtil(); context = new InitialDirContext(properties); SearchControls controls = new SearchControls(); controls.setReturningAttributes(new String[] { "distinguishedName", LOGIN_ATTR_VALUE, NAME_ATTR_VALUE, MAIL_ATTR_VALUE, PHONE_ATTR_VALUE }); controls.setSearchScope(SearchControls.SUBTREE_SCOPE); NamingEnumeration users = context.search(BASE_VALUE, FILTER_VALUE, controls); while (users != null && users.hasMoreElements()) { SearchResult result = (SearchResult) users.next(); Attribute attribute = result.getAttributes().get( LOGIN_ATTR_VALUE); if (userUtil.getUser((String) attribute.get()) != null) { //     JIRA continue; } String login = ((String) attribute.get()).toLowerCase(); attribute = result.getAttributes().get(NAME_ATTR_VALUE); String userName; if (attribute != null) { userName = (String) attribute.get(); } else { userName = login; } attribute = result.getAttributes().get(MAIL_ATTR_VALUE); String userMail; if (attribute != null) { userMail = (String) attribute.get(); } else { userMail = login.replace(' ', '.') + '@' + DOMAIN_VALUE; } attribute = result.getAttributes().get(PHONE_ATTR_VALUE); String userPhone = null; if (attribute != null) { userPhone = ((String) attribute.get()).replace("(", "") .replace(")", ""); } String userDN = (String) result.getAttributes() .get("distinguishedName").get(); //    String userPassword = Integer.toHexString(new Random() .nextInt()); //   User user = userUtil.createUserWithEvent(login, userPassword, userMail, userName, UserEventType.USER_CREATED); if (userPhone != null) { preferencesManager.getPreferences(user).setString( UserUtil.META_PROPERTY_PREFIX + "mobile", userPhone); } /* *     JIRA, *   AD,    */ int index = userDN.indexOf("OU=") + 3; String groupName = "[AD] " + userDN.substring(index, userDN.indexOf(',', index)); Group group = userUtil.getGroup(groupName); if (group != null) { userUtil.addUserToGroup(group, user); } } } finally { if (context != null) { context.close(); } } } //     ,    AD private void checkUsers() throws Exception { DirContext context = null; try { Properties properties = new Properties(); properties.put(ADDRESS_NAME, ADDRESS_VALUE); properties.put(LOGIN_NAME, LOGIN_VALUE); properties.put(PASSWORD_NAME, PASSWORD_VALUE); properties.put("java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory"); ComponentManager componentManager = ComponentManager.getInstance(); UserPreferencesManager preferencesManager = componentManager .getUserPreferencesManager(); UserUtil userUtil = componentManager.getUserUtil(); context = new InitialDirContext(properties); SearchControls controls = new SearchControls(); controls.setReturningAttributes(new String[] { PHONE_ATTR_VALUE }); controls.setSearchScope(SearchControls.SUBTREE_SCOPE); for (User user : userUtil.getAllUsers()) { NamingEnumeration users = context.search(BASE_VALUE, "(&(" + LOGIN_ATTR_VALUE + '=' + user.getName() + ')' + FILTER_VALUE + ')', controls); if (users == null || !users.hasMore()) { //    AD if (!user.getFullName().startsWith(FIRED_STRING_VALUE)) { user.setFullName(FIRED_STRING_VALUE + ' ' + user.getFullName()); } } else { Preferences preferences = preferencesManager. getPreferences(user); String phone = preferences.getString( UserUtil.META_PROPERTY_PREFIX + "mobile"); if (phone == null || phone.isEmpty()) { //        try { Attribute attribute = ((SearchResult) users.next()) .getAttributes().get(PHONE_ATTR_VALUE); if (attribute != null) { phone = ((String) attribute.get()).replace("(", "").replace(")", ""); } if (phone != null) { preferences.setString( UserUtil.META_PROPERTY_PREFIX + "mobile", phone); } } catch (Exception exception) { exception.printStackTrace(System.err); } } } } } finally { if (context != null) { context.close(); } } } @Override public boolean allFieldsHidden() { return false; } @Override public String getName() { return "LDAP Importer"; } @Override public String getDescription() { return "      LDAP    ,    LDAP"; } @SuppressWarnings("rawtypes") @Override public String getDescription(Map params) { return "      LDAP    ,    LDAP"; } @Override public String[] getEnabledFieldKeys() { return new String[] { ADDRESS_NAME, LOGIN_NAME, PASSWORD_NAME, FILTER_NAME, BASE_NAME, LOGIN_ATTR_NAME, NAME_ATTR_NAME, MAIL_ATTR_NAME, PHONE_ATTR_NAME, DOMAIN_NAME, FIRED_STRING_NAME }; } @Override public String getFieldDefault(String key) throws ObjectConfigurationException { if (key.equals(ADDRESS_NAME)) { return ADDRESS_VALUE; } else if (key.equals(LOGIN_NAME)) { return LOGIN_VALUE; } else if (key.equals(PASSWORD_NAME)) { return PASSWORD_VALUE; } else if (key.equals(BASE_NAME)) { return BASE_VALUE; } else if (key.equals(NAME_ATTR_NAME)) { return NAME_ATTR_VALUE; } else if (key.equals(LOGIN_ATTR_NAME)) { return LOGIN_ATTR_VALUE; } else if (key.equals(MAIL_ATTR_NAME)) { return MAIL_ATTR_VALUE; } else if (key.equals(PHONE_ATTR_NAME)) { return PHONE_ATTR_VALUE; } else if (key.equals(DOMAIN_NAME)) { return DOMAIN_VALUE; } else if (key.equals(FILTER_NAME)) { return FILTER_VALUE; } else { return FIRED_STRING_VALUE; } } @Override public String getFieldDescription(String key) throws ObjectConfigurationException { if (key.equals(BASE_NAME)) { return "     DN"; } else if (key.equals(MAIL_ATTR_NAME)) { return "  ,   \" + @ +  \""; } else if (key.equals(FIRED_STRING_NAME)) { return "        LDAP"; } else if (key.equals(NAME_ATTR_NAME)) { return "  ,   "; } return ""; } @Override public String[] getFieldKeys() { return new String[] { ADDRESS_NAME, LOGIN_NAME, PASSWORD_NAME, FILTER_NAME, BASE_NAME, LOGIN_ATTR_NAME, NAME_ATTR_NAME, MAIL_ATTR_NAME, PHONE_ATTR_NAME, DOMAIN_NAME, FIRED_STRING_NAME }; } @Override public String getFieldName(String key) throws ObjectConfigurationException { if (key.equals(ADDRESS_NAME)) { return "  LDAP"; } else if (key.equals(LOGIN_NAME)) { return "  LDAP"; } else if (key.equals(PASSWORD_NAME)) { return "  LDAP"; } else if (key.equals(BASE_NAME)) { return " DN"; } else if (key.equals(NAME_ATTR_NAME)) { return "  "; } else if (key.equals(LOGIN_ATTR_NAME)) { return "  "; } else if (key.equals(MAIL_ATTR_NAME)) { return "  "; } else if (key.equals(PHONE_ATTR_NAME)) { return " "; } else if (key.equals(DOMAIN_NAME)) { return " "; } else if (key.equals(FILTER_NAME)) { return " "; } else { return ""; } } @Override public int getFieldType(String key) throws ObjectConfigurationException { return 0; } @SuppressWarnings("rawtypes") @Override public Map getFieldValues(String key) throws ObjectConfigurationException { return null; } @SuppressWarnings("rawtypes") @Override public void init(Map params) { } @Override public boolean isEnabled(String key) { return true; } @Override public boolean isI18NValues(String key) { return false; } } 

Compiled jar file (using the Jira Plugin SDK) with the service put in $ JIRA_HOME / atlassian-jira / WEB-INF / lib / . You can add a service in Administration → System → Services

On this all, I hope the information will be useful to someone.

Source: https://habr.com/ru/post/141144/


All Articles