Thursday, July 3, 2014

Secure ATG BCC



  1. Use ProtocolSwitchServlet in the Servlet Pipeline that intercepts the request
  2. If the page request is made from a non-SSL port (port 80 or 8180 or 8840), then the same page request is made from its SSL equivalent (port 443 or 9443 or 8843, respectively).
  3. The http port number, and their SSL equivalent https port number are properties so that they are configurable through a Configuration.properties file.
  4. Open the command console on whatever operating system you are using and navigate to the directory where keytool.exe is located
    • Run the following command (where validity is the number of days before the certificate will expire):
    keytool -genkey -keyalg RSA -alias selfsigned -keystore keystore.jks -storepass password -validity 360 -keysize 2048
    • Fill in the prompts for your organization information. When it asks for your first and last name, enter the domain name of the server that users will be entering to connect to your application (e.g. www.google.com)
  5.  This new certificate needs to copy to the $JBOSS_HOME//server/pub/conf location
  6. The server.xml under $JBOSS_HOME/server/pub/deploy/jbossweb.sar will need to be modified to enable ssl.Please make sure that the following changes  are correct in server.xml
    1. <Connector protocol="HTTP/1.1" port="8180" address="${jboss.bind.address}"                connectionTimeout="20000" redirectPort="8543" /><Connector protocol="AJP/1.3" port="8109" address="${jboss.bind.address}"         redirectPort="8543" /><Connector protocol="HTTP/1.1" SSLEnabled="true"            port="8543" address="${jboss.bind.address}"           scheme="https" secure="true" clientAuth="false"            keystoreFile="${jboss.server.home.dir}/conf/keystore"           keystorePass="changeit" sslProtocol = "TLS" />
  7. Cofigurations
      • /servlet/dafpipeline/ProtocolSwitchServlet
        • $class=ProtocolSwitchServlet
        • insertAfterServlet=/atg/dynamo/servlet/dafpipeline/DynamoServlet
        • httpPort^=/atg/dynamo/Configuration.siteHttpServerPort
        • httpsPort^=/atg/dynamo/Configuration.httpsPort 
  8. Add ProtocolSwitchServlet in to initial.proprties
  9. Java Source
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class ProtocolSwitchServlet extends InsertableServletImpl {
 private static fina String SECURE_PROTOCOL= "https"
 public void service(DynamoHttpServletRequest pRequest,
                    DynamoHttpServletResponse pResponse) 
                    throws IOException, ServletException {
  
            if ((pRequest.getScheme() != null) 
                && !pRequest.getScheme().equalsIgnoreCase(SECURE_PROTOCOL)) {
                
  final final StringBuilder secureUrl = new StringBuilder(SECURE_PROTOCOL);
  final int httpsPort = this.getHttpsPort();//This value is taking Configuration.properties
  secureUrl.append("://");
  secureUrl.append(this.serverName);;//This value is taking Configuration.properties
         if (httpsPort != 0)) {
   secureUrl.append(":" + httpsPort);
  }
  secureUrl.append(pUrl);
  final String redirectURL = secureUrl.toString();
  pResponse.sendRedirect(pResponse.encodeRedirectURL(redirectURL));
            }else{
             this.passRequest(pRequest, pResponse);
         }
  }
}  

BCC LDAP Integration


Introduction

The ATG documentation has multiple references on LDAP integration but it was found to only affect authentication to dyn admin. This is a custom Integration which help us integrate ATG BCC with LDAP. We can use LDAP to authenticate BCC users and assign roles so that we can achieve controlled access of BCC using LDAP. 

Advantages
  • No need to  keep the LDAP admin username and password in ATG
  • LDAP admin can assign or revoke roles for a User
  1. Create User and Roles in LDAP

    • The Distinguished Name :- LDAP User : CN=Users,DC=test,DC=com
    • The Distinguished Name :  LDAP role   :OU=roles,DC=test,DC=com
    • Use sAMAccountName of LDAP user for login to BCC 
    • There should be one to one mapping between the ATG BCC roles ( Roles defined in DPI_ROLES table) and the roles in LDAP
    • For example, epubAdmin role in ATG should have a corresponding role in LDAP account with sAMAccountName as epubAdmin

  2.  LDAP authentication on BCC Login.

    • Override the findUser() method of IntrenalProfileFormHandler
    • Use the LDAPLoginManager java to authenticate and fetch the user details from LDAP
    • If LDAP authentication is successful, the following actions will be performed:
      • If a user with same login name exists in ATG then load that Profile from the repository. If not create a new profile in ATG repository.
      • If there is any mismatch between ATG and LDAP Data, synchronize LDAP user properties and roles with ATG using the modifyInternalProfileforLdapData() method of the LDAPLoginManager. 

  3. Avoid ATG password validation
    • /atg/userprofiling/InternalProfileUserAuthority
      • passwordHasher= /ads/LDAPNullPasswordHasher
  4. If the LDAP authentication fails, then show invalid user or password message
    • For that return the findUser() of the IntrenalProfileFormHandler with null value so that the parent method will add FormException to show invalid user or password message
  5. Set the  password  property of the user repositoryitem in InternalProfileAdaptor repository as non required
  6. Set /atg/dynamo/servlet/pipeline/ExpiredPasswordServlet.enabled=false to disable OOTB Expired Password configuration 
  7. Configurations
    1. Create /ads/LDAPNullPasswordHasher Component
      • Create a class LDAPNullPasswordHasher extends NullPasswordHasher
      • Override the checkPassword method return true;
    2. Create /ads/LDAPLoginManager
      • LDAP domain Name 
        • domainName=developerscrapped.com
    3. Modify /atg/userprofiling/InternalProfileUserAuthority
      • passwordHasher= /ads/LDAPNullPasswordHasher
    4. Modify /atg/userprofiling/InternalProfileFormHandler
      • Override the atg.userprofiling.ProfileForm#findUser(String, String, Repository, DynamoHttpServletRequest, DynamoHttpServletResponse) method

  8. Java Source
