├── readme.md ├── step-01 ├── apache-ds-tutorial.ldif ├── images │ ├── ldap-00-create-server.png │ ├── ldap-01-configure-server.png │ ├── ldap-02-create-partition.png │ ├── ldap-03-start-server.png │ ├── ldap-04-create-connection.png │ ├── ldap-05-import-ldiff.png │ ├── ldap-06-select-ldiff.png │ └── ldap-07-browse-structure.png └── tutorial-01-install-ldap.md ├── step-02 ├── images │ ├── aem-00-setup-logging.png │ ├── aem-01-find-ldap-idp.png │ ├── aem-02-configure-ldap-idp.png │ ├── aem-03-find-synchandler.png │ ├── aem-04-configure-synchandler.png │ ├── aem-05-find-loginmodule.png │ ├── aem-06-configure-loginmodule.png │ └── aem-07-jaas-console.png ├── ldap-first-config-pkg.zip └── tutorial-02-configure-aem.md ├── step-03 ├── images │ ├── test-01-useradmin.png │ ├── test-02-newconfig.png │ ├── test-03-useradmin-withprops.png │ ├── test-04-jmx-console.png │ ├── test-05-jmx-synchandler.png │ ├── test-06-jmx-syncexternalusers.png │ └── test-07-useradmin-more.png ├── ldap-config-with-profile-pkg.zip ├── log-snip-01.md └── tutorial-03-test.md └── step-04 ├── authorizables.json ├── example-idp ├── .gitignore ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── adobe │ └── gems │ └── exampleidp │ └── impl │ ├── ExternalGroupImpl.java │ ├── ExternalIdentityImpl.java │ ├── ExternalUserImpl.java │ └── JsonFileIdentityProvider.java ├── images ├── example-01-find-json-idp.png ├── example-02-configure-idp.png ├── example-03-configure-synchandler.png ├── example-04-configure-loginmodule.png └── example-05-useradmin.png ├── json-idp-config-pkg.zip └── tutorial-04-example-idp.md /readme.md: -------------------------------------------------------------------------------- 1 | AEM 6.1 External Authentication Tutorial 2 | ======================================== 3 | 4 | This tutorial goes through the steps to setup and configure a LDAP server and the configure AEM to use example data for authentication. 5 | 6 | As a bonus track, we show how to build and configure your own identity provider. 7 | 8 | Sections 9 | -------- 10 | 11 | 1 [Setup Apache Directory Server](step-01/tutorial-01-install-ldap.md) 12 | 2 [Configure LDAP Authentication in AEM](step-02/tutorial-02-configure-aem.md) 13 | 3 [Test LDAP Authentication in AEM](step-03/tutorial-03-test.md) 14 | 15 | Bonus 16 | ----- 17 | 18 | 4 [Create your own IDP](step-04/tutorial-04-example-idp.md) 19 | 20 | Resources 21 | --------- 22 | * Example ldif file: [apache-ds-tutorial.ldif](step-01/apache-ds-tutorial.ldif) 23 | * Package with AEM 6.1 LDAP example configurations: 24 | * First config: [ldap-first-config-pkg.zip](step-02/ldap-first-config-pkg.zip) 25 | * Config with profile: [ldap-config-with-profile-pkg.zip](step-03/ldap-config-with-profile-pkg.zip) 26 | 27 | Links 28 | ----- 29 | * [Apache Jackrabbit Oak](http://jackrabbit.apache.org/oak) 30 | * [Oak Security Documentation](http://jackrabbit.apache.org/oak/docs/security/overview.html) 31 | * [Apache Directory](http://directory.apache.org/apacheds) 32 | * [Oracle JAAS Authentication Doc](http://docs.oracle.com/javase/8/docs/technotes/guides/security/jgss/tutorials/AcnOnly.html) 33 | -------------------------------------------------------------------------------- /step-01/apache-ds-tutorial.ldif: -------------------------------------------------------------------------------- 1 | # Sample LDIF data for the ApacheDS v1.0 Basic User's Guide 2 | # 3 | # Some sailors and their ships 4 | # userpassword for all persons is "pass" 5 | # 6 | version: 1 7 | 8 | dn: ou=people,o=sevenSeas 9 | objectclass: organizationalUnit 10 | objectclass: top 11 | description: Contains entries which describe persons (seamen) 12 | ou: people 13 | 14 | dn: ou=groups,o=sevenSeas 15 | objectclass: organizationalUnit 16 | objectclass: top 17 | description: Contains entries which describe groups (crews, for instance) 18 | ou: groups 19 | 20 | dn: ou=crews,ou=groups,o=sevenSeas 21 | objectclass: organizationalUnit 22 | objectclass: top 23 | description: Contains entries which describe ship crews 24 | ou: crews 25 | 26 | dn: ou=ranks,ou=groups,o=sevenSeas 27 | objectclass: organizationalUnit 28 | objectclass: top 29 | description: Contains entries which describe naval ranks (e.g. captain) 30 | ou: ranks 31 | 32 | # HMS Lydia Crew 33 | # -------------- 34 | 35 | dn: cn=Horatio Hornblower,ou=people,o=sevenSeas 36 | objectclass: person 37 | objectclass: organizationalPerson 38 | objectclass: inetOrgPerson 39 | objectclass: top 40 | cn: Horatio Hornblower 41 | description: Capt. Horatio Hornblower, R.N 42 | givenname: Horatio 43 | sn: Hornblower 44 | uid: hhornblo 45 | mail: hhornblo@royalnavy.mod.uk 46 | userpassword: {SHA}nU4eI71bcnBGqeO0t9tXvY1u5oQ= 47 | 48 | dn: cn=William Bush,ou=people,o=sevenSeas 49 | objectclass: person 50 | objectclass: organizationalPerson 51 | objectclass: inetOrgPerson 52 | objectclass: top 53 | cn: William Bush 54 | description: Lt. William Bush 55 | givenname: William 56 | manager: cn=Horatio Hornblower,ou=people,o=sevenSeas 57 | sn: Bush 58 | uid: wbush 59 | mail: wbush@royalnavy.mod.uk 60 | userpassword: {SHA}nU4eI71bcnBGqeO0t9tXvY1u5oQ= 61 | 62 | dn: cn=Thomas Quist,ou=people,o=sevenSeas 63 | objectclass: person 64 | objectclass: organizationalPerson 65 | objectclass: inetOrgPerson 66 | objectclass: top 67 | cn: Thomas Quist 68 | description: Seaman Quist 69 | givenname: Thomas 70 | manager: cn=Horatio Hornblower,ou=people,o=sevenSeas 71 | sn: Quist 72 | uid: tquist 73 | mail: tquist@royalnavy.mod.uk 74 | userpassword: {SHA}nU4eI71bcnBGqeO0t9tXvY1u5oQ= 75 | 76 | dn: cn=Moultrie Crystal,ou=people,o=sevenSeas 77 | objectclass: person 78 | objectclass: organizationalPerson 79 | objectclass: inetOrgPerson 80 | objectclass: top 81 | cn: Moultrie Crystal 82 | description: Lt. Crystal 83 | givenname: Moultrie 84 | manager: cn=Horatio Hornblower,ou=people,o=sevenSeas 85 | sn: Crystal 86 | uid: mchrysta 87 | mail: mchrysta@royalnavy.mod.uk 88 | userpassword: {SHA}nU4eI71bcnBGqeO0t9tXvY1u5oQ= 89 | 90 | dn: cn=HMS Lydia,ou=crews,ou=groups,o=sevenSeas 91 | objectclass: groupOfUniqueNames 92 | objectclass: top 93 | cn: HMS Lydia 94 | uniquemember: cn=Horatio Hornblower,ou=people,o=sevenSeas 95 | uniquemember: cn=William Bush,ou=people,o=sevenSeas 96 | uniquemember: cn=Thomas Quist,ou=people,o=sevenSeas 97 | uniquemember: cn=Moultrie Crystal,ou=people,o=sevenSeas 98 | 99 | # HMS Victory Crew 100 | # ---------------- 101 | 102 | dn: cn=Horatio Nelson,ou=people,o=sevenSeas 103 | objectclass: person 104 | objectclass: organizationalPerson 105 | objectclass: inetOrgPerson 106 | objectclass: top 107 | cn: Horatio Nelson 108 | description: Lord Horatio Nelson 109 | givenname: Horatio 110 | sn: Nelson 111 | uid: hnelson 112 | mail: hnelson@royalnavy.mod.uk 113 | userpassword: {SHA}nU4eI71bcnBGqeO0t9tXvY1u5oQ= 114 | 115 | dn: cn=Thomas Masterman Hardy,ou=people,o=sevenSeas 116 | objectclass: person 117 | objectclass: organizationalPerson 118 | objectclass: inetOrgPerson 119 | objectclass: top 120 | cn: Thomas Masterman Hardy 121 | description: Sir Thomas Masterman Hardy 122 | givenname: Thomas 123 | manager: cn=Horatio Nelson,ou=people,o=sevenSeas 124 | sn: Hardy 125 | uid: thardy 126 | mail: thardy@royalnavy.mod.uk 127 | userpassword: {SHA}nU4eI71bcnBGqeO0t9tXvY1u5oQ= 128 | 129 | dn: cn=Cornelius Buckley,ou=people,o=sevenSeas 130 | objectclass: person 131 | objectclass: organizationalPerson 132 | objectclass: inetOrgPerson 133 | objectclass: top 134 | cn: Cornelius Buckley 135 | description: LM Cornelius Buckley 136 | givenname: Cornelius 137 | manager: cn=Horatio Nelson,ou=people,o=sevenSeas 138 | sn: Buckley 139 | uid: cbuckley 140 | mail: cbuckley@royalnavy.mod.uk 141 | userpassword: {SHA}nU4eI71bcnBGqeO0t9tXvY1u5oQ= 142 | 143 | dn: cn=HMS Victory,ou=crews,ou=groups,o=sevenSeas 144 | objectclass: groupOfUniqueNames 145 | objectclass: top 146 | cn: HMS Victory 147 | uniquemember: cn=Horatio Nelson,ou=people,o=sevenSeas 148 | uniquemember: cn=Thomas Masterman Hardy,ou=people,o=sevenSeas 149 | uniquemember: cn=Cornelius Buckley,ou=people,o=sevenSeas 150 | 151 | # HMS Bounty Crew 152 | # --------------- 153 | 154 | dn: cn=William Bligh,ou=people,o=sevenSeas 155 | objectclass: person 156 | objectclass: organizationalPerson 157 | objectclass: inetOrgPerson 158 | objectclass: top 159 | cn: William Bligh 160 | description: Captain William Bligh 161 | givenname: William 162 | sn: Bligh 163 | uid: wbligh 164 | mail: wbligh@royalnavy.mod.uk 165 | userpassword: {SHA}nU4eI71bcnBGqeO0t9tXvY1u5oQ= 166 | 167 | dn: cn=Fletcher Christian,ou=people,o=sevenSeas 168 | objectclass: person 169 | objectclass: organizationalPerson 170 | objectclass: inetOrgPerson 171 | objectclass: top 172 | cn: Fletcher Christian 173 | description: Lieutenant Fletcher Christian 174 | givenname: Fletcher 175 | manager: cn=William Bligh,ou=people,o=sevenSeas 176 | sn: Christian 177 | uid: fchristi 178 | mail: fchristi@royalnavy.mod.uk 179 | userpassword: {SHA}nU4eI71bcnBGqeO0t9tXvY1u5oQ= 180 | 181 | dn: cn=John Fryer,ou=people,o=sevenSeas 182 | objectclass: person 183 | objectclass: organizationalPerson 184 | objectclass: inetOrgPerson 185 | objectclass: top 186 | cn: John Fryer 187 | description: Master John Fryer 188 | givenname: John 189 | manager: cn=William Bligh,ou=people,o=sevenSeas 190 | sn: Fryer 191 | uid: jfryer 192 | mail: jfryer@royalnavy.mod.uk 193 | userpassword: {SHA}nU4eI71bcnBGqeO0t9tXvY1u5oQ= 194 | 195 | dn: cn=John Hallett,ou=people,o=sevenSeas 196 | objectclass: person 197 | objectclass: organizationalPerson 198 | objectclass: inetOrgPerson 199 | objectclass: top 200 | cn: John Hallett 201 | description: Midshipman John Hallett 202 | givenname: John 203 | manager: cn=William Bligh,ou=people,o=sevenSeas 204 | sn: Hallett 205 | uid: jhallett 206 | mail: jhallett@royalnavy.mod.uk 207 | userpassword: {SHA}nU4eI71bcnBGqeO0t9tXvY1u5oQ= 208 | 209 | dn: cn=HMS Bounty,ou=crews,ou=groups,o=sevenSeas 210 | objectclass: groupOfUniqueNames 211 | objectclass: top 212 | cn: HMS Bounty 213 | uniquemember: cn=William Bligh,ou=people,o=sevenSeas 214 | uniquemember: cn=Fletcher Christian,ou=people,o=sevenSeas 215 | uniquemember: cn=John Fryer,ou=people,o=sevenSeas 216 | uniquemember: cn=John Hallett,ou=people,o=sevenSeas 217 | 218 | 219 | 220 | -------------------------------------------------------------------------------- /step-01/images/ldap-00-create-server.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Adobe-Marketing-Cloud/aem-ldap-tutorial/c19b8f0cb1f828eeb311fa12e22e3c9323457ca2/step-01/images/ldap-00-create-server.png -------------------------------------------------------------------------------- /step-01/images/ldap-01-configure-server.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Adobe-Marketing-Cloud/aem-ldap-tutorial/c19b8f0cb1f828eeb311fa12e22e3c9323457ca2/step-01/images/ldap-01-configure-server.png -------------------------------------------------------------------------------- /step-01/images/ldap-02-create-partition.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Adobe-Marketing-Cloud/aem-ldap-tutorial/c19b8f0cb1f828eeb311fa12e22e3c9323457ca2/step-01/images/ldap-02-create-partition.png -------------------------------------------------------------------------------- /step-01/images/ldap-03-start-server.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Adobe-Marketing-Cloud/aem-ldap-tutorial/c19b8f0cb1f828eeb311fa12e22e3c9323457ca2/step-01/images/ldap-03-start-server.png -------------------------------------------------------------------------------- /step-01/images/ldap-04-create-connection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Adobe-Marketing-Cloud/aem-ldap-tutorial/c19b8f0cb1f828eeb311fa12e22e3c9323457ca2/step-01/images/ldap-04-create-connection.png -------------------------------------------------------------------------------- /step-01/images/ldap-05-import-ldiff.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Adobe-Marketing-Cloud/aem-ldap-tutorial/c19b8f0cb1f828eeb311fa12e22e3c9323457ca2/step-01/images/ldap-05-import-ldiff.png -------------------------------------------------------------------------------- /step-01/images/ldap-06-select-ldiff.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Adobe-Marketing-Cloud/aem-ldap-tutorial/c19b8f0cb1f828eeb311fa12e22e3c9323457ca2/step-01/images/ldap-06-select-ldiff.png -------------------------------------------------------------------------------- /step-01/images/ldap-07-browse-structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Adobe-Marketing-Cloud/aem-ldap-tutorial/c19b8f0cb1f828eeb311fa12e22e3c9323457ca2/step-01/images/ldap-07-browse-structure.png -------------------------------------------------------------------------------- /step-01/tutorial-01-install-ldap.md: -------------------------------------------------------------------------------- 1 | AEM 6.1 LDAP Authentication Tutorial 2 | ==================================== 3 | 4 | Step 01 - Install Apache Directory Server 5 | ----------------------------------------- 6 | 7 | In this step we install _Apache Directory Server_ and _Apache Directory Studio_, create and configure a new LDAP server and load it with example data. 8 | 9 | ### Requirements 10 | 1. Apache Directory Server 11 | 2. Apache Directory Studio 12 | 2. Example data: http://directory.apache.org/apacheds/basic-ug/resources/apache-ds-tutorial.ldif 13 | 14 | #### 1. install apache directory server 15 | Folow: http://directory.apache.org/apacheds/basic-ug/1.3-installing-and-starting.html 16 | 17 | #### 2. install apache directory studio 18 | See: http://directory.apache.org/studio/ 19 | 20 | #### 3. create a new server 21 | - In directory studio, select the server tab and click the icon for create server. 22 | - choose some meaningful name and click finish 23 | ![create ldap server](images/ldap-00-create-server.png) 24 | 25 | #### 4. configure server 26 | Doubleclick the newly created server to open its configuration. 27 | 28 | - disable anonymous access 29 | - enable access control 30 | 31 | ![configure server](images/ldap-01-configure-server.png) 32 | 33 | - open _Advanced Partions Configuration_ 34 | - delete the _example_ partition 35 | - create new partition: 36 | - for id enter: `SevenSeas` 37 | - for suffix enter: `o=SevenSeas` 38 | 39 | ![create partition](images/ldap-02-create-partition.png) 40 | 41 | **Save the configuration !!** 42 | 43 | #### 5. start the server 44 | Click on the "Start" in the servers tab 45 | 46 | ![start server](images/ldap-03-start-server.png) 47 | 48 | #### 6. create connection 49 | Right click the server and select _Create a Connection_ 50 | 51 | ![create connection](images/ldap-04-create-connection.png) 52 | 53 | #### 7. connect to server 54 | Double click on the newly created connection in order to connect to the server. 55 | 56 | #### 8. import ldif 57 | Import the example data: 58 | 59 | - right click on the _o=SevenSeas_ node 60 | - select: _Import_ -> _LDIF Import..._ 61 | 62 | ![import ldif](images/ldap-05-import-ldiff.png) 63 | 64 | - choose the `apache-ds-tutorial.ldif` file and click _Finish_ 65 | 66 | ![ldif import dialog](images/ldap-06-select-ldiff.png) 67 | 68 | #### 9. browse structure 69 | You can verify the newly imported entries by browsing the structure below the _o=SevenSeas_ node. 70 | 71 | ![browse seven seas](images/ldap-07-browse-structure.png) 72 | 73 | -------------------------------------------------------------------------------- /step-02/images/aem-00-setup-logging.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Adobe-Marketing-Cloud/aem-ldap-tutorial/c19b8f0cb1f828eeb311fa12e22e3c9323457ca2/step-02/images/aem-00-setup-logging.png -------------------------------------------------------------------------------- /step-02/images/aem-01-find-ldap-idp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Adobe-Marketing-Cloud/aem-ldap-tutorial/c19b8f0cb1f828eeb311fa12e22e3c9323457ca2/step-02/images/aem-01-find-ldap-idp.png -------------------------------------------------------------------------------- /step-02/images/aem-02-configure-ldap-idp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Adobe-Marketing-Cloud/aem-ldap-tutorial/c19b8f0cb1f828eeb311fa12e22e3c9323457ca2/step-02/images/aem-02-configure-ldap-idp.png -------------------------------------------------------------------------------- /step-02/images/aem-03-find-synchandler.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Adobe-Marketing-Cloud/aem-ldap-tutorial/c19b8f0cb1f828eeb311fa12e22e3c9323457ca2/step-02/images/aem-03-find-synchandler.png -------------------------------------------------------------------------------- /step-02/images/aem-04-configure-synchandler.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Adobe-Marketing-Cloud/aem-ldap-tutorial/c19b8f0cb1f828eeb311fa12e22e3c9323457ca2/step-02/images/aem-04-configure-synchandler.png -------------------------------------------------------------------------------- /step-02/images/aem-05-find-loginmodule.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Adobe-Marketing-Cloud/aem-ldap-tutorial/c19b8f0cb1f828eeb311fa12e22e3c9323457ca2/step-02/images/aem-05-find-loginmodule.png -------------------------------------------------------------------------------- /step-02/images/aem-06-configure-loginmodule.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Adobe-Marketing-Cloud/aem-ldap-tutorial/c19b8f0cb1f828eeb311fa12e22e3c9323457ca2/step-02/images/aem-06-configure-loginmodule.png -------------------------------------------------------------------------------- /step-02/images/aem-07-jaas-console.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Adobe-Marketing-Cloud/aem-ldap-tutorial/c19b8f0cb1f828eeb311fa12e22e3c9323457ca2/step-02/images/aem-07-jaas-console.png -------------------------------------------------------------------------------- /step-02/ldap-first-config-pkg.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Adobe-Marketing-Cloud/aem-ldap-tutorial/c19b8f0cb1f828eeb311fa12e22e3c9323457ca2/step-02/ldap-first-config-pkg.zip -------------------------------------------------------------------------------- /step-02/tutorial-02-configure-aem.md: -------------------------------------------------------------------------------- 1 | AEM 6.1 LDAP Authentication Tutorial 2 | ==================================== 3 | 4 | Step 02 - Configure LDAP Authentication in AEM 5 | ---------------------------------------------- 6 | 7 | In this step we configure AEM 6.1 to use our newly created LDAP server for login. 8 | 9 | **!** If you want to fast forward, here's the [ldap-first-config-pkg.zip](ldap-first-config-pkg.zip) package with the resulting configuration of this step. 10 | 11 | ### Requirements 12 | 1. LDAP Server with example data (preferably from previous step 01) 13 | 2. Running vanilla AEM 6.1 installation 14 | 15 | #### 1. configure logging 16 | It is always advisable to configure LDAP related logging during initial setup. 17 | 18 | - open [Sling Log Support](http://localhost:4502/system/console/slinglog) in the Felix console 19 | - add `org.apache.jackrabbit.oak.spi.security.authentication.external` as category for controlling the logging of the external login module and sync handler 20 | - add `org.apache.jackrabbit.oak.security.authentication.ldap` as category for controlling the logging of the LDAP identity provider. 21 | - optionally, add `org.apache.directory` as a category for very lowlevel logging of the ldap client calls. 22 | 23 | ![setup logging](images/aem-00-setup-logging.png) 24 | 25 | #### 2. configure LDAP identity provider 26 | Now we configure the LDAP idp. this is the piece that connects to the LDAP server. 27 | 28 | - open the [Felix Configuration Manager](http://localhost:4502/system/console/configMgr) and search for the _"ldap identity provider"_ factory config and click on the plus **+** button. 29 | 30 | ![create idp config](images/aem-01-find-ldap-idp.png) 31 | 32 | Enter the following information: 33 | 34 | | Name | Value | 35 | |------------------------------|----------------- 36 | | LDAP Provider Name | `ldap` 37 | | LDAP Server Hostname | `localhost` 38 | | LDAP Server Port | 10389 39 | | Use SSL | _false_ 40 | | Use TLS | _false_ 41 | | Disable certificate checking | _false_ 42 | | Bind DN | `uid=admin,ou=system` 43 | | Bind Password | `secret` 44 | | Search Timeout | `60s` 45 | | Admin pool max active | 8 46 | | User pool max active | 8 47 | | User base DN | `ou=people,o=SevenSeas` 48 | | User object classes | `person` 49 | | User id attribute | `uid` 50 | | User extra filter | 51 | | User DN paths | _false_ 52 | | Group base DN | `ou=groups,o=SevenSeas` 53 | | Group object classes | `groupOfUniqueNames` 54 | | Group name attribute | `cn` 55 | | Group extra filter | 56 | | Group DN paths | _false_ 57 | | Group member attribute | `uniquemember` 58 | 59 | And save the config. 60 | 61 | ![configure ldap idp](images/aem-02-configure-ldap-idp.png) 62 | 63 | #### 3. configure Default Sync Handler 64 | The sync handler is responsible to synchronize the external users with the local repository. 65 | 66 | - open the [Felix Configuration Manager](http://localhost:4502/system/console/configMgr) and search for the _"Default Sync Handler"_ factory config and click on the plus **+** button. 67 | 68 | ![create sync config](images/aem-03-find-synchandler.png) 69 | 70 | Enter the following information: 71 | 72 | | Name | Value 73 | |-------------------------------|-------------------- 74 | | Sync Handler Name | `default` 75 | | User Expiration Time | `1h` 76 | | User auto membership | `contributor` 77 | | User property mapping | `rep:fullname=cn` 78 | | User Path Prefix | `/ldap_seven_seas` 79 | | User Membership Expiration | `1h` 80 | | User membership nesting depth | `1` 81 | | Group Expiration Time | `1d` 82 | | Group auto membership | 83 | | Group property mapping | 84 | | Group Path Prefix | `/ldap_seven_seas` 85 | 86 | And save the config 87 | 88 | ![sync config](images/aem-04-configure-synchandler.png) 89 | 90 | #### 4. configure the external login module 91 | The external login module is the bridge between the login, the idp and the sync handler. 92 | 93 | - open the [Felix Configuration Manager](http://localhost:4502/system/console/configMgr) and search for the _"External Login Module"_ factory config and click on the plus **+** button. 94 | 95 | ![create login module](images/aem-05-find-loginmodule.png) 96 | 97 | Enter the following information: 98 | 99 | | Name | Value 100 | |------------------------|---------- 101 | | JAAS Ranking | 50 102 | | JAAS Control Flag | `SUFFICIENT` 103 | | JAAS Realm | 104 | | Identity Provider Name | `ldap` 105 | | Sync Handler Name | `default` 106 | 107 | And save the config 108 | 109 | ![configure loginmodule](images/aem-06-configure-loginmodule.png) 110 | 111 | #### 5. verify the JAAS console 112 | The external login module should now show up in the JAAS console: 113 | 114 | - open the [Felix JAAS Console](http://localhost:4502/system/console/jaas) 115 | 116 | ![jaas console](images/aem-07-jaas-console.png) 117 | 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /step-03/images/test-01-useradmin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Adobe-Marketing-Cloud/aem-ldap-tutorial/c19b8f0cb1f828eeb311fa12e22e3c9323457ca2/step-03/images/test-01-useradmin.png -------------------------------------------------------------------------------- /step-03/images/test-02-newconfig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Adobe-Marketing-Cloud/aem-ldap-tutorial/c19b8f0cb1f828eeb311fa12e22e3c9323457ca2/step-03/images/test-02-newconfig.png -------------------------------------------------------------------------------- /step-03/images/test-03-useradmin-withprops.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Adobe-Marketing-Cloud/aem-ldap-tutorial/c19b8f0cb1f828eeb311fa12e22e3c9323457ca2/step-03/images/test-03-useradmin-withprops.png -------------------------------------------------------------------------------- /step-03/images/test-04-jmx-console.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Adobe-Marketing-Cloud/aem-ldap-tutorial/c19b8f0cb1f828eeb311fa12e22e3c9323457ca2/step-03/images/test-04-jmx-console.png -------------------------------------------------------------------------------- /step-03/images/test-05-jmx-synchandler.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Adobe-Marketing-Cloud/aem-ldap-tutorial/c19b8f0cb1f828eeb311fa12e22e3c9323457ca2/step-03/images/test-05-jmx-synchandler.png -------------------------------------------------------------------------------- /step-03/images/test-06-jmx-syncexternalusers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Adobe-Marketing-Cloud/aem-ldap-tutorial/c19b8f0cb1f828eeb311fa12e22e3c9323457ca2/step-03/images/test-06-jmx-syncexternalusers.png -------------------------------------------------------------------------------- /step-03/images/test-07-useradmin-more.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Adobe-Marketing-Cloud/aem-ldap-tutorial/c19b8f0cb1f828eeb311fa12e22e3c9323457ca2/step-03/images/test-07-useradmin-more.png -------------------------------------------------------------------------------- /step-03/ldap-config-with-profile-pkg.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Adobe-Marketing-Cloud/aem-ldap-tutorial/c19b8f0cb1f828eeb311fa12e22e3c9323457ca2/step-03/ldap-config-with-profile-pkg.zip -------------------------------------------------------------------------------- /step-03/log-snip-01.md: -------------------------------------------------------------------------------- 1 | Log snippet during initial successfull login 2 | -------------------------------------------- 3 | 4 | ```` 5 | 22.07.2015 16:41:08.186 *DEBUG* [qtp1188867090-847] org.apache.jackrabbit.oak.security.authentication.ldap.impl.LdapIdentityProvider search below ou=people,o=SevenSeas with (&(uid=wbush)(objectclass=person)) found cn=William Bush,ou=people,o=SevenSeas 6 | 22.07.2015 16:41:08.186 *DEBUG* [qtp1188867090-847] org.apache.jackrabbit.oak.security.authentication.ldap.impl.LdapIdentityProvider getUser(wbush) (connect=1.92ms, lookup=3.03ms) 7 | 22.07.2015 16:41:08.186 *DEBUG* [qtp1188867090-847] org.apache.jackrabbit.oak.security.authentication.ldap.impl.PoolableUnboundConnectionFactory activate connection: org.apache.directory.ldap.client.api.LdapNetworkConnection@3aa49064 8 | 22.07.2015 16:41:08.188 *DEBUG* [qtp1188867090-847] org.apache.jackrabbit.oak.security.authentication.ldap.impl.PoolableUnboundConnectionFactory validating connection org.apache.directory.ldap.client.api.LdapNetworkConnection@3aa49064: true 9 | 22.07.2015 16:41:08.196 *DEBUG* [qtp1188867090-847] org.apache.jackrabbit.oak.security.authentication.ldap.impl.LdapIdentityProvider authenticate(wbush) (connect=1.61ms, bind=8.30ms) 10 | 22.07.2015 16:41:08.196 *DEBUG* [qtp1188867090-847] org.apache.jackrabbit.oak.security.authentication.ldap.impl.PoolableUnboundConnectionFactory passivate connection: org.apache.directory.ldap.client.api.LdapNetworkConnection@3aa49064 11 | 22.07.2015 16:41:08.196 *DEBUG* [qtp1188867090-847] org.apache.jackrabbit.oak.spi.security.authentication.external.impl.ExternalLoginModule IDP ldap returned valid user LdapIdentity{ref=ExternalIdentityRef{id='cn=William Bush,ou=people,o=SevenSeas', providerName='ldap'}, id='wbush'} 12 | 22.07.2015 16:41:08.202 *DEBUG* [qtp1188867090-847] org.apache.jackrabbit.oak.spi.security.authentication.external.impl.DefaultSyncHandler Properties of user 'wbush' need sync. rep:lastSynced not set. 13 | 22.07.2015 16:41:08.203 *DEBUG* [qtp1188867090-847] org.apache.jackrabbit.oak.spi.security.authentication.external.impl.DefaultSyncHandler Membership of user 'wbush' need sync. rep:lastSynced not set. 14 | 22.07.2015 16:41:08.203 *DEBUG* [qtp1188867090-847] org.apache.jackrabbit.oak.spi.security.authentication.external.impl.DefaultSyncHandler Syncing membership 'cn=William Bush,ou=people,o=SevenSeas;ldap' -> 'wbush' 15 | 22.07.2015 16:41:08.210 *DEBUG* [qtp1188867090-847] org.apache.jackrabbit.oak.security.authentication.ldap.impl.LdapIdentityProvider search below ou=groups,o=SevenSeas with (&(uniquemember=cn=William Bush,ou=people,o=SevenSeas)(objectclass=groupOfUniqueNames)) found 1 entries. (connect=2.34ms, search=226.00us, iterate=3.28ms) 16 | 22.07.2015 16:41:08.211 *DEBUG* [qtp1188867090-847] org.apache.jackrabbit.oak.spi.security.authentication.external.impl.DefaultSyncHandler - processing membership cn=HMS Lydia,ou=crews,ou=groups,o=SevenSeas 17 | 22.07.2015 16:41:08.215 *DEBUG* [qtp1188867090-847] org.apache.jackrabbit.oak.spi.security.authentication.external.impl.DefaultSyncHandler - idp returned 'HMS Lydia' 18 | 22.07.2015 16:41:08.216 *DEBUG* [qtp1188867090-847] org.apache.jackrabbit.oak.spi.security.authentication.external.impl.DefaultSyncHandler - user manager returned 'null' 19 | 22.07.2015 16:41:08.217 *DEBUG* [qtp1188867090-847] org.apache.jackrabbit.oak.spi.security.authentication.external.impl.DefaultSyncHandler - created new group 20 | 22.07.2015 16:41:08.217 *DEBUG* [qtp1188867090-847] org.apache.jackrabbit.oak.spi.security.authentication.external.impl.DefaultSyncHandler Properties of group 'HMS Lydia' need sync. rep:lastSynced not set. 21 | 22.07.2015 16:41:08.218 *DEBUG* [qtp1188867090-847] org.apache.jackrabbit.oak.spi.security.authentication.external.impl.DefaultSyncHandler - added 'User 'wbush'' as member to 'Group 'HMS Lydia'' 22 | 22.07.2015 16:41:08.218 *DEBUG* [qtp1188867090-847] org.apache.jackrabbit.oak.spi.security.authentication.external.impl.DefaultSyncHandler - group nesting level for 'HMS Lydia' reached 23 | 22.07.2015 16:41:08.219 *DEBUG* [qtp1188867090-847] org.apache.jackrabbit.oak.spi.security.authentication.external.impl.DefaultSyncHandler syncMembership(wbush) (fetching=6.26ms, reading=1.27ms, adding=7.41ms, removing=25.00us) 24 | 22.07.2015 16:41:08.219 *DEBUG* [qtp1188867090-847] org.apache.jackrabbit.oak.spi.security.authentication.external.impl.DefaultSyncHandler sync(cn=William Bush,ou=people,o=SevenSeas;ldap) -> wbush (find=848.00us, create=2.03ms, sync=16.78ms) 25 | 22.07.2015 16:41:08.223 *DEBUG* [qtp1188867090-847] org.apache.jackrabbit.oak.spi.security.authentication.external.impl.ExternalLoginModule syncUser(wbush) (sync=21.99ms, commit=4.05ms) 26 | ```` 27 | 28 | Log snippet when user (foo) does not exist: 29 | ------------------------------------- 30 | ```` 31 | 22.07.2015 17:00:30.029 *DEBUG* [qtp1188867090-843] org.apache.jackrabbit.oak.security.authentication.ldap.impl.LdapIdentityProvider search below ou=people,o=SevenSeas with (&(uid=foo)(objectclass=person)) found 0 entries. 32 | 22.07.2015 17:00:30.029 *DEBUG* [qtp1188867090-843] org.apache.jackrabbit.oak.security.authentication.ldap.impl.LdapIdentityProvider getUser(foo) (connect=1.88ms, lookup=3.31ms) 33 | 22.07.2015 17:00:30.029 *DEBUG* [qtp1188867090-843] org.apache.jackrabbit.oak.spi.security.authentication.external.impl.ExternalLoginModule IDP ldap returned null for simple creds of foo 34 | ```` 35 | 36 | Log snippet for invalid password: 37 | --------------------------------- 38 | ```` 39 | 22.07.2015 17:02:04.886 *DEBUG* [qtp1188867090-843] org.apache.jackrabbit.oak.security.authentication.ldap.impl.LdapIdentityProvider search below ou=people,o=SevenSeas with (&(uid=wbush)(objectclass=person)) found cn=William Bush,ou=people,o=SevenSeas 40 | 22.07.2015 17:02:04.886 *DEBUG* [qtp1188867090-843] org.apache.jackrabbit.oak.security.authentication.ldap.impl.LdapIdentityProvider getUser(wbush) (connect=1.79ms, lookup=2.97ms) 41 | 22.07.2015 17:02:04.886 *DEBUG* [qtp1188867090-843] org.apache.jackrabbit.oak.security.authentication.ldap.impl.PoolableUnboundConnectionFactory activate connection: org.apache.directory.ldap.client.api.LdapNetworkConnection@3aa49064 42 | 22.07.2015 17:02:04.887 *DEBUG* [qtp1188867090-843] org.apache.jackrabbit.oak.security.authentication.ldap.impl.PoolableUnboundConnectionFactory validating connection org.apache.directory.ldap.client.api.LdapNetworkConnection@3aa49064: true 43 | 22.07.2015 17:02:04.894 *DEBUG* [qtp1188867090-843] org.apache.jackrabbit.oak.security.authentication.ldap.impl.PoolableUnboundConnectionFactory passivate connection: org.apache.directory.ldap.client.api.LdapNetworkConnection@3aa49064 44 | 22.07.2015 17:02:04.894 *DEBUG* [qtp1188867090-843] org.apache.jackrabbit.oak.spi.security.authentication.external.impl.ExternalLoginModule IDP ldap throws login exception for 'wbush': Unable to authenticate against LDAP server: INVALID_CREDENTIALS: Bind failed: ERR_229 Cannot authenticate user cn=William Bush,ou=people,o=SevenSeas: 45 | org.apache.directory.api.ldap.model.exception.LdapAuthenticationException: ERR_229 Cannot authenticate user cn=William Bush,ou=people,o=SevenSeas 46 | at org.apache.directory.server.core.authn.AuthenticationInterceptor.bind(AuthenticationInterceptor.java:671) 47 | at org.apache.directory.server.core.DefaultOperationManager.bind(DefaultOperationManager.java:439) 48 | at org.apache.directory.server.ldap.handlers.request.BindRequestHandler.handleSimpleAuth(BindRequestHandler.java:184) 49 | at org.apache.directory.server.ldap.handlers.request.BindRequestHandler.handle(BindRequestHandler.java:636) 50 | at org.apache.directory.server.ldap.handlers.request.BindRequestHandler.handle(BindRequestHandler.java:66) 51 | at org.apache.directory.server.ldap.handlers.LdapRequestHandler.handleMessage(LdapRequestHandler.java:193) 52 | at org.apache.directory.server.ldap.handlers.LdapRequestHandler.handleMessage(LdapRequestHandler.java:56) 53 | at org.apache.mina.handler.demux.DemuxingIoHandler.messageReceived(DemuxingIoHandler.java:221) 54 | at org.apache.directory.server.ldap.LdapProtocolHandler.messageReceived(LdapProtocolHandler.java:217) 55 | at org.apache.mina.core.filterchain.DefaultIoFilterChain$TailFilter.messageReceived(DefaultIoFilterChain.java:854) 56 | at org.apache.mina.core.filterchain.DefaultIoFilterChain.callNextMessageReceived(DefaultIoFilterChain.java:542) 57 | at org.apache.mina.core.filterchain.DefaultIoFilterChain.access$1300(DefaultIoFilterChain.java:48) 58 | at org.apache.mina.core.filterchain.DefaultIoFilterChain$EntryImpl$1.messageReceived(DefaultIoFilterChain.java:943) 59 | at org.apache.mina.core.filterchain.IoFilterEvent.fire(IoFilterEvent.java:74) 60 | at org.apache.mina.core.session.IoEvent.run(IoEvent.java:63) 61 | at org.apache.mina.filter.executor.UnorderedThreadPoolExecutor$Worker.runTask(UnorderedThreadPoolExecutor.java:475) 62 | at org.apache.mina.filter.executor.UnorderedThreadPoolExecutor$Worker.run(UnorderedThreadPoolExecutor.java:429) 63 | at java.lang.Thread.run(Thread.java:744) 64 | 65 | 66 | BindRequest = 67 | MessageType : BIND_REQUEST 68 | Message ID : 16 69 | BindRequest 70 | Version : '3' 71 | Name : 'cn=William Bush,ou=people,o=SevenSeas' 72 | Simple authentication : '(omitted-for-safety)' 73 | 74 | 22.07.2015 17:02:04.894 *INFO* [qtp1188867090-843] org.apache.sling.auth.core.impl.SlingAuthenticator handleLoginFailure: Unable to authenticate null: UserId/Password mismatch. 75 | ```` 76 | 77 | Log snippet when user is resynced 78 | --------------------------------- 79 | ```` 80 | 22.07.2015 17:27:26.663 *DEBUG* [qtp1188867090-1077] org.apache.jackrabbit.oak.security.authentication.ldap.impl.LdapIdentityProvider search below ou=people,o=SevenSeas with (&(uid=wbush)(objectclass=person)) found cn=William Bush,ou=people,o=SevenSeas 81 | 22.07.2015 17:27:26.664 *DEBUG* [qtp1188867090-1077] org.apache.jackrabbit.oak.security.authentication.ldap.impl.LdapIdentityProvider getUser(wbush) (connect=1.79ms, lookup=2.87ms) 82 | 22.07.2015 17:27:26.664 *DEBUG* [qtp1188867090-1077] org.apache.jackrabbit.oak.security.authentication.ldap.impl.PoolableUnboundConnectionFactory activate connection: org.apache.directory.ldap.client.api.LdapNetworkConnection@3aa49064 83 | 22.07.2015 17:27:26.665 *DEBUG* [qtp1188867090-1077] org.apache.jackrabbit.oak.security.authentication.ldap.impl.PoolableUnboundConnectionFactory validating connection org.apache.directory.ldap.client.api.LdapNetworkConnection@3aa49064: true 84 | 22.07.2015 17:27:26.668 *DEBUG* [qtp1188867090-1077] org.apache.jackrabbit.oak.security.authentication.ldap.impl.LdapIdentityProvider authenticate(wbush) (connect=1.46ms, bind=2.83ms) 85 | 22.07.2015 17:27:26.668 *DEBUG* [qtp1188867090-1077] org.apache.jackrabbit.oak.security.authentication.ldap.impl.PoolableUnboundConnectionFactory passivate connection: org.apache.directory.ldap.client.api.LdapNetworkConnection@3aa49064 86 | 22.07.2015 17:27:26.668 *DEBUG* [qtp1188867090-1077] org.apache.jackrabbit.oak.spi.security.authentication.external.impl.ExternalLoginModule IDP ldap returned valid user LdapIdentity{ref=ExternalIdentityRef{id='cn=William Bush,ou=people,o=SevenSeas', providerName='ldap'}, id='wbush'} 87 | 22.07.2015 17:27:26.669 *DEBUG* [qtp1188867090-1077] org.apache.jackrabbit.oak.spi.security.authentication.external.impl.DefaultSyncHandler Properties of user 'wbush' need sync. rep:lastSynced expired (2113690 > 10000) 88 | 22.07.2015 17:27:26.670 *DEBUG* [qtp1188867090-1077] org.apache.jackrabbit.oak.spi.security.authentication.external.impl.DefaultSyncHandler Membership of user 'wbush' do not need sync. 89 | 22.07.2015 17:27:26.670 *DEBUG* [qtp1188867090-1077] org.apache.jackrabbit.oak.spi.security.authentication.external.impl.DefaultSyncHandler sync(cn=William Bush,ou=people,o=SevenSeas;ldap) -> wbush (find=436.00us, sync=1.24ms) 90 | 22.07.2015 17:27:26.671 *DEBUG* [qtp1188867090-1077] org.apache.jackrabbit.oak.spi.security.authentication.external.impl.ExternalLoginModule syncUser(wbush) (sync=1.79ms, commit=826.00us) 91 | ```` 92 | 93 | -------------------------------------------------------------------------------- /step-03/tutorial-03-test.md: -------------------------------------------------------------------------------- 1 | AEM 6.1 LDAP Authentication Tutorial 2 | ==================================== 3 | 4 | Step 03 - Test that it works! 5 | ----------------------------- 6 | 7 | In this step we verify that the LDAP authentication works and if the users are synced properly. 8 | 9 | **!** If you want to fast forward, here's the [ldap-config-with-profile-pkg.zip](ldap-config-with-profile-pkg.zip) package with the resulting configuration of this step. 10 | 11 | 12 | ### Requirements 13 | 1. LDAP Server with example data (from step 01) 14 | 2. AEM 6.1 installation with configured LDAP authentication (from step 02) 15 | 16 | #### 1. simple test - login as a ldap user 17 | The first test is to login as a user that does not exist in the repository but only on the ldap. 18 | 19 | - ensure to logout previous session or clear all browser cookie or use a different browser, hostname or IP 20 | - open browser to aem: http://127.0.0.1:4502/ 21 | - login as `wbush` with password `pass` 22 | 23 | if the login succeeds, you should now see the authoring environment because we configured the `user.autoMembership` to include the `contributor` group. 24 | 25 | Looking at the users and groups should show the user _William Bush_ and his group: 26 | 27 | - open the [AEM useradmin](http://localhost:4502/useradmin) 28 | - search for `seven` 29 | 30 | ![useradmin](images/test-01-useradmin.png) 31 | 32 | If you look at the log files, you should see something like this here: [log-snip-01.md](log-snip-01.md) 33 | 34 | #### 2. add more config for first- and givenname 35 | As you can see in the AEM user admin, the fields for _First Name_ and _Last Name_ are empty, because the AEM useradmin uses the `profile/givenName` and `profile/familyName` properties. 36 | 37 | So let's alter the config so that this information is populated as well. 38 | 39 | - open the [Felix Configuration Manager](http://localhost:4502/system/console/configMgr) and search for the _"Default Sync Handler"_ factory config and click on the first confg to edit it 40 | 41 | - change the value for _User property mapping_ and add the mappings. 42 | - also lower the value for _User expiration time_ so we can see the effects sooner 43 | 44 | 45 | Enter the following information: 46 | 47 | | Name | Value 48 | |-------------------------------|-------------------- 49 | | User Expiration Time | `10s` 50 | | User property mapping | `rep:fullname=cn`
`profile/nt:primaryType="nt:unstructured"`
`profile/givenName=givenname`
`profile/familyName=sn` | 51 | 52 | ![new config](images/test-02-newconfig.png) 53 | 54 | And save the config. 55 | 56 | Now, if you logout and login again with `wbush` the user should be resynced. 57 | 58 | ![useradmin](images/test-03-useradmin-withprops.png) 59 | 60 | #### 3. use JMX console to sync more users 61 | Oak comes with a default JMX mbean that allows you to control the sync handler. 62 | 63 | - open the [Felix JMX Console](http://localhost:4502/system/console/jmx) and search for the _"External Identity"_ bean and select it 64 | 65 | ![jmx console](images/test-04-jmx-console.png) 66 | 67 | ![jmx synchandler](images/test-05-jmx-synchandler.png) 68 | 69 | - click on `syncAllExternalUsers()` and then on `invoke`. this will collect all the users from the IDP and sync them with the repository. you will see an `add` _op_ property for all newly added users, and an `upd` for the updated ones. 70 | 71 | ![sync users](images/test-06-jmx-syncexternalusers.png) 72 | 73 | checking back the user admin shows the newly imported users and groups 74 | 75 | ![useradmin](images/test-07-useradmin-more.png) 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /step-04/authorizables.json: -------------------------------------------------------------------------------- 1 | { 2 | "enterprise": { 3 | "id": "enterprise", 4 | "members": ["kirk", "spock", "mccoy"] 5 | }, 6 | "kirk": { 7 | "id": "kirk", 8 | "fullname": "James T. Kirk", 9 | "givenname": "James T.", 10 | "familyname": "Kirk", 11 | "email": "kirk@seven_skies.example.com", 12 | "password": "pass", 13 | "groups": ["enterprise"] 14 | }, 15 | "spock": { 16 | "id": "spock", 17 | "fullname": "Spock", 18 | "givenname": "Spock", 19 | "familyname": "", 20 | "email": "spock@seven_skies.example.com", 21 | "password": "pass", 22 | "groups": ["enterprise"] 23 | }, 24 | "mccoy": { 25 | "id": "mcccoy", 26 | "fullname": "Leonard McCoy", 27 | "givenname": "Leonard", 28 | "familyname": "McCoy", 29 | "email": "mccoy@seven_skies.example.com", 30 | "password": "pass", 31 | "groups": ["enterprise"] 32 | }, 33 | 34 | "voyager": { 35 | "id": "voyager", 36 | "members": ["janeway", "chakotay", "tuvok"] 37 | }, 38 | "janeway": { 39 | "id": "janeway", 40 | "fullname": "Kathryn Janeway", 41 | "givenname": "Kathryn", 42 | "familyname": "Janeway", 43 | "email": "janeway@seven_skies.example.com", 44 | "password": "pass", 45 | "groups": ["voyager"] 46 | }, 47 | "chakotay": { 48 | "id": "chakotay", 49 | "fullname": "Chakotay", 50 | "givenname": "Chakotay", 51 | "familyname": "", 52 | "email": "chakotay@seven_skies.example.com", 53 | "password": "pass", 54 | "groups": ["voyager"] 55 | }, 56 | "tuvok": { 57 | "id": "tuvok", 58 | "fullname": "Tuvok", 59 | "givenname": "Tuvok", 60 | "familyname": "", 61 | "email": "tuvok@seven_skies.example.com", 62 | "password": "pass", 63 | "groups": ["voyager"] 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /step-04/example-idp/.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | target 3 | -------------------------------------------------------------------------------- /step-04/example-idp/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 18 | 20 | 4.0.0 21 | 22 | 23 | 24 | 25 | org.apache.jackrabbit 26 | oak-parent 27 | 1.2.2 28 | 29 | 30 | 31 | 32 | 33 | 34 | com.adobe.gems 35 | com.adobe.gems.exampleidp 36 | 1.0-SNAPSHOT 37 | AEM 6.1 Gems Example IDP 38 | Example IDP that reads users and groups from a json file 39 | bundle 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | org.apache.felix 48 | maven-bundle-plugin 49 | 50 | 51 | org.apache.felix 52 | maven-scr-plugin 53 | 1.20.0 54 | 55 | 56 | org.apache.sling 57 | maven-sling-plugin 58 | 59 | http://localhost:4502/system/console 60 | false 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | org.apache.jackrabbit 72 | oak-auth-external 73 | 1.2.2 74 | provided 75 | 76 | 77 | org.apache.sling 78 | org.apache.sling.commons.json 79 | 2.0.10 80 | provided 81 | 82 | 83 | 84 | org.slf4j 85 | slf4j-api 86 | provided 87 | 88 | 89 | 90 | org.apache.felix 91 | org.apache.felix.scr.annotations 92 | provided 93 | 94 | 95 | com.google.code.findbugs 96 | jsr305 97 | provided 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /step-04/example-idp/src/main/java/com/adobe/gems/exampleidp/impl/ExternalGroupImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | package com.adobe.gems.exampleidp.impl; 20 | 21 | import java.util.HashSet; 22 | import java.util.Map; 23 | import java.util.Set; 24 | 25 | import javax.annotation.Nonnull; 26 | 27 | import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalGroup; 28 | import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalIdentityException; 29 | import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalIdentityRef; 30 | import org.apache.sling.commons.json.JSONArray; 31 | 32 | /** 33 | * {@code ExternalGroupImpl} implements an external group based on properties 34 | */ 35 | public class ExternalGroupImpl extends ExternalIdentityImpl implements ExternalGroup { 36 | 37 | private Set members; 38 | 39 | public ExternalGroupImpl(String providerName, ExternalIdentityRef ref, String id, Map properties) { 40 | super(providerName, ref, id, properties); 41 | } 42 | 43 | @Nonnull 44 | @Override 45 | public Iterable getDeclaredMembers() throws ExternalIdentityException { 46 | if (members == null) { 47 | members = new HashSet(); 48 | JSONArray gs = (JSONArray) properties.get(JsonFileIdentityProvider.PN_MEMBERS); 49 | if (gs != null) { 50 | for (int i = 0; i groups; 44 | 45 | protected final Map properties; 46 | 47 | protected ExternalIdentityImpl(String providerName, ExternalIdentityRef ref, String id, Map properties) { 48 | this.providerName = providerName; 49 | this.ref = ref; 50 | this.id = id; 51 | this.properties = properties; 52 | } 53 | 54 | @Nonnull 55 | @Override 56 | public ExternalIdentityRef getExternalId() { 57 | return ref; 58 | } 59 | 60 | @Nonnull 61 | @Override 62 | public String getId() { 63 | return id; 64 | } 65 | 66 | @Nonnull 67 | @Override 68 | public String getPrincipalName() { 69 | return ref.getId(); 70 | } 71 | 72 | @Override 73 | public String getIntermediatePath() { 74 | return null; 75 | } 76 | 77 | @Nonnull 78 | @Override 79 | public Iterable getDeclaredGroups() throws ExternalIdentityException { 80 | if (groups == null) { 81 | groups = new HashSet(); 82 | JSONArray gs = (JSONArray) properties.get(JsonFileIdentityProvider.PN_GROUPS); 83 | if (gs != null) { 84 | for (int i = 0; i getProperties() { 98 | return properties; 99 | } 100 | 101 | @Override 102 | public String toString() { 103 | return "ExternalIdentityImpl{" + "ref=" + ref + ", id='" + id + '\'' + '}'; 104 | } 105 | 106 | } -------------------------------------------------------------------------------- /step-04/example-idp/src/main/java/com/adobe/gems/exampleidp/impl/ExternalUserImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | package com.adobe.gems.exampleidp.impl; 20 | 21 | import java.util.Map; 22 | 23 | import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalIdentityRef; 24 | import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalUser; 25 | 26 | /** 27 | * {@code ExternalUserImpl} implements an external user based on properties 28 | */ 29 | public class ExternalUserImpl extends ExternalIdentityImpl implements ExternalUser { 30 | 31 | public ExternalUserImpl(String providerName, ExternalIdentityRef ref, String id, Map properties) { 32 | super(providerName, ref, id, properties); 33 | } 34 | } -------------------------------------------------------------------------------- /step-04/example-idp/src/main/java/com/adobe/gems/exampleidp/impl/JsonFileIdentityProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | package com.adobe.gems.exampleidp.impl; 20 | 21 | import java.io.File; 22 | import java.io.FileNotFoundException; 23 | import java.io.IOException; 24 | import java.util.ArrayList; 25 | import java.util.HashMap; 26 | import java.util.Iterator; 27 | import java.util.List; 28 | import java.util.Map; 29 | 30 | import javax.annotation.CheckForNull; 31 | import javax.annotation.Nonnull; 32 | import javax.annotation.Nullable; 33 | import javax.jcr.Credentials; 34 | import javax.jcr.SimpleCredentials; 35 | import javax.security.auth.login.LoginException; 36 | 37 | import org.apache.commons.io.FileUtils; 38 | import org.apache.felix.scr.annotations.Activate; 39 | import org.apache.felix.scr.annotations.Component; 40 | import org.apache.felix.scr.annotations.ConfigurationPolicy; 41 | import org.apache.felix.scr.annotations.Deactivate; 42 | import org.apache.felix.scr.annotations.Property; 43 | import org.apache.felix.scr.annotations.Service; 44 | import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters; 45 | import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalGroup; 46 | import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalIdentity; 47 | import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalIdentityException; 48 | import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalIdentityProvider; 49 | import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalIdentityRef; 50 | import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalUser; 51 | import org.apache.sling.commons.json.JSONArray; 52 | import org.apache.sling.commons.json.JSONException; 53 | import org.apache.sling.commons.json.JSONObject; 54 | import org.slf4j.Logger; 55 | import org.slf4j.LoggerFactory; 56 | 57 | /** 58 | * {@code JsonFileIdentityProvider} implements an external identity provider that reads users and groups from 59 | * a simple json file. the structure is very simple, using the authorizable id as key. if the object has a 'members' property, 60 | * it is considered a group. all password are just plaintext. 61 | * 62 | * for simpler group membership lookup, we also store the groups in the user objects. 63 | * 64 | * Example: 65 | * 66 | * 67 | * { 68 | * "enterprise": { 69 | * "id": "enterprise", 70 | * "members": ["kirk", "spock"] 71 | * }, 72 | * "kirk": { 73 | * "id": "kirk", 74 | * "fullname": "James T. Kirk", 75 | * "password": "pass", 76 | * "groups": ["enterprise"] 77 | * }, 78 | * "spock": { 79 | * "id": "spock", 80 | * "fullname": "Spock", 81 | * "password": "pass", 82 | * "groups": ["enterprise"] 83 | * } 84 | * } 85 | * 86 | */ 87 | @Component( 88 | label = "JSON File Identity Provider", 89 | configurationFactory = true, 90 | metatype = true, 91 | policy = ConfigurationPolicy.REQUIRE 92 | ) 93 | @Service 94 | public class JsonFileIdentityProvider implements ExternalIdentityProvider { 95 | 96 | /** 97 | * property name for members 98 | */ 99 | public static final String PN_MEMBERS = "members"; 100 | 101 | /** 102 | * property name for groups 103 | */ 104 | public static final String PN_GROUPS = "groups"; 105 | 106 | /** 107 | * property name for password 108 | */ 109 | public static final String PN_PASSWORD = "password"; 110 | 111 | /** 112 | * default logger 113 | */ 114 | private static final Logger log = LoggerFactory.getLogger(JsonFileIdentityProvider.class); 115 | 116 | /** 117 | * @see #getName() 118 | */ 119 | public static final String PARAM_NAME_DEFAULT = "json"; 120 | 121 | /** 122 | * @see #getName() 123 | */ 124 | @Property( 125 | label = "Provider Name", 126 | description = "Name of this provider configuration. This is used to reference this provider by the login modules.", 127 | value = PARAM_NAME_DEFAULT 128 | ) 129 | public static final String PARAM_NAME = "provider.name"; 130 | 131 | /** 132 | * The default value of the json file 133 | */ 134 | public static final String PARAM_FILE_NAME_DEFAULT = "authorizables.json"; 135 | 136 | /** 137 | * The property for the json file 138 | */ 139 | @Property( 140 | label = "JSON Filename", 141 | description = "Filename (path) of the json file that stores the user and group information.", 142 | value = PARAM_FILE_NAME_DEFAULT 143 | ) 144 | public static final String PARAM_FILE_NAME = "filename"; 145 | 146 | /** 147 | * name of this provider 148 | */ 149 | private String name; 150 | 151 | /** 152 | * configured filename 153 | */ 154 | private String fileName; 155 | 156 | /** 157 | * resolved json file 158 | */ 159 | private File jsonFile; 160 | 161 | @SuppressWarnings("UnusedDeclaration") 162 | @Activate 163 | private void activate(Map properties) { 164 | ConfigurationParameters cfg = ConfigurationParameters.of(properties); 165 | name = cfg.getConfigValue(PARAM_NAME, PARAM_NAME_DEFAULT); 166 | fileName = cfg.getConfigValue(PARAM_FILE_NAME, PARAM_FILE_NAME_DEFAULT); 167 | init(); 168 | } 169 | 170 | @SuppressWarnings("UnusedDeclaration") 171 | @Deactivate 172 | private void deactivate() { 173 | } 174 | 175 | /** 176 | * Initialized the provider and validates the properties. 177 | */ 178 | private void init() { 179 | if (jsonFile == null || !jsonFile.exists()) { 180 | try { 181 | jsonFile = new File(fileName).getCanonicalFile(); 182 | log.info("json file IDP initialized. using file: {}", jsonFile.getPath()); 183 | } catch (IOException e) { 184 | jsonFile = null; 185 | log.warn("error while initializing json file IDP. ", e); 186 | } 187 | } 188 | } 189 | 190 | /** 191 | * Loads the authorizable JSON. 192 | * @return the JSON object of the data. 193 | * @throws IOException if an error occurrs 194 | */ 195 | @Nonnull 196 | private JSONObject loadJSON() throws IOException { 197 | init(); 198 | if (jsonFile != null) { 199 | String json = FileUtils.readFileToString(jsonFile, "utf-8"); 200 | try { 201 | return new JSONObject(json); 202 | } catch (JSONException e) { 203 | log.error("error while parsing json {}", fileName, e); 204 | throw new IOException("Error while parsing json"); 205 | } 206 | } else { 207 | throw new FileNotFoundException("JSON file not found: " + fileName); 208 | } 209 | } 210 | 211 | /** 212 | * Checks if the given identity reference has the same provider name as this one. 213 | * @param ref the reference 214 | * @return {@code true} if the reference originates from this provider. 215 | */ 216 | private boolean isMyRef(@Nonnull ExternalIdentityRef ref) { 217 | final String refProviderName = ref.getProviderName(); 218 | return refProviderName == null || refProviderName.isEmpty() || getName().equals(refProviderName); 219 | } 220 | 221 | /** 222 | * Creates a new external identity of the given {@code type} or {@code null} if the type does not match the object 223 | * @param id the id 224 | * @param ref the extern reference or {@code null} 225 | * @param obj the json data 226 | * @param type the desired type 227 | * @return the new identity or {@code null} 228 | * @throws JSONException 229 | */ 230 | @CheckForNull 231 | private T createIdentity(@Nonnull String id, @Nullable ExternalIdentityRef ref, 232 | @Nullable JSONObject obj, @Nonnull Class type) throws JSONException { 233 | if (obj == null) { 234 | return null; 235 | } 236 | if ((type == ExternalGroup.class || type == ExternalIdentity.class) && obj.has(PN_MEMBERS)) { 237 | if (ref == null) { 238 | ref = new ExternalIdentityRef(id, getName()); 239 | } 240 | //noinspection unchecked 241 | return (T) new ExternalGroupImpl(getName(), ref, id, convertJSONtoMap(obj)); 242 | 243 | } else if ((type == ExternalUser.class || type == ExternalIdentity.class) && !obj.has(PN_MEMBERS)) { 244 | if (ref == null) { 245 | ref = new ExternalIdentityRef(id, getName()); 246 | } 247 | //noinspection unchecked 248 | return (T) new ExternalUserImpl(getName(), ref, id, convertJSONtoMap(obj)); 249 | } else { 250 | return null; 251 | } 252 | } 253 | 254 | /** 255 | * Returns an iterator over all identities of the given type 256 | * @param type the type 257 | * @return an iterator 258 | * @throws ExternalIdentityException 259 | */ 260 | @Nonnull 261 | private Iterator listIdentities(@Nonnull Class type) throws ExternalIdentityException { 262 | try { 263 | List identities = new ArrayList(); 264 | JSONObject obj = loadJSON(); 265 | JSONArray names = obj.names(); 266 | for (int i=0; i convertJSONtoMap(@Nonnull JSONObject obj) throws JSONException { 288 | Map props = new HashMap(); 289 | JSONArray names = obj.names(); 290 | for (int i=0; i listUsers() throws ExternalIdentityException { 414 | return listIdentities(ExternalUser.class); 415 | } 416 | 417 | /** 418 | * List all external groups. 419 | * @return an iterator over all external groups 420 | * @throws ExternalIdentityException if an error occurs. 421 | */ 422 | @Nonnull 423 | public Iterator listGroups() throws ExternalIdentityException { 424 | return listIdentities(ExternalGroup.class); 425 | } 426 | 427 | } -------------------------------------------------------------------------------- /step-04/images/example-01-find-json-idp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Adobe-Marketing-Cloud/aem-ldap-tutorial/c19b8f0cb1f828eeb311fa12e22e3c9323457ca2/step-04/images/example-01-find-json-idp.png -------------------------------------------------------------------------------- /step-04/images/example-02-configure-idp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Adobe-Marketing-Cloud/aem-ldap-tutorial/c19b8f0cb1f828eeb311fa12e22e3c9323457ca2/step-04/images/example-02-configure-idp.png -------------------------------------------------------------------------------- /step-04/images/example-03-configure-synchandler.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Adobe-Marketing-Cloud/aem-ldap-tutorial/c19b8f0cb1f828eeb311fa12e22e3c9323457ca2/step-04/images/example-03-configure-synchandler.png -------------------------------------------------------------------------------- /step-04/images/example-04-configure-loginmodule.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Adobe-Marketing-Cloud/aem-ldap-tutorial/c19b8f0cb1f828eeb311fa12e22e3c9323457ca2/step-04/images/example-04-configure-loginmodule.png -------------------------------------------------------------------------------- /step-04/images/example-05-useradmin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Adobe-Marketing-Cloud/aem-ldap-tutorial/c19b8f0cb1f828eeb311fa12e22e3c9323457ca2/step-04/images/example-05-useradmin.png -------------------------------------------------------------------------------- /step-04/json-idp-config-pkg.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Adobe-Marketing-Cloud/aem-ldap-tutorial/c19b8f0cb1f828eeb311fa12e22e3c9323457ca2/step-04/json-idp-config-pkg.zip -------------------------------------------------------------------------------- /step-04/tutorial-04-example-idp.md: -------------------------------------------------------------------------------- 1 | AEM 6.1 Authentication Tutorial Bonus 2 | ===================================== 3 | 4 | Step 04 - Creating your own IDP 5 | ------------------------------- 6 | In this step we show you how to create your own IDP. The IDP provided in this example is very simple as it reads the authorizable data from a JSON file. The sample data in [authorizables.json](authorizables.json) provides the crews of 2 fictional spaceships. 7 | 8 | The code for the new IDP is located in the [example-idp](example-idp/) directory. The majority of the code is in the [JsonFileIdentityProvider.java](example-idp/src/main/java/com/adobe/gems/exampleidp/impl/JsonFileIdentityProvider.java) class. The code is very simple and should be self explanatory. 9 | 10 | **!** If you want to fast forward, here's the [json-idp-config-pkg.zip](json-idp-config-pkg.zip) package with the configuration. all you need to do is to build and deploy the bundle. 11 | 12 | ### Requirements 13 | 1. Running vanilla AEM 6.1 installation 14 | 2. Apache maven 15 | 16 | #### 1. build the bundle 17 | - open a terminal or command prompt and navigate into the `example-idp` directory. 18 | - build and install the bundle with `mvn clean install sling:install` 19 | 20 | the output should show something like: 21 | 22 | ```` 23 | ... 24 | [INFO] ------------------------------------------------------------------------ 25 | [INFO] Building AEM 6.1 Gems Example IDP 1.0-SNAPSHOT 26 | [INFO] ------------------------------------------------------------------------ 27 | ... 28 | [INFO] 29 | [INFO] --- maven-sling-plugin:2.1.0:install (default-cli) @ com.adobe.gems.exampleidp --- 30 | [INFO] Installing Bundle com.adobe.gems.exampleidp(.../example-idp/target/com.adobe.gems.exampleidp-1.0-SNAPSHOT.jar) to http://localhost:4502/system/console via POST 31 | [INFO] Bundle installed 32 | [INFO] ------------------------------------------------------------------------ 33 | [INFO] BUILD SUCCESS 34 | [INFO] ------------------------------------------------------------------------ 35 | ... 36 | ```` 37 | 38 | This will build the bundle and automatically deploy it into an AEM instance running at localhost:4502. 39 | 40 | #### 2. configure the identity provider 41 | Similar to the steps when configuring the ldap idp, we need to create a configuration for our new IDP. 42 | 43 | - open the [Felix Configuration Manager](http://localhost:4502/system/console/configMgr) and search for the _"json file identity provider"_ factory config and click on the plus **+** button. 44 | 45 | ![create json idp](images/example-01-find-json-idp.png) 46 | 47 | Enter the following information: 48 | 49 | 50 | | Name | Value | 51 | |---------------|--------------------| 52 | | Provider Name | json | 53 | | JSON Filename | authorizables.json | 54 | 55 | 56 | ![config json idp](images/example-02-configure-idp.png) 57 | 58 | #### 3. copy the authorizables json file 59 | As soon as you configure the IDP, you should see a log entry like this: 60 | 61 | ``` 62 | 23.07.2015 14:15:15.936 *INFO* [CM Event Dispatcher (Fire ConfigurationEvent: pid=com.adobe.gems.exampleidp.impl.JsonFileIdentityProvider.467a467e-6559-4e40-bb7e-041becb6819e)] com.adobe.gems.exampleidp.impl.JsonFileIdentityProvider json file IDP initialized. using file: /data/develop/cq5/6.1/author/authorizables.json 63 | ``` 64 | 65 | as you can see, the file that the IDP needs is relative to where you started AEM. so copy the sample [authorizables.json](authorizables.json) there. 66 | 67 | #### 4. configure the sync handler 68 | Similar to the steps when configuring the sync handler for the ldap idp, we need to create a new configuration here as well. 69 | 70 | - open the [Felix Configuration Manager](http://localhost:4502/system/console/configMgr) and search for the _"Default Sync Handler"_ factory config and click on the plus **+** button. 71 | 72 | ![find sync handler](../step-02/images/aem-03-find-synchandler.png) 73 | 74 | Enter the following information: 75 | 76 | | Name | Value 77 | |-------------------------------|-------------------- 78 | | Sync Handler Name | `tutorial_handler` 79 | | User Expiration Time | `10s` 80 | | User auto membership | `contributor` 81 | | User property mapping | `rep:fullname=cn`
`profile/nt:primaryType="nt:unstructured"`
`profile/givenName=givenname`
`profile/familyName=familyname`
`profile/email=email` | 82 | | User Path Prefix | `/seven_skies` 83 | | User Membership Expiration | `1h` 84 | | User membership nesting depth | `1` 85 | | Group Expiration Time | `1d` 86 | | Group auto membership | 87 | | Group property mapping | 88 | | Group Path Prefix | `/seven_skies` 89 | 90 | And save the config. We already add the mappings for the extra profile properties, as we learned in [Step 03](../step-03/tutorial-03-test.md) 91 | 92 | ![configure synchandler](images/example-03-configure-synchandler.png) 93 | 94 | #### 5. configure the external login module 95 | As before, the external login module is the bridge between the login, the idp and the sync handler. so add a new configuration that pairs the new `json` idp with the `tutorial_handler` sync handler 96 | 97 | - open the [Felix Configuration Manager](http://localhost:4502/system/console/configMgr) and search for the _"External Login Module"_ factory config and click on the plus **+** button. 98 | 99 | ![create login module](../step-02/images/aem-05-find-loginmodule.png) 100 | 101 | Enter the following information: 102 | 103 | | Name | Value 104 | |------------------------|---------- 105 | | JAAS Ranking | 50 106 | | JAAS Control Flag | `SUFFICIENT` 107 | | JAAS Realm | 108 | | Identity Provider Name | `json` 109 | | Sync Handler Name | `tutorial_handler` 110 | 111 | And save the config 112 | 113 | ![configure loginmodule](images/example-04-configure-loginmodule.png) 114 | 115 | #### 6. test that it works 116 | - ensure to logout previous session or clear all browser cookie or use a different browser, hostname or IP 117 | - open browser to aem: http://127.0.0.1:4502/ 118 | - login as `kirk` with password `pass` 119 | 120 | If the login succeeds, you should now see the authoring environment because we configured the `user.autoMembership` to include the `contributor` group. 121 | 122 | Looking at the users and groups should show the group _enterprise_ and his member(s): 123 | 124 | - open the [AEM useradmin](http://localhost:4502/useradmin) 125 | - search for `enterprise` 126 | - after that, use the JMX console to import the test of the space team! 127 | 128 | ![useradmin](images/example-05-useradmin.png) 129 | 130 | 131 | --------------------------------------------------------------------------------