------------------------------------------------------------------------------------------------------------

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
public class LDAPLoginManager extends GenericService {
    private static final String AT_SIGN = "@";
    private UserDirectoryTools userDirectoryTools;
    private ProfileTools profileTools;
    private String domainName;
    public LDAPUserInfo authenticate(final String userName, final String password) {

        final LDAPUserInfo ldapUser = new LDAPUserInfo();

        final StringBuilder securityPrincipal = new StringBuilder().append(userName)
        .append(LDAPLoginManager.AT_SIGN)
        .append(this.domainName);

        final LDAPJNDIEnvironment ldapEnv = new LDAPJNDIEnvironment();

        ldapEnv.setProviderURL("ldap://localhost:389");

        ldapEnv.setSecurityAuthentication("simple");

        ldapEnv.setSecurityPrincipal(securityPrincipal.toString());

        ldapEnv.setSecurityCredentials(password);

        final DirContext loginctx = new InitialDirContext(ldapEnv);

        NamingEnumeration<SearchResult> results = null;

        String distinguishedName = null;

        final SearchControls controls = new SearchControls();

        controls.setSearchScope(SearchControls.SUBTREE_SCOPE);

        controls.setCountLimit(10);

        controls.setTimeLimit(60000);

        results = loginctx.search("CN=Users,DC=test,DC=com",
 MessageFormat.format("(&(objectClass=user)(sAMAccountName={0}))", userName), controls);

        if (results.hasMore()) {

            final SearchResult result = results.next();

            final Attributes attrs = result.getAttributes();

            ldapUser.addProperty("firstName", (String) attrs.get("givenName").get());

            distinguishedName = (String) attrs.get("distinguishedName").get();
        }
        results = loginctx.search("OU=roles,DC=test,DC=com",
 MessageFormat.format("(&(objectClass=group)(member={0}))", userName), controls);

        while (results.hasMore()) {

            final SearchResult result = results.next();

            final Attributes attrs = result.getAttributes();

            ldapUser.getLdapRoles().add((String) attrs.get("sAMAccountName").get());

        }
        return ldapUser;
    }
    public void modifyInternalProfileforLdapData(final LDAPUserInfo ldapUserInfo, 
      final RepositoryItem profileItem)
       throws RepositoryException {

        this.profileTools.updateProperties(ldapUserInfo.getLdapProperties(),
     profileItem);

        this.userDirectoryTools.removeAllRolesFromUser(profileItem.getRepositoryId());

        this.userDirectoryTools.assignRolesToUser(ldapUserInfo.getLdapRoles(),
       profileItem.getRepositoryId());

    }

}
 ------------------------------------------------------------------------------------------------------------
1
2
3
4
5
6
7
8
9
10
11
12
13
public class LDAPUserInfo {
    private final Dictionary ldapProperties = new ArrayDictionary();
    private final Set<String> ldapRoles = new HashSet<String>(15);
    public void addProperty(final String key, final String value) {
        this.ldapProperties.put(key, value);
    }
    public Dictionary getLdapProperties() {
        return this.ldapProperties;
    }
    public Set<String> getLdapRoles() {
        return this.ldapRoles;
    }
}
 ------------------------------------------------------------------------------------------------------------
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class ExtendedInternalProfileFormHandler extends ProfileFormHandler {
    
    @Override
    protected RepositoryItem findUser(final String pLogin,
                                      final String pPassword,
                                      final Repository pProfileRepository,
                                      final DynamoHttpServletRequest pRequest,
                                      final DynamoHttpServletResponse pResponse) throws RepositoryException, ServletException, IOException {
      
            LDAPUserInfo ldapUser;
            try {
                ldapUser = this.ldapLoginManager.authenticate(pLogin, pPassword);
            } catch (final Exception e) {
              
                return null;// super class method will add form exceptions
            }
          
            RepositoryItem profileItem = profileTools.getItem(pLogin, null, this.getLoginProfileType());
            if (profileItem == null) {

                profileItem = profileTools.createUser(pLogin);
            }
            this.ldapLoginManager.modifyInternalProfileforLdapData(ldapUser, profileItem);
      
       return super.findUser(pLogin, pPassword, pProfileRepository, pRequest, pResponse);
    }
}
------------------------------------------------------------------------------------------------------------
1
2
3
4
5
6
7
8
9
10
public class ExtendedInternalProfileTools extends CommerceProfileTools {
  
    public MutableRepositoryItem createUser(final String userName) throws RepositoryException {
        final MutableRepositoryItem profileItem = this.getProfileRepository().createItem(this.getDefaultProfileType());
        profileItem.setPropertyValue(this.getPropertyManager().getLoginPropertyName(), userName);
        this.getProfileRepository().addItem(profileItem);
        return profileItem;
    }
}