diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml new file mode 100644 index 0000000..c667862 --- /dev/null +++ b/.github/workflows/ci-build.yml @@ -0,0 +1,19 @@ +# This workflow will build a Java project with Maven +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven + +name: CI build + +on: [ push, pull_request ] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'temurin' + cache: 'maven' + - name: Maven build + run: mvn -B clean verify diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8b8c81d --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +target/ +dependency-reduced-pom.xml diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..4ca3427 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..aa00ffa --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 0000000..712ab9d --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..67e1e61 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/Attach_Keycloak_localhost_8787.xml b/.idea/runConfigurations/Attach_Keycloak_localhost_8787.xml new file mode 100644 index 0000000..86dd4bf --- /dev/null +++ b/.idea/runConfigurations/Attach_Keycloak_localhost_8787.xml @@ -0,0 +1,14 @@ + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/docker_compose.xml b/.idea/runConfigurations/docker_compose.xml new file mode 100644 index 0000000..4b33458 --- /dev/null +++ b/.idea/runConfigurations/docker_compose.xml @@ -0,0 +1,16 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml new file mode 100644 index 0000000..2b63946 --- /dev/null +++ b/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000..cb28b0e Binary files /dev/null and b/.mvn/wrapper/maven-wrapper.jar differ diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..346d645 --- /dev/null +++ b/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.6/apache-maven-3.9.6-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md new file mode 100644 index 0000000..9bdef5b --- /dev/null +++ b/README.md @@ -0,0 +1,22 @@ +# Keycloak Confluence LDAP Group Mapper + +[![CI build](https://github.com/vaulttec/keycloak-confluence-ldap-group-mapper/actions/workflows/ci-build.yml/badge.svg)](hhttps://github.com/vaulttec/keycloak-confluence-ldap-group-mapper/actions/workflows/ci-build.yml) +![](https://img.shields.io/github/license/vaulttec/keycloak-confluence-ldap-group-mapper?label=License) +![](https://img.shields.io/badge/Keycloak-23.0-blue) + +Custom [Keycloak](https://www.keycloak.org) LDAP Group Mapper which creates groups and group memberships retrieved from [Confluence](https://www.atlassian.com/software/confluence) pages (representing the group hierarchy) and page properties (providing a HTML table with a group member column) via [Confluence's REST API](config/mock/confluence-openapi.yaml). + +This project uses ideas or artifacts from other projects, e.g. +* The [peanuts-userprovider](https://github.com/dasniko/keycloak-extensions-demo/tree/main/peanuts-userprovider) from Niko Köbler's [keycloak-extensions-demo](https://github.com/dasniko/keycloak-extensions-demo) project. +* The test support [KeycloakEnvironment](https://github.com/thomasdarimont/keycloak-project-example/blob/main/keycloak/extensions/src/test/java/com/github/thomasdarimont/keycloak/custom/KeycloakEnvironment.java) and [OpenLDAP support](https://github.com/thomasdarimont/keycloak-project-example/blob/main/deployments/local/dev/docker-compose-openldap.yml) from Thomas Darimont's [keycloak-project-example](https://github.com/thomasdarimont/keycloak-project-example) + +Kudos to Niko and Thomas for their great work! + +## Demo Docker Compose Environment + +There's a `docker-compose.yml` definition to use with Docker Compose. It uses the same configuration as the integration tests. + +Build and run all the stuff with: +``` +./mvnw clean package -DskipTests && docker compose up --force-recreate +``` diff --git a/config/global.env b/config/global.env new file mode 100644 index 0000000..7f7882c --- /dev/null +++ b/config/global.env @@ -0,0 +1,38 @@ +# Keycloak +KEYCLOAK_ADMIN=admin +KEYCLOAK_ADMIN_PASSWORD=admin +KC_HTTP_RELATIVE_PATH=/auth +KC_FEATURES=preview +KC_LOG_LEVEL=INFO,org.vaulttec:DEBUG +LDAP_URL=ldap://openldap:389 +CONFLUENCE_URL=http://mockserver:1080/confluence + +# Keycloak CLI +KEYCLOAK_USER=admin +KEYCLOAK_PASSWORD=admin +KEYCLOAK_URL=http://keycloak:8080/auth +KEYCLOAK_FRONTEND_URL=http://keycloak:8080/auth +KEYCLOAK_AVAILABILITYCHECK_ENABLED=true +KEYCLOAK_AVAILABILITYCHECK_TIMEOUT=30s +IMPORT_FILES_LOCATION=/config/* +IMPORT_CACHE_ENABLED=true +IMPORT_VAR_SUBSTITUTION_ENABLED=true +IMPORT_VALIDATE=true + # Root log level of INFO is needed for Testcontainers Wait Strategy +LOGGING_LEVEL_ROOT=INFO +LOGGING_LEVEL_KEYCLOAKCONFIGCLI=DEBUG +LOGGING_LEVEL_REALMCONFIG=DEBUG + +# OpenLDAP +LDAP_ORGANISATION=ACME +LDAP_DOMAIN=corp.acme.local +LDAP_USER=cn=keycloak,dc=corp,dc=acme,dc=local +LDAP_PASSWORD=keycloak +LDAP_USERS_DN=dc=corp,dc=acme,dc=local +LDAP_GROUP_DN=dc=corp,dc=acme,dc=local +LDAP_SEED_INTERNAL_LDIF_PATH=/tmp/ldif +LDAP_LOG_LEVEL=0 + +# Mock Server +MOCKSERVER_LOG_LEVEL=WARN +MOCKSERVER_INITIALIZATION_JSON_PATH=/config/initializerJson.json diff --git a/config/ldap/acme.ldif b/config/ldap/acme.ldif new file mode 100644 index 0000000..330e1fb --- /dev/null +++ b/config/ldap/acme.ldif @@ -0,0 +1,3289 @@ +version: 1 + +# Add Keycloak bind user +dn: cn=keycloak,dc=corp,dc=acme,dc=local +changetype: add +objectClass: person +objectClass: top +cn: keycloak +sn: keycloak +userPassword:: e1NTSEF9MW1pSWwrTUROUGlvMExTRUZPOGloejk1eldpTTN3ZGRIZDV6Z2c9P + Q== + +# Add custom ACL for keycloak bind user +dn: olcDatabase={1}mdb,cn=config +changetype: modify +add: olcAccess +olcAccess: {2}to * by self read by dn="cn=admin,dc=corp,dc=acme,dc=local" by dn="cn=keycloak,dc=corp,dc=acme,dc=local" write by * none + +dn: ou=Accounting,dc=corp,dc=acme,dc=local +changetype: add +ou: Accounting +objectClass: top +objectClass: organizationalUnit + +dn: ou=Product Development,dc=corp,dc=acme,dc=local +changetype: add +ou: Product Development +objectClass: top +objectClass: organizationalUnit + +dn: ou=Product Testing,dc=corp,dc=acme,dc=local +changetype: add +ou: Product Testing +objectClass: top +objectClass: organizationalUnit + +dn: ou=Human Resources,dc=corp,dc=acme,dc=local +changetype: add +ou: Human Resources +objectClass: top +objectClass: organizationalUnit + +dn: ou=Payroll,dc=corp,dc=acme,dc=local +changetype: add +ou: Payroll +objectClass: top +objectClass: organizationalUnit + +dn: ou=Janitorial,dc=corp,dc=acme,dc=local +changetype: add +ou: Janitorial +objectClass: top +objectClass: organizationalUnit + +dn: ou=Management,dc=corp,dc=acme,dc=local +changetype: add +ou: Management +objectClass: top +objectClass: organizationalUnit + +dn: ou=Administrative,dc=corp,dc=acme,dc=local +changetype: add +ou: Administrative +objectClass: top +objectClass: organizationalUnit + +dn: ou=Peons,dc=corp,dc=acme,dc=local +changetype: add +ou: Peons +objectClass: top +objectClass: organizationalUnit + +dn: ou=Planning,dc=corp,dc=acme,dc=local +changetype: add +ou: Planning +objectClass: top +objectClass: organizationalUnit + +dn: cn=John Doo,ou=Planning,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: John Doo +sn: Doo +description: This is John Doo's description +facsimileTelephoneNumber: +1 510 315-3162 +l: Sunnyvale +ou: Planning +postalAddress: Accounting$Sunnyvale +telephoneNumber: +1 510 408-1538 +title: Head of Planning +userPassword: Password1 +uid: DooJ1 +givenName: John +mail: JohnDoo@ns-mail8.com +carLicense: BDGU314 +departmentNumber: 1224 +employeeType: Manager +homePhone: +1 510 464-1671 +initials: J. D. +mobile: +1 510 180-1671 +pager: +1 510 699-1671 +roomNumber: 7051 + +dn: cn=Jane Doo,ou=Accounting,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Jane Doo +sn: Doo +description: This is Jane Doo's description +facsimileTelephoneNumber: +1 510 315-3162 +l: Sunnyvale +ou: Accounting +postalAddress: Accounting$Sunnyvale +telephoneNumber: +1 510 408-1538 +title: Head of Accounting +userPassword: Password1 +uid: DooJ2 +givenName: Jane +mail: JaneDoo@ns-mail8.com +carLicense: BDGU314 +departmentNumber: 1224 +employeeType: Manager +homePhone: +1 510 464-1670 +initials: J. D. +mobile: +1 510 180-1670 +pager: +1 510 699-1670 +roomNumber: 7050 + +dn: cn=Jim Doo,ou=Janitorial,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Jim Doo +sn: Doo +description: This is Jim Doo's description +facsimileTelephoneNumber: +1 510 315-3162 +l: Sunnyvale +ou: Janitorial +postalAddress: Janitorial$Sunnyvale +telephoneNumber: +1 510 408-1538 +title: Chief Janitorial Admin +userPassword: Password1 +uid: DooJ3 +givenName: Jim +mail: JimDoo@ns-mail8.com +carLicense: BDGU31 +departmentNumber: 9222 +employeeType: Employee +homePhone: +1 510 464-1671 +initials: J. D. +mobile: +1 510 180-1685 +pager: +1 510 699-9122 +roomNumber: 8640 + +dn: cn=Anica Kakuta,ou=Janitorial,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Anica Kakuta +sn: Kakuta +description: This is Anica Kakuta's description +facsimileTelephoneNumber: +1 818 449-2614 +l: Armonk +ou: Janitorial +postalAddress: Janitorial$Armonk +telephoneNumber: +1 818 748-7509 +title: Master Janitorial Mascot +userPassword: Password1 +uid: KakutaA +givenName: Anica +mail: KakutaA@ns-mail4.com +carLicense: VU6HIV +departmentNumber: 3490 +employeeType: Employee +homePhone: +1 818 483-2264 +initials: A. K. +mobile: +1 818 787-1089 +pager: +1 818 972-7280 +roomNumber: 8483 +manager: cn=Redgie Fleugel,ou=Janitorial,dc=corp,dc=acme,dc=local + +dn: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Evvy Gattrell +sn: Gattrell +description: This is Evvy Gattrell's description +facsimileTelephoneNumber: +1 818 219-8955 +l: Palo Alto +ou: Management +postalAddress: Management$Palo Alto +telephoneNumber: +1 818 156-4431 +title: Associate Management Director +userPassword: Password1 +uid: GattrelE +givenName: Evvy +mail: GattrelE@ns-mail6.com +carLicense: 39WWKF +departmentNumber: 6424 +employeeType: Employee +homePhone: +1 818 284-8958 +initials: E. G. +mobile: +1 818 531-5583 +pager: +1 818 813-3201 +roomNumber: 9817 +secretary: cn=Anica Kakuta,ou=Janitorial,dc=corp,dc=acme,dc=local +manager: cn=Redgie Fleugel,ou=Janitorial,dc=corp,dc=acme,dc=local + +dn: cn=Tilly Pilote,ou=Peons,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Tilly Pilote +sn: Pilote +description: This is Tilly Pilote's description +facsimileTelephoneNumber: +1 804 640-1719 +l: San Mateo +ou: Peons +postalAddress: Peons$San Mateo +telephoneNumber: +1 804 399-6208 +title: Associate Peons Admin +userPassword: Password1 +uid: PiloteT +givenName: Tilly +mail: PiloteT@ns-mail9.com +carLicense: PPRWB5 +departmentNumber: 7000 +employeeType: Contract +homePhone: +1 804 515-6885 +initials: T. P. +mobile: +1 804 112-4703 +pager: +1 804 758-8153 +roomNumber: 9573 +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local +manager: cn=Redgie Fleugel,ou=Janitorial,dc=corp,dc=acme,dc=local + +dn: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Nashir Shiffer +sn: Shiffer +description: This is Nashir Shiffer's description +facsimileTelephoneNumber: +1 804 395-7794 +l: Armonk +ou: Management +postalAddress: Management$Armonk +telephoneNumber: +1 804 654-9736 +title: Associate Management Warrior +userPassword: Password1 +uid: ShifferN +givenName: Nashir +mail: ShifferN@ns-mail7.com +carLicense: D1MVLI +departmentNumber: 1436 +employeeType: Contract +homePhone: +1 804 994-7078 +initials: N. S. +mobile: +1 804 897-9748 +pager: +1 804 111-1942 +roomNumber: 8672 +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local +manager: cn=Tilly Pilote,ou=Peons,dc=corp,dc=acme,dc=local + +dn: cn=Joy Hilton,ou=Peons,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Joy Hilton +sn: Hilton +description: This is Joy Hilton's description +facsimileTelephoneNumber: +1 415 295-3430 +l: San Mateo +ou: Peons +postalAddress: Peons$San Mateo +telephoneNumber: +1 415 791-7686 +title: Associate Peons President +userPassword: Password1 +uid: HiltonJ +givenName: Joy +mail: HiltonJ@ns-mail5.com +carLicense: YV34JP +departmentNumber: 7074 +employeeType: Employee +homePhone: +1 415 858-4150 +initials: J. H. +mobile: +1 415 695-7685 +pager: +1 415 309-8363 +roomNumber: 9755 +secretary: cn=Anica Kakuta,ou=Janitorial,dc=corp,dc=acme,dc=local +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Christel Basmadjian,ou=Product Testing,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Christel Basmadjian +sn: Basmadjian +description: This is Christel Basmadjian's description +facsimileTelephoneNumber: +1 415 556-4046 +l: Palo Alto +ou: Product Testing +postalAddress: Product Testing$Palo Alto +telephoneNumber: +1 415 279-4634 +title: Master Product Testing Visionary +userPassword: Password1 +uid: BasmadjC +givenName: Christel +mail: BasmadjC@ns-mail8.com +carLicense: EYSLAE +departmentNumber: 5127 +employeeType: Normal +homePhone: +1 415 198-1452 +initials: C. B. +mobile: +1 415 424-9664 +pager: +1 415 186-2950 +roomNumber: 9934 +secretary: cn=Anica Kakuta,ou=Janitorial,dc=corp,dc=acme,dc=local +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Roselin Charney,ou=Payroll,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Roselin Charney +sn: Charney +description: This is Roselin Charney's description +facsimileTelephoneNumber: +1 415 174-5083 +l: Cupertino +ou: Payroll +postalAddress: Payroll$Cupertino +telephoneNumber: +1 415 306-9466 +title: Supreme Payroll Warrior +userPassword: Password1 +uid: CharneyR +givenName: Roselin +mail: CharneyR@ns-mail8.com +carLicense: BTH7XQ +departmentNumber: 8954 +employeeType: Contract +homePhone: +1 415 990-7114 +initials: R. C. +mobile: +1 415 276-4723 +pager: +1 415 592-8003 +roomNumber: 8791 +manager: cn=Redgie Fleugel,ou=Janitorial,dc=corp,dc=acme,dc=local + +dn: cn=Lorita Bittenbender,ou=Payroll,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Lorita Bittenbender +sn: Bittenbender +description: This is Lorita Bittenbender's description +facsimileTelephoneNumber: +1 510 750-5365 +l: San Mateo +ou: Payroll +postalAddress: Payroll$San Mateo +telephoneNumber: +1 510 101-8577 +title: Junior Payroll Director +userPassword: Password1 +uid: BittenbL +givenName: Lorita +mail: BittenbL@ns-mail9.com +carLicense: MR2Q3H +departmentNumber: 8654 +employeeType: Normal +homePhone: +1 510 791-2621 +initials: L. B. +mobile: +1 510 802-3579 +pager: +1 510 485-1258 +roomNumber: 9594 +secretary: cn=Anica Kakuta,ou=Janitorial,dc=corp,dc=acme,dc=local +manager: cn=Roselin Charney,ou=Payroll,dc=corp,dc=acme,dc=local + +dn: cn=Kimihiko Fujiwara,ou=Administrative,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Kimihiko Fujiwara +sn: Fujiwara +description: This is Kimihiko Fujiwara's description +facsimileTelephoneNumber: +1 804 721-1544 +l: Fremont +ou: Administrative +postalAddress: Administrative$Fremont +telephoneNumber: +1 804 802-2024 +title: Associate Administrative Stooge +userPassword: Password1 +uid: FujiwarK +givenName: Kimihiko +mail: FujiwarK@ns-mail5.com +carLicense: 9X8M99 +departmentNumber: 8753 +employeeType: Employee +homePhone: +1 804 593-7870 +initials: K. F. +mobile: +1 804 556-6289 +pager: +1 804 172-2546 +roomNumber: 9634 +manager: cn=Tilly Pilote,ou=Peons,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Pas Linder,ou=Janitorial,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Pas Linder +sn: Linder +description: This is Pas Linder's description +facsimileTelephoneNumber: +1 804 634-3333 +l: San Francisco +ou: Janitorial +postalAddress: Janitorial$San Francisco +telephoneNumber: +1 804 213-3375 +title: Master Janitorial Consultant +userPassword: Password1 +uid: LinderP +givenName: Pas +mail: LinderP@ns-mail9.com +carLicense: HVHA6S +departmentNumber: 2743 +employeeType: Contract +homePhone: +1 804 384-9662 +initials: P. L. +mobile: +1 804 102-5670 +pager: +1 804 197-1463 +roomNumber: 8958 +manager: cn=Tilly Pilote,ou=Peons,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Helge Blumenfeld,ou=Product Testing,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Helge Blumenfeld +sn: Blumenfeld +description: This is Helge Blumenfeld's description +facsimileTelephoneNumber: +1 804 169-5952 +l: San Francisco +ou: Product Testing +postalAddress: Product Testing$San Francisco +telephoneNumber: +1 804 351-9054 +title: Associate Product Testing Stooge +userPassword: Password1 +uid: BlumenfH +givenName: Helge +mail: BlumenfH@ns-mail9.com +carLicense: IOP72I +departmentNumber: 5084 +employeeType: Employee +homePhone: +1 804 160-2700 +initials: H. B. +mobile: +1 804 532-3761 +pager: +1 804 246-6956 +roomNumber: 8401 +manager: cn=Tilly Pilote,ou=Peons,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Joete Lough,ou=Payroll,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Joete Lough +sn: Lough +description: This is Joete Lough's description +facsimileTelephoneNumber: +1 510 916-2579 +l: Milpitas +ou: Payroll +postalAddress: Payroll$Milpitas +telephoneNumber: +1 510 225-4178 +title: Chief Payroll Director +userPassword: Password1 +uid: LoughJ +givenName: Joete +mail: LoughJ@ns-mail4.com +carLicense: 7X8DSV +departmentNumber: 5497 +employeeType: Employee +homePhone: +1 510 227-4121 +initials: J. L. +mobile: +1 510 269-9216 +pager: +1 510 155-8420 +roomNumber: 8072 +manager: cn=Tilly Pilote,ou=Peons,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Pak-Jong Marouchos,ou=Administrative,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Pak-Jong Marouchos +sn: Marouchos +description: This is Pak-Jong Marouchos's description +facsimileTelephoneNumber: +1 408 634-3839 +l: Armonk +ou: Administrative +postalAddress: Administrative$Armonk +telephoneNumber: +1 408 515-7189 +title: Master Administrative Artist +userPassword: Password1 +uid: MarouchP +givenName: Pak-Jong +mail: MarouchP@ns-mail5.com +carLicense: WTDP4S +departmentNumber: 8014 +employeeType: Employee +homePhone: +1 408 781-3799 +initials: P. M. +mobile: +1 408 616-6286 +pager: +1 408 124-2046 +roomNumber: 9840 +manager: cn=Tilly Pilote,ou=Peons,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Dat Mejia,ou=Human Resources,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Dat Mejia +sn: Mejia +description: This is Dat Mejia's description +facsimileTelephoneNumber: +1 804 240-2269 +l: Santa Clara +ou: Human Resources +postalAddress: Human Resources$Santa Clara +telephoneNumber: +1 804 418-3946 +title: Supreme Human Resources Architect +userPassword: Password1 +uid: MejiaD +givenName: Dat +mail: MejiaD@ns-mail2.com +carLicense: UDR1CH +departmentNumber: 8839 +employeeType: Contract +homePhone: +1 804 501-1284 +initials: D. M. +mobile: +1 804 940-7166 +pager: +1 804 777-9199 +roomNumber: 9353 +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Goldy Jankowski,ou=Janitorial,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Goldy Jankowski +sn: Jankowski +description: This is Goldy Jankowski's description +facsimileTelephoneNumber: +1 415 686-7962 +l: Palo Alto +ou: Janitorial +postalAddress: Janitorial$Palo Alto +telephoneNumber: +1 415 543-8028 +title: Chief Janitorial Warrior +userPassword: Password1 +uid: JankowsG +givenName: Goldy +mail: JankowsG@ns-mail3.com +carLicense: CLM0YH +departmentNumber: 4684 +employeeType: Employee +homePhone: +1 415 764-6176 +initials: G. J. +mobile: +1 415 621-7063 +pager: +1 415 411-2427 +roomNumber: 9062 +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Hiroki Morelli,ou=Human Resources,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Hiroki Morelli +sn: Morelli +description: This is Hiroki Morelli's description +facsimileTelephoneNumber: +1 213 217-2091 +l: Cambridge +ou: Human Resources +postalAddress: Human Resources$Cambridge +telephoneNumber: +1 213 182-4690 +title: Master Human Resources Engineer +userPassword: Password1 +uid: MorelliH +givenName: Hiroki +mail: MorelliH@ns-mail7.com +carLicense: KCVRCT +departmentNumber: 4254 +employeeType: Normal +homePhone: +1 213 211-7773 +initials: H. M. +mobile: +1 213 408-9383 +pager: +1 213 467-7822 +roomNumber: 8730 +manager: cn=Tilly Pilote,ou=Peons,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Hadria Adam,ou=Management,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Hadria Adam +sn: Adam +description: This is Hadria Adam's description +facsimileTelephoneNumber: +1 804 832-1956 +l: Cupertino +ou: Management +postalAddress: Management$Cupertino +telephoneNumber: +1 804 954-5484 +title: Chief Management Mascot +userPassword: Password1 +uid: AdamH +givenName: Hadria +mail: AdamH@ns-mail9.com +carLicense: UE3K6W +departmentNumber: 8391 +employeeType: Employee +homePhone: +1 804 362-3145 +initials: H. A. +mobile: +1 804 851-4844 +pager: +1 804 633-9866 +roomNumber: 9008 +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Loella Tibi,ou=Janitorial,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Loella Tibi +sn: Tibi +description: This is Loella Tibi's description +facsimileTelephoneNumber: +1 804 993-8020 +l: Orem +ou: Janitorial +postalAddress: Janitorial$Orem +telephoneNumber: +1 804 370-7549 +title: Associate Janitorial Sales Rep +userPassword: Password1 +uid: TibiL +givenName: Loella +mail: TibiL@ns-mail5.com +carLicense: A4B7L4 +departmentNumber: 8654 +employeeType: Employee +homePhone: +1 804 820-5702 +initials: L. T. +mobile: +1 804 780-6560 +pager: +1 804 885-3920 +roomNumber: 8674 +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Sharri McElligott,ou=Management,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Sharri McElligott +sn: McElligott +description: This is Sharri McElligott's description +facsimileTelephoneNumber: +1 206 824-3652 +l: Palo Alto +ou: Management +postalAddress: Management$Palo Alto +telephoneNumber: +1 206 552-6740 +title: Associate Management Figurehead +userPassword: Password1 +uid: McElligS +givenName: Sharri +mail: McElligS@ns-mail8.com +carLicense: VVG7V6 +departmentNumber: 6125 +employeeType: Normal +homePhone: +1 206 922-3353 +initials: S. M. +mobile: +1 206 298-4265 +pager: +1 206 629-7792 +roomNumber: 8106 +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Yovonnda Van Veen,ou=Product Development,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Yovonnda Van Veen +sn: Van Veen +description: This is Yovonnda Van Veen's description +facsimileTelephoneNumber: +1 408 774-9961 +l: Cambridge +ou: Product Development +postalAddress: Product Development$Cambridge +telephoneNumber: +1 408 170-9652 +title: Supreme Product Development Admin +userPassword: Password1 +uid: Van VeeY +givenName: Yovonnda +mail: Van VeeY@ns-mail2.com +carLicense: IDQN19 +departmentNumber: 3630 +employeeType: Normal +homePhone: +1 408 700-8967 +initials: Y. V. +mobile: +1 408 846-3931 +pager: +1 408 645-3212 +roomNumber: 9081 +manager: cn=Tilly Pilote,ou=Peons,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Aura Quinn,ou=Janitorial,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Aura Quinn +sn: Quinn +description: This is Aura Quinn's description +facsimileTelephoneNumber: +1 206 116-6507 +l: Redmond +ou: Janitorial +postalAddress: Janitorial$Redmond +telephoneNumber: +1 206 821-1850 +title: Junior Janitorial Manager +userPassword: Password1 +uid: QuinnA +givenName: Aura +mail: QuinnA@ns-mail4.com +carLicense: MVTGED +departmentNumber: 2308 +employeeType: Normal +homePhone: +1 206 154-5641 +initials: A. Q. +mobile: +1 206 747-4585 +pager: +1 206 933-5036 +roomNumber: 9706 +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Faina Michalos,ou=Management,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Faina Michalos +sn: Michalos +description: This is Faina Michalos's description +facsimileTelephoneNumber: +1 206 824-7047 +l: Redmond +ou: Management +postalAddress: Management$Redmond +telephoneNumber: +1 206 617-6467 +title: Supreme Management Stooge +userPassword: Password1 +uid: MichaloF +givenName: Faina +mail: MichaloF@ns-mail8.com +carLicense: N9NUVE +departmentNumber: 5205 +employeeType: Contract +homePhone: +1 206 747-8261 +initials: F. M. +mobile: +1 206 370-6675 +pager: +1 206 337-9183 +roomNumber: 9679 +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Weitzel Piasecki,ou=Product Testing,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Weitzel Piasecki +sn: Piasecki +description: This is Weitzel Piasecki's description +facsimileTelephoneNumber: +1 415 369-9640 +l: Redwood Shores +ou: Product Testing +postalAddress: Product Testing$Redwood Shores +telephoneNumber: +1 415 227-6210 +title: Supreme Product Testing Czar +userPassword: Password1 +uid: PiaseckW +givenName: Weitzel +mail: PiaseckW@ns-mail7.com +carLicense: EK7JWG +departmentNumber: 5750 +employeeType: Contract +homePhone: +1 415 631-4536 +initials: W. P. +mobile: +1 415 969-6594 +pager: +1 415 324-9946 +roomNumber: 9567 +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Roseanna Goh,ou=Human Resources,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Roseanna Goh +sn: Goh +description: This is Roseanna Goh's description +facsimileTelephoneNumber: +1 415 451-4260 +l: Redmond +ou: Human Resources +postalAddress: Human Resources$Redmond +telephoneNumber: +1 415 353-8360 +title: Master Human Resources Punk +userPassword: Password1 +uid: GohR +givenName: Roseanna +mail: GohR@ns-mail3.com +carLicense: VQYVKH +departmentNumber: 8527 +employeeType: Normal +homePhone: +1 415 970-6225 +initials: R. G. +mobile: +1 415 649-3386 +pager: +1 415 997-5556 +roomNumber: 9852 +manager: cn=Tilly Pilote,ou=Peons,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Jenifer Wortman,ou=Payroll,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Jenifer Wortman +sn: Wortman +description: This is Jenifer Wortman's description +facsimileTelephoneNumber: +1 510 219-4967 +l: San Jose +ou: Payroll +postalAddress: Payroll$San Jose +telephoneNumber: +1 510 616-5746 +title: Junior Payroll Engineer +userPassword: Password1 +uid: WortmanJ +givenName: Jenifer +mail: WortmanJ@ns-mail3.com +carLicense: TUQTBU +departmentNumber: 6294 +employeeType: Normal +homePhone: +1 510 584-1622 +initials: J. W. +mobile: +1 510 235-2091 +pager: +1 510 202-3776 +roomNumber: 9299 +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Tania Everitt,ou=Management,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Tania Everitt +sn: Everitt +description: This is Tania Everitt's description +facsimileTelephoneNumber: +1 818 126-1680 +l: Cambridge +ou: Management +postalAddress: Management$Cambridge +telephoneNumber: +1 818 969-1227 +title: Master Management Vice President +userPassword: Password1 +uid: EverittT +givenName: Tania +mail: EverittT@ns-mail5.com +carLicense: MEA23G +departmentNumber: 8855 +employeeType: Employee +homePhone: +1 818 835-6339 +initials: T. E. +mobile: +1 818 568-6942 +pager: +1 818 132-5780 +roomNumber: 9993 +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Arlina Erkel,ou=Peons,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Arlina Erkel +sn: Erkel +description: This is Arlina Erkel's description +facsimileTelephoneNumber: +1 818 969-5292 +l: Redwood Shores +ou: Peons +postalAddress: Peons$Redwood Shores +telephoneNumber: +1 818 833-2593 +title: Junior Peons Warrior +userPassword: Password1 +uid: ErkelA +givenName: Arlina +mail: ErkelA@ns-mail3.com +carLicense: XTF35K +departmentNumber: 9345 +employeeType: Contract +homePhone: +1 818 103-1672 +initials: A. E. +mobile: +1 818 263-4930 +pager: +1 818 558-8339 +roomNumber: 9679 +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Yvet ENG,ou=Product Testing,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Yvet ENG +sn: ENG +description: This is Yvet ENG's description +facsimileTelephoneNumber: +1 415 669-5280 +l: Fremont +ou: Product Testing +postalAddress: Product Testing$Fremont +telephoneNumber: +1 415 389-8020 +title: Master Product Testing Czar +userPassword: Password1 +uid: ENGY +givenName: Yvet +mail: ENGY@ns-mail6.com +carLicense: OLCDLR +departmentNumber: 7534 +employeeType: Normal +homePhone: +1 415 519-1854 +initials: Y. E. +mobile: +1 415 773-2475 +pager: +1 415 164-6343 +roomNumber: 8398 +manager: cn=Tilly Pilote,ou=Peons,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Sherrye Buttrey,ou=Janitorial,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Sherrye Buttrey +sn: Buttrey +description: This is Sherrye Buttrey's description +facsimileTelephoneNumber: +1 415 754-6746 +l: Sunnyvale +ou: Janitorial +postalAddress: Janitorial$Sunnyvale +telephoneNumber: +1 415 190-2648 +title: Associate Janitorial Consultant +userPassword: Password1 +uid: ButtreyS +givenName: Sherrye +mail: ButtreyS@ns-mail9.com +carLicense: QCVKJ2 +departmentNumber: 7404 +employeeType: Employee +homePhone: +1 415 651-1536 +initials: S. B. +mobile: +1 415 304-1250 +pager: +1 415 447-8041 +roomNumber: 9797 +manager: cn=Tilly Pilote,ou=Peons,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Alida Nahmias,ou=Janitorial,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Alida Nahmias +sn: Nahmias +description: This is Alida Nahmias's description +facsimileTelephoneNumber: +1 415 492-1662 +l: Redwood Shores +ou: Janitorial +postalAddress: Janitorial$Redwood Shores +telephoneNumber: +1 415 377-7650 +title: Chief Janitorial Stooge +userPassword: Password1 +uid: NahmiasA +givenName: Alida +mail: NahmiasA@ns-mail3.com +carLicense: GW837I +departmentNumber: 4549 +employeeType: Contract +homePhone: +1 415 598-7345 +initials: A. N. +mobile: +1 415 244-3718 +pager: +1 415 929-7865 +roomNumber: 9919 +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Jerrilyn Ruddle,ou=Product Development,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Jerrilyn Ruddle +sn: Ruddle +description: This is Jerrilyn Ruddle's description +facsimileTelephoneNumber: +1 804 830-8298 +l: Redwood Shores +ou: Product Development +postalAddress: Product Development$Redwood Shores +telephoneNumber: +1 804 180-7934 +title: Chief Product Development Madonna +userPassword: Password1 +uid: RuddleJ +givenName: Jerrilyn +mail: RuddleJ@ns-mail4.com +carLicense: PHDM53 +departmentNumber: 4570 +employeeType: Contract +homePhone: +1 804 474-7271 +initials: J. R. +mobile: +1 804 374-3735 +pager: +1 804 386-8771 +roomNumber: 8389 +manager: cn=Tilly Pilote,ou=Peons,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Alev Boucouris,ou=Management,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Alev Boucouris +sn: Boucouris +description: This is Alev Boucouris's description +facsimileTelephoneNumber: +1 408 146-7711 +l: Cupertino +ou: Management +postalAddress: Management$Cupertino +telephoneNumber: +1 408 759-2449 +title: Junior Management Artist +userPassword: Password1 +uid: BoucourA +givenName: Alev +mail: BoucourA@ns-mail7.com +carLicense: V8GELA +departmentNumber: 2840 +employeeType: Employee +homePhone: +1 408 808-1395 +initials: A. B. +mobile: +1 408 308-6826 +pager: +1 408 653-4290 +roomNumber: 8194 +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Hazem Hagan,ou=Management,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Hazem Hagan +sn: Hagan +description: This is Hazem Hagan's description +facsimileTelephoneNumber: +1 818 360-8313 +l: Cambridge +ou: Management +postalAddress: Management$Cambridge +telephoneNumber: +1 818 255-1352 +title: Associate Management Evangelist +userPassword: Password1 +uid: HaganH +givenName: Hazem +mail: HaganH@ns-mail8.com +carLicense: BBN1Y6 +departmentNumber: 8167 +employeeType: Contract +homePhone: +1 818 904-1676 +initials: H. H. +mobile: +1 818 264-6149 +pager: +1 818 910-9009 +roomNumber: 9320 +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Romano IEM,ou=Administrative,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Romano IEM +sn: IEM +description: This is Romano IEM's description +facsimileTelephoneNumber: +1 818 888-4612 +l: Cambridge +ou: Administrative +postalAddress: Administrative$Cambridge +telephoneNumber: +1 818 267-4274 +title: Chief Administrative Technician +userPassword: Password1 +uid: IEMR +givenName: Romano +mail: IEMR@ns-mail3.com +carLicense: QBXXAK +departmentNumber: 1326 +employeeType: Employee +homePhone: +1 818 158-2704 +initials: R. I. +mobile: +1 818 477-8860 +pager: +1 818 104-3298 +roomNumber: 8784 +manager: cn=Tilly Pilote,ou=Peons,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Floria Hoagland,ou=Product Testing,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Floria Hoagland +sn: Hoagland +description: This is Floria Hoagland's description +facsimileTelephoneNumber: +1 213 619-7948 +l: San Jose +ou: Product Testing +postalAddress: Product Testing$San Jose +telephoneNumber: +1 213 641-6481 +title: Master Product Testing Madonna +userPassword: Password1 +uid: HoaglanF +givenName: Floria +mail: HoaglanF@ns-mail2.com +carLicense: 63S8FA +departmentNumber: 2429 +employeeType: Normal +homePhone: +1 213 754-1997 +initials: F. H. +mobile: +1 213 445-7805 +pager: +1 213 586-2690 +roomNumber: 8063 +manager: cn=Tilly Pilote,ou=Peons,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Ray Preston,ou=Product Testing,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Ray Preston +sn: Preston +description: This is Ray Preston's description +facsimileTelephoneNumber: +1 213 838-9375 +l: San Mateo +ou: Product Testing +postalAddress: Product Testing$San Mateo +telephoneNumber: +1 213 125-3426 +title: Supreme Product Testing Consultant +userPassword: Password1 +uid: PrestonR +givenName: Ray +mail: PrestonR@ns-mail3.com +carLicense: RNFK4Y +departmentNumber: 8204 +employeeType: Employee +homePhone: +1 213 446-4465 +initials: R. P. +mobile: +1 213 507-2929 +pager: +1 213 373-6534 +roomNumber: 8894 +manager: cn=Tilly Pilote,ou=Peons,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Veronike Vodicka,ou=Janitorial,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Veronike Vodicka +sn: Vodicka +description: This is Veronike Vodicka's description +facsimileTelephoneNumber: +1 213 657-1189 +l: Orem +ou: Janitorial +postalAddress: Janitorial$Orem +telephoneNumber: +1 213 311-1925 +title: Junior Janitorial Consultant +userPassword: Password1 +uid: VodickaV +givenName: Veronike +mail: VodickaV@ns-mail8.com +carLicense: OFTAVH +departmentNumber: 3879 +employeeType: Contract +homePhone: +1 213 516-5523 +initials: V. V. +mobile: +1 213 280-7666 +pager: +1 213 976-8415 +roomNumber: 8848 +manager: cn=Tilly Pilote,ou=Peons,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Genovera Smulders,ou=Payroll,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Genovera Smulders +sn: Smulders +description: This is Genovera Smulders's description +facsimileTelephoneNumber: +1 415 897-4784 +l: Redmond +ou: Payroll +postalAddress: Payroll$Redmond +telephoneNumber: +1 415 200-1690 +title: Supreme Payroll Dictator +userPassword: Password1 +uid: SmulderG +givenName: Genovera +mail: SmulderG@ns-mail4.com +carLicense: J1Q44M +departmentNumber: 8659 +employeeType: Employee +homePhone: +1 415 400-1612 +initials: G. S. +mobile: +1 415 799-3144 +pager: +1 415 910-6380 +roomNumber: 9866 +manager: cn=Tilly Pilote,ou=Peons,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Fritz Stachowiak,ou=Janitorial,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Fritz Stachowiak +sn: Stachowiak +description: This is Fritz Stachowiak's description +facsimileTelephoneNumber: +1 213 388-3488 +l: Cambridge +ou: Janitorial +postalAddress: Janitorial$Cambridge +telephoneNumber: +1 213 342-6961 +title: Junior Janitorial Evangelist +userPassword: Password1 +uid: StachowF +givenName: Fritz +mail: StachowF@ns-mail7.com +carLicense: QCL57L +departmentNumber: 8997 +employeeType: Normal +homePhone: +1 213 409-7064 +initials: F. S. +mobile: +1 213 291-7561 +pager: +1 213 395-9221 +roomNumber: 8839 +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Gordon Hitchcock,ou=Administrative,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Gordon Hitchcock +sn: Hitchcock +description: This is Gordon Hitchcock's description +facsimileTelephoneNumber: +1 510 820-9805 +l: Cupertino +ou: Administrative +postalAddress: Administrative$Cupertino +telephoneNumber: +1 510 147-8695 +title: Master Administrative Director +userPassword: Password1 +uid: HitchcoG +givenName: Gordon +mail: HitchcoG@ns-mail8.com +carLicense: TXKDC4 +departmentNumber: 9897 +employeeType: Normal +homePhone: +1 510 168-7227 +initials: G. H. +mobile: +1 510 546-4843 +pager: +1 510 326-8101 +roomNumber: 9854 +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Piero MacLean,ou=Janitorial,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Piero MacLean +sn: MacLean +description: This is Piero MacLean's description +facsimileTelephoneNumber: +1 818 689-3101 +l: Menlo Park +ou: Janitorial +postalAddress: Janitorial$Menlo Park +telephoneNumber: +1 818 156-9471 +title: Associate Janitorial Warrior +userPassword: Password1 +uid: MacLeanP +givenName: Piero +mail: MacLeanP@ns-mail8.com +carLicense: 9SH30R +departmentNumber: 7221 +employeeType: Normal +homePhone: +1 818 927-2826 +initials: P. M. +mobile: +1 818 702-4365 +pager: +1 818 559-6404 +roomNumber: 8204 +manager: cn=Tilly Pilote,ou=Peons,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Kass Belcher,ou=Peons,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Kass Belcher +sn: Belcher +description: This is Kass Belcher's description +facsimileTelephoneNumber: +1 206 507-6876 +l: Redwood Shores +ou: Peons +postalAddress: Peons$Redwood Shores +telephoneNumber: +1 206 563-4349 +title: Associate Peons President +userPassword: Password1 +uid: BelcherK +givenName: Kass +mail: BelcherK@ns-mail3.com +carLicense: XJG74B +departmentNumber: 3411 +employeeType: Contract +homePhone: +1 206 251-4208 +initials: K. B. +mobile: +1 206 517-9175 +pager: +1 206 423-2595 +roomNumber: 8891 +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Geoff Petrick,ou=Management,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Geoff Petrick +sn: Petrick +description: This is Geoff Petrick's description +facsimileTelephoneNumber: +1 206 931-4080 +l: Menlo Park +ou: Management +postalAddress: Management$Menlo Park +telephoneNumber: +1 206 408-8873 +title: Junior Management Warrior +userPassword: Password1 +uid: PetrickG +givenName: Geoff +mail: PetrickG@ns-mail4.com +carLicense: QRS2IV +departmentNumber: 5946 +employeeType: Employee +homePhone: +1 206 606-3394 +initials: G. P. +mobile: +1 206 212-4321 +pager: +1 206 520-1109 +roomNumber: 8565 +manager: cn=Tilly Pilote,ou=Peons,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Lynna Klebsch,ou=Human Resources,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Lynna Klebsch +sn: Klebsch +description: This is Lynna Klebsch's description +facsimileTelephoneNumber: +1 804 708-1163 +l: San Jose +ou: Human Resources +postalAddress: Human Resources$San Jose +telephoneNumber: +1 804 848-5944 +title: Junior Human Resources Engineer +userPassword: Password1 +uid: KlebschL +givenName: Lynna +mail: KlebschL@ns-mail2.com +carLicense: JPRMHP +departmentNumber: 5330 +employeeType: Employee +homePhone: +1 804 885-1229 +initials: L. K. +mobile: +1 804 190-7759 +pager: +1 804 825-4457 +roomNumber: 8905 +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Sibley Frederick,ou=Janitorial,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Sibley Frederick +sn: Frederick +description: This is Sibley Frederick's description +facsimileTelephoneNumber: +1 818 844-7037 +l: San Mateo +ou: Janitorial +postalAddress: Janitorial$San Mateo +telephoneNumber: +1 818 651-3340 +title: Associate Janitorial Warrior +userPassword: Password1 +uid: FrederiS +givenName: Sibley +mail: FrederiS@ns-mail2.com +carLicense: SC6Q2I +departmentNumber: 4175 +employeeType: Employee +homePhone: +1 818 645-2860 +initials: S. F. +mobile: +1 818 684-3673 +pager: +1 818 846-2402 +roomNumber: 8013 +manager: cn=Tilly Pilote,ou=Peons,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Coord Reddington,ou=Product Testing,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Coord Reddington +sn: Reddington +description: This is Coord Reddington's description +facsimileTelephoneNumber: +1 408 655-4629 +l: Sunnyvale +ou: Product Testing +postalAddress: Product Testing$Sunnyvale +telephoneNumber: +1 408 820-2555 +title: Master Product Testing President +userPassword: Password1 +uid: ReddingC +givenName: Coord +mail: ReddingC@ns-mail8.com +carLicense: AVBLSP +departmentNumber: 2194 +employeeType: Employee +homePhone: +1 408 411-7466 +initials: C. R. +mobile: +1 408 965-4472 +pager: +1 408 723-5928 +roomNumber: 9944 +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Graham Pereira,ou=Payroll,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Graham Pereira +sn: Pereira +description: This is Graham Pereira's description +facsimileTelephoneNumber: +1 408 526-6291 +l: Palo Alto +ou: Payroll +postalAddress: Payroll$Palo Alto +telephoneNumber: +1 408 437-3512 +title: Supreme Payroll Mascot +userPassword: Password1 +uid: PereiraG +givenName: Graham +mail: PereiraG@ns-mail7.com +carLicense: 82L3RI +departmentNumber: 7186 +employeeType: Normal +homePhone: +1 408 193-1306 +initials: G. P. +mobile: +1 408 452-6149 +pager: +1 408 196-8079 +roomNumber: 9882 +manager: cn=Tilly Pilote,ou=Peons,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Angy Cuthbert,ou=Product Testing,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Angy Cuthbert +sn: Cuthbert +description: This is Angy Cuthbert's description +facsimileTelephoneNumber: +1 804 408-1630 +l: Orem +ou: Product Testing +postalAddress: Product Testing$Orem +telephoneNumber: +1 804 351-5903 +title: Chief Product Testing Technician +userPassword: Password1 +uid: CuthberA +givenName: Angy +mail: CuthberA@ns-mail5.com +carLicense: IOA2HP +departmentNumber: 5746 +employeeType: Employee +homePhone: +1 804 165-7245 +initials: A. C. +mobile: +1 804 744-5515 +pager: +1 804 705-5980 +roomNumber: 8201 +manager: cn=Tilly Pilote,ou=Peons,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Feodora Hoehn,ou=Payroll,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Feodora Hoehn +sn: Hoehn +description: This is Feodora Hoehn's description +facsimileTelephoneNumber: +1 206 780-2856 +l: Sunnyvale +ou: Payroll +postalAddress: Payroll$Sunnyvale +telephoneNumber: +1 206 346-1250 +title: Chief Payroll Manager +userPassword: Password1 +uid: HoehnF +givenName: Feodora +mail: HoehnF@ns-mail2.com +carLicense: MSSAMG +departmentNumber: 9626 +employeeType: Normal +homePhone: +1 206 141-7389 +initials: F. H. +mobile: +1 206 148-3534 +pager: +1 206 869-5278 +roomNumber: 9504 +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Misti Sinanan,ou=Product Testing,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Misti Sinanan +sn: Sinanan +description: This is Misti Sinanan's description +facsimileTelephoneNumber: +1 206 327-3723 +l: San Jose +ou: Product Testing +postalAddress: Product Testing$San Jose +telephoneNumber: +1 206 731-8974 +title: Junior Product Testing Artist +userPassword: Password1 +uid: SinananM +givenName: Misti +mail: SinananM@ns-mail2.com +carLicense: GVY0L2 +departmentNumber: 9439 +employeeType: Normal +homePhone: +1 206 678-8995 +initials: M. S. +mobile: +1 206 738-2238 +pager: +1 206 176-7878 +roomNumber: 9975 +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Ilya Routhier,ou=Peons,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Ilya Routhier +sn: Routhier +description: This is Ilya Routhier's description +facsimileTelephoneNumber: +1 213 661-8820 +l: Santa Clara +ou: Peons +postalAddress: Peons$Santa Clara +telephoneNumber: +1 213 823-7498 +title: Junior Peons Czar +userPassword: Password1 +uid: RouthieI +givenName: Ilya +mail: RouthieI@ns-mail3.com +carLicense: K9SNHF +departmentNumber: 7574 +employeeType: Contract +homePhone: +1 213 459-1487 +initials: I. R. +mobile: +1 213 397-2381 +pager: +1 213 825-7583 +roomNumber: 9542 +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Megen Thibeault,ou=Management,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Megen Thibeault +sn: Thibeault +description: This is Megen Thibeault's description +facsimileTelephoneNumber: +1 408 409-2882 +l: San Francisco +ou: Management +postalAddress: Management$San Francisco +telephoneNumber: +1 408 859-9279 +title: Associate Management Fellow +userPassword: Password1 +uid: ThibeauM +givenName: Megen +mail: ThibeauM@ns-mail9.com +carLicense: 9HH3AN +departmentNumber: 8743 +employeeType: Employee +homePhone: +1 408 836-9082 +initials: M. T. +mobile: +1 408 309-6861 +pager: +1 408 557-6378 +roomNumber: 8929 +manager: cn=Tilly Pilote,ou=Peons,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Wannell Regimbald,ou=Human Resources,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Wannell Regimbald +sn: Regimbald +description: This is Wannell Regimbald's description +facsimileTelephoneNumber: +1 818 521-3130 +l: Milpitas +ou: Human Resources +postalAddress: Human Resources$Milpitas +telephoneNumber: +1 818 113-7120 +title: Associate Human Resources Technician +userPassword: Password1 +uid: RegimbaW +givenName: Wannell +mail: RegimbaW@ns-mail9.com +carLicense: 2THDCT +departmentNumber: 6395 +employeeType: Employee +homePhone: +1 818 776-4063 +initials: W. R. +mobile: +1 818 231-9431 +pager: +1 818 171-8300 +roomNumber: 9927 +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Odele Rosser,ou=Product Testing,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Odele Rosser +sn: Rosser +description: This is Odele Rosser's description +facsimileTelephoneNumber: +1 213 277-4724 +l: Cupertino +ou: Product Testing +postalAddress: Product Testing$Cupertino +telephoneNumber: +1 213 467-1891 +title: Supreme Product Testing Mascot +userPassword: Password1 +uid: RosserO +givenName: Odele +mail: RosserO@ns-mail4.com +carLicense: SM50GM +departmentNumber: 7376 +employeeType: Employee +homePhone: +1 213 759-1876 +initials: O. R. +mobile: +1 213 473-5144 +pager: +1 213 233-3151 +roomNumber: 8120 +manager: cn=Tilly Pilote,ou=Peons,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Mariska Chauhan,ou=Human Resources,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Mariska Chauhan +sn: Chauhan +description: This is Mariska Chauhan's description +facsimileTelephoneNumber: +1 510 686-8071 +l: Armonk +ou: Human Resources +postalAddress: Human Resources$Armonk +telephoneNumber: +1 510 385-5509 +title: Chief Human Resources Janitor +userPassword: Password1 +uid: ChauhanM +givenName: Mariska +mail: ChauhanM@ns-mail7.com +carLicense: LJT2HS +departmentNumber: 3740 +employeeType: Employee +homePhone: +1 510 470-1535 +initials: M. C. +mobile: +1 510 959-6649 +pager: +1 510 690-3046 +roomNumber: 9630 +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Witold Blesi,ou=Product Testing,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Witold Blesi +sn: Blesi +description: This is Witold Blesi's description +facsimileTelephoneNumber: +1 818 360-1222 +l: San Mateo +ou: Product Testing +postalAddress: Product Testing$San Mateo +telephoneNumber: +1 818 283-2462 +title: Master Product Testing Warrior +userPassword: Password1 +uid: BlesiW +givenName: Witold +mail: BlesiW@ns-mail4.com +carLicense: CK96BA +departmentNumber: 3674 +employeeType: Employee +homePhone: +1 818 341-8860 +initials: W. B. +mobile: +1 818 512-8782 +pager: +1 818 164-4350 +roomNumber: 8828 +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Claudelle Oman,ou=Management,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Claudelle Oman +sn: Oman +description: This is Claudelle Oman's description +facsimileTelephoneNumber: +1 415 217-2238 +l: San Mateo +ou: Management +postalAddress: Management$San Mateo +telephoneNumber: +1 415 141-7796 +title: Associate Management Artist +userPassword: Password1 +uid: OmanC +givenName: Claudelle +mail: OmanC@ns-mail8.com +carLicense: TG2R5U +departmentNumber: 1027 +employeeType: Contract +homePhone: +1 415 496-8846 +initials: C. O. +mobile: +1 415 367-7841 +pager: +1 415 926-2000 +roomNumber: 8004 +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Liz LeClair,ou=Administrative,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Liz LeClair +sn: LeClair +description: This is Liz LeClair's description +facsimileTelephoneNumber: +1 818 436-9079 +l: Alameda +ou: Administrative +postalAddress: Administrative$Alameda +telephoneNumber: +1 818 890-8136 +title: Associate Administrative Technician +userPassword: Password1 +uid: LeClairL +givenName: Liz +mail: LeClairL@ns-mail2.com +carLicense: 5D50MG +departmentNumber: 3922 +employeeType: Employee +homePhone: +1 818 895-3646 +initials: L. L. +mobile: +1 818 815-1759 +pager: +1 818 737-8568 +roomNumber: 8020 +manager: cn=Tilly Pilote,ou=Peons,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Linette Kessler,ou=Administrative,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Linette Kessler +sn: Kessler +description: This is Linette Kessler's description +facsimileTelephoneNumber: +1 804 135-2670 +l: San Mateo +ou: Administrative +postalAddress: Administrative$San Mateo +telephoneNumber: +1 804 609-6227 +title: Master Administrative Writer +userPassword: Password1 +uid: KesslerL +givenName: Linette +mail: KesslerL@ns-mail7.com +carLicense: HWNP8T +departmentNumber: 8941 +employeeType: Normal +homePhone: +1 804 596-8015 +initials: L. K. +mobile: +1 804 910-8981 +pager: +1 804 715-3786 +roomNumber: 9285 +manager: cn=Tilly Pilote,ou=Peons,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Cang Coverdale,ou=Peons,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Cang Coverdale +sn: Coverdale +description: This is Cang Coverdale's description +facsimileTelephoneNumber: +1 804 877-2698 +l: Sunnyvale +ou: Peons +postalAddress: Peons$Sunnyvale +telephoneNumber: +1 804 250-8301 +title: Chief Peons Writer +userPassword: Password1 +uid: CoverdaC +givenName: Cang +mail: CoverdaC@ns-mail3.com +carLicense: 0LT7LC +departmentNumber: 2554 +employeeType: Normal +homePhone: +1 804 958-9840 +initials: C. C. +mobile: +1 804 271-5714 +pager: +1 804 265-1282 +roomNumber: 8314 +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Luella Scheffler,ou=Janitorial,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Luella Scheffler +sn: Scheffler +description: This is Luella Scheffler's description +facsimileTelephoneNumber: +1 804 584-9027 +l: Armonk +ou: Janitorial +postalAddress: Janitorial$Armonk +telephoneNumber: +1 804 913-6978 +title: Chief Janitorial Artist +userPassword: Password1 +uid: SchefflL +givenName: Luella +mail: SchefflL@ns-mail6.com +carLicense: L426VC +departmentNumber: 6083 +employeeType: Normal +homePhone: +1 804 456-5925 +initials: L. S. +mobile: +1 804 159-8096 +pager: +1 804 810-4962 +roomNumber: 9005 +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Ginny Mattiussi,ou=Product Development,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Ginny Mattiussi +sn: Mattiussi +description: This is Ginny Mattiussi's description +facsimileTelephoneNumber: +1 804 914-9991 +l: San Francisco +ou: Product Development +postalAddress: Product Development$San Francisco +telephoneNumber: +1 804 519-9515 +title: Master Product Development Grunt +userPassword: Password1 +uid: MattiusG +givenName: Ginny +mail: MattiusG@ns-mail9.com +carLicense: HW15SO +departmentNumber: 3687 +employeeType: Employee +homePhone: +1 804 213-5427 +initials: G. M. +mobile: +1 804 160-1741 +pager: +1 804 461-9969 +roomNumber: 9539 +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Aline Parham,ou=Administrative,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Aline Parham +sn: Parham +description: This is Aline Parham's description +facsimileTelephoneNumber: +1 510 706-8345 +l: Alameda +ou: Administrative +postalAddress: Administrative$Alameda +telephoneNumber: +1 510 690-4531 +title: Chief Administrative Mascot +userPassword: Password1 +uid: ParhamA +givenName: Aline +mail: ParhamA@ns-mail9.com +carLicense: XXVJPS +departmentNumber: 7990 +employeeType: Contract +homePhone: +1 510 255-3374 +initials: A. P. +mobile: +1 510 423-9519 +pager: +1 510 352-3609 +roomNumber: 9652 +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Nahum Rozumna,ou=Administrative,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Nahum Rozumna +sn: Rozumna +description: This is Nahum Rozumna's description +facsimileTelephoneNumber: +1 415 362-5910 +l: Orem +ou: Administrative +postalAddress: Administrative$Orem +telephoneNumber: +1 415 587-5000 +title: Supreme Administrative Fellow +userPassword: Password1 +uid: RozumnaN +givenName: Nahum +mail: RozumnaN@ns-mail4.com +carLicense: 9X3JU6 +departmentNumber: 5771 +employeeType: Contract +homePhone: +1 415 332-4469 +initials: N. R. +mobile: +1 415 851-6778 +pager: +1 415 744-7717 +roomNumber: 9069 +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Teresa Standel,ou=Human Resources,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Teresa Standel +sn: Standel +description: This is Teresa Standel's description +facsimileTelephoneNumber: +1 510 909-8353 +l: Fremont +ou: Human Resources +postalAddress: Human Resources$Fremont +telephoneNumber: +1 510 574-8221 +title: Master Human Resources Writer +userPassword: Password1 +uid: StandelT +givenName: Teresa +mail: StandelT@ns-mail4.com +carLicense: SO745H +departmentNumber: 6921 +employeeType: Employee +homePhone: +1 510 224-9306 +initials: T. S. +mobile: +1 510 767-9421 +pager: +1 510 480-4823 +roomNumber: 8868 +manager: cn=Tilly Pilote,ou=Peons,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Linn Chaintreuil,ou=Peons,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Linn Chaintreuil +sn: Chaintreuil +description: This is Linn Chaintreuil's description +facsimileTelephoneNumber: +1 408 158-6711 +l: Redmond +ou: Peons +postalAddress: Peons$Redmond +telephoneNumber: +1 408 385-8391 +title: Junior Peons Janitor +userPassword: Password1 +uid: ChaintrL +givenName: Linn +mail: ChaintrL@ns-mail4.com +carLicense: J8LFWI +departmentNumber: 9011 +employeeType: Normal +homePhone: +1 408 691-2236 +initials: L. C. +mobile: +1 408 400-5967 +pager: +1 408 572-7014 +roomNumber: 9973 +manager: cn=Tilly Pilote,ou=Peons,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Annmarie Howe-Patterson,ou=Product Testing,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Annmarie Howe-Patterson +sn: Howe-Patterson +description: This is Annmarie Howe-Patterson's description +facsimileTelephoneNumber: +1 206 563-7538 +l: Fremont +ou: Product Testing +postalAddress: Product Testing$Fremont +telephoneNumber: +1 206 870-8715 +title: Junior Product Testing Madonna +userPassword: Password1 +uid: Howe-PaA +givenName: Annmarie +mail: Howe-PaA@ns-mail9.com +carLicense: 8TG5CO +departmentNumber: 6432 +employeeType: Normal +homePhone: +1 206 644-3679 +initials: A. H. +mobile: +1 206 252-3374 +pager: +1 206 984-3849 +roomNumber: 9630 +manager: cn=Tilly Pilote,ou=Peons,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Idaline Sentner,ou=Peons,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Idaline Sentner +sn: Sentner +description: This is Idaline Sentner's description +facsimileTelephoneNumber: +1 213 603-9938 +l: Redwood Shores +ou: Peons +postalAddress: Peons$Redwood Shores +telephoneNumber: +1 213 227-4437 +title: Junior Peons Manager +userPassword: Password1 +uid: SentnerI +givenName: Idaline +mail: SentnerI@ns-mail7.com +carLicense: G1SYEB +departmentNumber: 4382 +employeeType: Contract +homePhone: +1 213 447-1918 +initials: I. S. +mobile: +1 213 530-8298 +pager: +1 213 507-7988 +roomNumber: 9144 +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Debbi Coriaty,ou=Peons,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Debbi Coriaty +sn: Coriaty +description: This is Debbi Coriaty's description +facsimileTelephoneNumber: +1 206 128-8898 +l: Palo Alto +ou: Peons +postalAddress: Peons$Palo Alto +telephoneNumber: +1 206 700-7016 +title: Supreme Peons Madonna +userPassword: Password1 +uid: CoriatyD +givenName: Debbi +mail: CoriatyD@ns-mail3.com +carLicense: HU3C3K +departmentNumber: 1018 +employeeType: Contract +homePhone: +1 206 430-4177 +initials: D. C. +mobile: +1 206 967-3108 +pager: +1 206 680-2262 +roomNumber: 8654 +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Freek Centeno,ou=Product Testing,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Freek Centeno +sn: Centeno +description: This is Freek Centeno's description +facsimileTelephoneNumber: +1 804 692-5003 +l: San Francisco +ou: Product Testing +postalAddress: Product Testing$San Francisco +telephoneNumber: +1 804 296-3073 +title: Associate Product Testing Warrior +userPassword: Password1 +uid: CentenoF +givenName: Freek +mail: CentenoF@ns-mail7.com +carLicense: KO19MC +departmentNumber: 9433 +employeeType: Normal +homePhone: +1 804 934-4523 +initials: F. C. +mobile: +1 804 920-5665 +pager: +1 804 162-5559 +roomNumber: 9852 +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Open O Karina,ou=Management,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Open O Karina +sn: O Karina +description: This is Open O Karina's description +facsimileTelephoneNumber: +1 408 356-5158 +l: Menlo Park +ou: Management +postalAddress: Management$Menlo Park +telephoneNumber: +1 408 708-3880 +title: Supreme Management Sales Rep +userPassword: Password1 +uid: O KarinO +givenName: Open +mail: O KarinO@ns-mail8.com +carLicense: 9TYDV7 +departmentNumber: 6655 +employeeType: Employee +homePhone: +1 408 789-7498 +initials: O. O. +mobile: +1 408 342-4421 +pager: +1 408 159-1587 +roomNumber: 8636 +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Rajani Ciochon,ou=Human Resources,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Rajani Ciochon +sn: Ciochon +description: This is Rajani Ciochon's description +facsimileTelephoneNumber: +1 408 395-6771 +l: Cupertino +ou: Human Resources +postalAddress: Human Resources$Cupertino +telephoneNumber: +1 408 603-7449 +title: Associate Human Resources President +userPassword: Password1 +uid: CiochonR +givenName: Rajani +mail: CiochonR@ns-mail4.com +carLicense: 2V9ICI +departmentNumber: 5209 +employeeType: Employee +homePhone: +1 408 686-6963 +initials: R. C. +mobile: +1 408 468-1043 +pager: +1 408 806-7145 +roomNumber: 9276 +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Perla Chilton,ou=Product Testing,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Perla Chilton +sn: Chilton +description: This is Perla Chilton's description +facsimileTelephoneNumber: +1 408 360-5565 +l: Fremont +ou: Product Testing +postalAddress: Product Testing$Fremont +telephoneNumber: +1 408 236-1743 +title: Associate Product Testing Consultant +userPassword: Password1 +uid: ChiltonP +givenName: Perla +mail: ChiltonP@ns-mail8.com +carLicense: AN68BN +departmentNumber: 3054 +employeeType: Contract +homePhone: +1 408 571-6456 +initials: P. C. +mobile: +1 408 574-9687 +pager: +1 408 908-1256 +roomNumber: 9168 +manager: cn=Tilly Pilote,ou=Peons,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Viviene Caruth,ou=Janitorial,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Viviene Caruth +sn: Caruth +description: This is Viviene Caruth's description +facsimileTelephoneNumber: +1 408 285-5896 +l: Sunnyvale +ou: Janitorial +postalAddress: Janitorial$Sunnyvale +telephoneNumber: +1 408 430-7660 +title: Chief Janitorial Figurehead +userPassword: Password1 +uid: CaruthV +givenName: Viviene +mail: CaruthV@ns-mail4.com +carLicense: 2WJGYG +departmentNumber: 9174 +employeeType: Normal +homePhone: +1 408 954-7152 +initials: V. C. +mobile: +1 408 795-6215 +pager: +1 408 803-2110 +roomNumber: 9951 +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Quyen Witzman,ou=Janitorial,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Quyen Witzman +sn: Witzman +description: This is Quyen Witzman's description +facsimileTelephoneNumber: +1 408 968-6635 +l: Menlo Park +ou: Janitorial +postalAddress: Janitorial$Menlo Park +telephoneNumber: +1 408 506-5945 +title: Chief Janitorial Mascot +userPassword: Password1 +uid: WitzmanQ +givenName: Quyen +mail: WitzmanQ@ns-mail5.com +carLicense: 53VMR1 +departmentNumber: 4066 +employeeType: Employee +homePhone: +1 408 602-4608 +initials: Q. W. +mobile: +1 408 541-6494 +pager: +1 408 880-8573 +roomNumber: 9964 +manager: cn=Tilly Pilote,ou=Peons,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Bevvy Xmssupport,ou=Management,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Bevvy Xmssupport +sn: Xmssupport +description: This is Bevvy Xmssupport's description +facsimileTelephoneNumber: +1 206 454-8695 +l: Armonk +ou: Management +postalAddress: Management$Armonk +telephoneNumber: +1 206 671-2556 +title: Chief Management Grunt +userPassword: Password1 +uid: XmssuppB +givenName: Bevvy +mail: XmssuppB@ns-mail9.com +carLicense: S4OOKY +departmentNumber: 1644 +employeeType: Normal +homePhone: +1 206 231-8140 +initials: B. X. +mobile: +1 206 159-4420 +pager: +1 206 947-5096 +roomNumber: 8586 +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Dulsea Norment,ou=Peons,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Dulsea Norment +sn: Norment +description: This is Dulsea Norment's description +facsimileTelephoneNumber: +1 213 576-4550 +l: Sunnyvale +ou: Peons +postalAddress: Peons$Sunnyvale +telephoneNumber: +1 213 729-4103 +title: Master Peons Dictator +userPassword: Password1 +uid: NormentD +givenName: Dulsea +mail: NormentD@ns-mail3.com +carLicense: VV4JV4 +departmentNumber: 2104 +employeeType: Contract +homePhone: +1 213 852-3212 +initials: D. N. +mobile: +1 213 749-3759 +pager: +1 213 934-6464 +roomNumber: 9412 +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Dinny Golshan,ou=Human Resources,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Dinny Golshan +sn: Golshan +description: This is Dinny Golshan's description +facsimileTelephoneNumber: +1 408 391-3392 +l: San Jose +ou: Human Resources +postalAddress: Human Resources$San Jose +telephoneNumber: +1 408 196-2264 +title: Associate Human Resources Architect +userPassword: Password1 +uid: GolshanD +givenName: Dinny +mail: GolshanD@ns-mail2.com +carLicense: N22U7C +departmentNumber: 8809 +employeeType: Normal +homePhone: +1 408 690-7616 +initials: D. G. +mobile: +1 408 699-7377 +pager: +1 408 657-3991 +roomNumber: 9134 +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Gnni Leone,ou=Janitorial,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Gnni Leone +sn: Leone +description: This is Gnni Leone's description +facsimileTelephoneNumber: +1 408 994-6226 +l: San Francisco +ou: Janitorial +postalAddress: Janitorial$San Francisco +telephoneNumber: +1 408 813-6244 +title: Associate Janitorial Technician +userPassword: Password1 +uid: LeoneG +givenName: Gnni +mail: LeoneG@ns-mail2.com +carLicense: LNVD7N +departmentNumber: 8886 +employeeType: Employee +homePhone: +1 408 574-4642 +initials: G. L. +mobile: +1 408 499-5496 +pager: +1 408 803-2909 +roomNumber: 9374 +manager: cn=Tilly Pilote,ou=Peons,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Kambhampati Hutson,ou=Peons,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Kambhampati Hutson +sn: Hutson +description: This is Kambhampati Hutson's description +facsimileTelephoneNumber: +1 408 621-9746 +l: Santa Clara +ou: Peons +postalAddress: Peons$Santa Clara +telephoneNumber: +1 408 181-1729 +title: Junior Peons Artist +userPassword: Password1 +uid: HutsonK +givenName: Kambhampati +mail: HutsonK@ns-mail3.com +carLicense: 924O92 +departmentNumber: 8427 +employeeType: Contract +homePhone: +1 408 583-4543 +initials: K. H. +mobile: +1 408 657-3416 +pager: +1 408 798-3429 +roomNumber: 9495 +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Vallier Jarchow,ou=Peons,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Vallier Jarchow +sn: Jarchow +description: This is Vallier Jarchow's description +facsimileTelephoneNumber: +1 818 662-8472 +l: Fremont +ou: Peons +postalAddress: Peons$Fremont +telephoneNumber: +1 818 511-8166 +title: Chief Peons Fellow +userPassword: Password1 +uid: JarchowV +givenName: Vallier +mail: JarchowV@ns-mail6.com +carLicense: SU0SYE +departmentNumber: 9398 +employeeType: Employee +homePhone: +1 818 369-7976 +initials: V. J. +mobile: +1 818 983-6860 +pager: +1 818 982-1894 +roomNumber: 9228 +manager: cn=Tilly Pilote,ou=Peons,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Dalila Acree,ou=Management,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Dalila Acree +sn: Acree +description: This is Dalila Acree's description +facsimileTelephoneNumber: +1 510 124-6912 +l: Cupertino +ou: Management +postalAddress: Management$Cupertino +telephoneNumber: +1 510 891-1960 +title: Supreme Management Architect +userPassword: Password1 +uid: AcreeD +givenName: Dalila +mail: AcreeD@ns-mail7.com +carLicense: HV6DTG +departmentNumber: 4869 +employeeType: Normal +homePhone: +1 510 560-5075 +initials: D. A. +mobile: +1 510 919-7847 +pager: +1 510 886-9892 +roomNumber: 9876 +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Akemi Brosselard,ou=Administrative,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Akemi Brosselard +sn: Brosselard +description: This is Akemi Brosselard's description +facsimileTelephoneNumber: +1 804 319-8418 +l: Orem +ou: Administrative +postalAddress: Administrative$Orem +telephoneNumber: +1 804 642-5991 +title: Supreme Administrative Writer +userPassword: Password1 +uid: BrosselA +givenName: Akemi +mail: BrosselA@ns-mail3.com +carLicense: 8G6K6F +departmentNumber: 5657 +employeeType: Employee +homePhone: +1 804 435-1410 +initials: A. B. +mobile: +1 804 844-6239 +pager: +1 804 983-8937 +roomNumber: 8980 +manager: cn=Tilly Pilote,ou=Peons,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Katsunori Soong,ou=Administrative,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Katsunori Soong +sn: Soong +description: This is Katsunori Soong's description +facsimileTelephoneNumber: +1 804 155-1859 +l: Armonk +ou: Administrative +postalAddress: Administrative$Armonk +telephoneNumber: +1 804 786-3820 +title: Junior Administrative Manager +userPassword: Password1 +uid: SoongK +givenName: Katsunori +mail: SoongK@ns-mail4.com +carLicense: WL605T +departmentNumber: 2495 +employeeType: Normal +homePhone: +1 804 425-3905 +initials: K. S. +mobile: +1 804 666-4430 +pager: +1 804 562-1815 +roomNumber: 8364 +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Lai Vezina,ou=Product Testing,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Lai Vezina +sn: Vezina +description: This is Lai Vezina's description +facsimileTelephoneNumber: +1 818 329-2142 +l: San Francisco +ou: Product Testing +postalAddress: Product Testing$San Francisco +telephoneNumber: +1 818 250-4180 +title: Chief Product Testing Engineer +userPassword: Password1 +uid: VezinaL +givenName: Lai +mail: VezinaL@ns-mail4.com +carLicense: NTMMT2 +departmentNumber: 2673 +employeeType: Normal +homePhone: +1 818 143-3010 +initials: L. V. +mobile: +1 818 779-1783 +pager: +1 818 816-1789 +roomNumber: 8787 +manager: cn=Tilly Pilote,ou=Peons,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Erdem Kelleher,ou=Peons,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Erdem Kelleher +sn: Kelleher +description: This is Erdem Kelleher's description +facsimileTelephoneNumber: +1 804 221-1664 +l: Palo Alto +ou: Peons +postalAddress: Peons$Palo Alto +telephoneNumber: +1 804 131-4472 +title: Junior Peons Admin +userPassword: Password1 +uid: KelleheE +givenName: Erdem +mail: KelleheE@ns-mail5.com +carLicense: 4G7QOA +departmentNumber: 8901 +employeeType: Employee +homePhone: +1 804 370-6448 +initials: E. K. +mobile: +1 804 758-6068 +pager: +1 804 638-7576 +roomNumber: 9797 +manager: cn=Tilly Pilote,ou=Peons,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Huyen Gebhart,ou=Human Resources,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Huyen Gebhart +sn: Gebhart +description: This is Huyen Gebhart's description +facsimileTelephoneNumber: +1 818 535-4968 +l: Sunnyvale +ou: Human Resources +postalAddress: Human Resources$Sunnyvale +telephoneNumber: +1 818 512-1598 +title: Associate Human Resources Sales Rep +userPassword: Password1 +uid: GebhartH +givenName: Huyen +mail: GebhartH@ns-mail7.com +carLicense: 6T55R9 +departmentNumber: 4076 +employeeType: Employee +homePhone: +1 818 457-6466 +initials: H. G. +mobile: +1 818 348-4167 +pager: +1 818 722-2918 +roomNumber: 9844 +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Wendye Deligdisch,ou=Janitorial,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Wendye Deligdisch +sn: Deligdisch +description: This is Wendye Deligdisch's description +facsimileTelephoneNumber: +1 804 722-9859 +l: Milpitas +ou: Janitorial +postalAddress: Janitorial$Milpitas +telephoneNumber: +1 804 760-1413 +title: Master Janitorial Visionary +userPassword: Password1 +uid: DeligdiW +givenName: Wendye +mail: DeligdiW@ns-mail4.com +carLicense: S877EF +departmentNumber: 9328 +employeeType: Employee +homePhone: +1 804 983-7072 +initials: W. D. +mobile: +1 804 619-9717 +pager: +1 804 426-7794 +roomNumber: 8975 +manager: cn=Tilly Pilote,ou=Peons,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Gerrard Rosko,ou=Management,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Gerrard Rosko +sn: Rosko +description: This is Gerrard Rosko's description +facsimileTelephoneNumber: +1 206 183-8561 +l: San Mateo +ou: Management +postalAddress: Management$San Mateo +telephoneNumber: +1 206 246-1483 +title: Master Management Figurehead +userPassword: Password1 +uid: RoskoG +givenName: Gerrard +mail: RoskoG@ns-mail3.com +carLicense: K96SWS +departmentNumber: 5059 +employeeType: Normal +homePhone: +1 206 444-5411 +initials: G. R. +mobile: +1 206 248-5523 +pager: +1 206 442-1788 +roomNumber: 9685 +manager: cn=Tilly Pilote,ou=Peons,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Alanah Thornber,ou=Product Development,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Alanah Thornber +sn: Thornber +description: This is Alanah Thornber's description +facsimileTelephoneNumber: +1 206 643-4551 +l: Armonk +ou: Product Development +postalAddress: Product Development$Armonk +telephoneNumber: +1 206 420-8668 +title: Associate Product Development Pinhead +userPassword: Password1 +uid: ThornbeA +givenName: Alanah +mail: ThornbeA@ns-mail5.com +carLicense: 0N330J +departmentNumber: 2333 +employeeType: Employee +homePhone: +1 206 864-8920 +initials: A. T. +mobile: +1 206 629-1327 +pager: +1 206 959-6612 +roomNumber: 8068 +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Peggie Environment,ou=Product Development,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Peggie Environment +sn: Environment +description: This is Peggie Environment's description +facsimileTelephoneNumber: +1 415 742-7965 +l: Milpitas +ou: Product Development +postalAddress: Product Development$Milpitas +telephoneNumber: +1 415 464-8729 +title: Master Product Development Architect +userPassword: Password1 +uid: EnvironP +givenName: Peggie +mail: EnvironP@ns-mail8.com +carLicense: E1X5Y4 +departmentNumber: 7482 +employeeType: Normal +homePhone: +1 415 922-9682 +initials: P. E. +mobile: +1 415 361-2591 +pager: +1 415 773-5529 +roomNumber: 9573 +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Darcey McMahon,ou=Janitorial,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Darcey McMahon +sn: McMahon +description: This is Darcey McMahon's description +facsimileTelephoneNumber: +1 510 767-7263 +l: Palo Alto +ou: Janitorial +postalAddress: Janitorial$Palo Alto +telephoneNumber: +1 510 403-4704 +title: Master Janitorial Fellow +userPassword: Password1 +uid: McMahonD +givenName: Darcey +mail: McMahonD@ns-mail4.com +carLicense: XKRJSW +departmentNumber: 3382 +employeeType: Employee +homePhone: +1 510 944-1203 +initials: D. M. +mobile: +1 510 438-2676 +pager: +1 510 375-9906 +roomNumber: 8696 +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Sallyanne Efland,ou=Administrative,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Sallyanne Efland +sn: Efland +description: This is Sallyanne Efland's description +facsimileTelephoneNumber: +1 818 696-2230 +l: San Jose +ou: Administrative +postalAddress: Administrative$San Jose +telephoneNumber: +1 818 536-5370 +title: Supreme Administrative Janitor +userPassword: Password1 +uid: EflandS +givenName: Sallyanne +mail: EflandS@ns-mail9.com +carLicense: A3DRW4 +departmentNumber: 2649 +employeeType: Normal +homePhone: +1 818 923-4868 +initials: S. E. +mobile: +1 818 963-2769 +pager: +1 818 623-2612 +roomNumber: 8464 +manager: cn=Tilly Pilote,ou=Peons,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Ginevra Adamson,ou=Management,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Ginevra Adamson +sn: Adamson +description: This is Ginevra Adamson's description +facsimileTelephoneNumber: +1 408 351-9104 +l: Menlo Park +ou: Management +postalAddress: Management$Menlo Park +telephoneNumber: +1 408 367-8946 +title: Chief Management Dictator +userPassword: Password1 +uid: AdamsonG +givenName: Ginevra +mail: AdamsonG@ns-mail2.com +carLicense: R9T589 +departmentNumber: 1042 +employeeType: Employee +homePhone: +1 408 567-7901 +initials: G. A. +mobile: +1 408 269-6573 +pager: +1 408 196-2973 +roomNumber: 8699 +manager: cn=Tilly Pilote,ou=Peons,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Oralia Winlow,ou=Peons,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Oralia Winlow +sn: Winlow +description: This is Oralia Winlow's description +facsimileTelephoneNumber: +1 818 498-6469 +l: Milpitas +ou: Peons +postalAddress: Peons$Milpitas +telephoneNumber: +1 818 363-5209 +title: Chief Peons Madonna +userPassword: Password1 +uid: WinlowO +givenName: Oralia +mail: WinlowO@ns-mail4.com +carLicense: X8N9Y4 +departmentNumber: 1787 +employeeType: Employee +homePhone: +1 818 733-3917 +initials: O. W. +mobile: +1 818 472-3442 +pager: +1 818 842-7463 +roomNumber: 9394 +manager: cn=Tilly Pilote,ou=Peons,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Syyed O Karina,ou=Management,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Syyed O Karina +sn: O Karina +description: This is Syyed O Karina's description +facsimileTelephoneNumber: +1 206 685-4339 +l: Redmond +ou: Management +postalAddress: Management$Redmond +telephoneNumber: +1 206 165-3305 +title: Junior Management Stooge +userPassword: Password1 +uid: O KarinS +givenName: Syyed +mail: O KarinS@ns-mail5.com +carLicense: 79HD2M +departmentNumber: 5833 +employeeType: Contract +homePhone: +1 206 359-4285 +initials: S. O. +mobile: +1 206 300-5317 +pager: +1 206 776-7090 +roomNumber: 8148 +manager: cn=Tilly Pilote,ou=Peons,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Kazuo Rintoul,ou=Administrative,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Kazuo Rintoul +sn: Rintoul +description: This is Kazuo Rintoul's description +facsimileTelephoneNumber: +1 510 457-4969 +l: San Mateo +ou: Administrative +postalAddress: Administrative$San Mateo +telephoneNumber: +1 510 687-8512 +title: Supreme Administrative Engineer +userPassword: Password1 +uid: RintoulK +givenName: Kazuo +mail: RintoulK@ns-mail2.com +carLicense: H4ALP9 +departmentNumber: 4549 +employeeType: Contract +homePhone: +1 510 881-3861 +initials: K. R. +mobile: +1 510 771-5475 +pager: +1 510 238-3485 +roomNumber: 9965 +manager: cn=Nashir Shiffer,ou=Management,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Kaile Klingsporn,ou=Human Resources,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Kaile Klingsporn +sn: Klingsporn +description: This is Kaile Klingsporn's description +facsimileTelephoneNumber: +1 415 324-2082 +l: Fremont +ou: Human Resources +postalAddress: Human Resources$Fremont +telephoneNumber: +1 415 639-4326 +title: Supreme Human Resources Sales Rep +userPassword: Password1 +uid: KlingspK +givenName: Kaile +mail: KlingspK@ns-mail3.com +carLicense: 3I7UOR +departmentNumber: 7954 +employeeType: Contract +homePhone: +1 415 607-7030 +initials: K. K. +mobile: +1 415 947-1280 +pager: +1 415 876-4511 +roomNumber: 8027 +manager: cn=Tilly Pilote,ou=Peons,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Ext Askins,ou=Janitorial,dc=corp,dc=acme,dc=local +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Ext Askins +sn: Askins +description: This is Ext Askins's description +facsimileTelephoneNumber: +1 415 741-9451 +l: San Mateo +ou: Janitorial +postalAddress: Janitorial$San Mateo +telephoneNumber: +1 415 481-4962 +title: Supreme Janitorial Figurehead +userPassword: Password1 +uid: AskinsE +givenName: Ext +mail: AskinsE@ns-mail4.com +carLicense: YIJATP +departmentNumber: 4834 +employeeType: Normal +homePhone: +1 415 820-1417 +initials: E. A. +mobile: +1 415 665-9922 +pager: +1 415 381-9637 +roomNumber: 9532 +manager: cn=Tilly Pilote,ou=Peons,dc=corp,dc=acme,dc=local +secretary: cn=Evvy Gattrell,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Accountants,ou=Accounting,dc=corp,dc=acme,dc=local +changetype: add +objectclass: top +objectclass: groupofnames +cn: Accountants +member: uid=LaurentiusA,ou=Accounting,dc=corp,dc=acme,dc=local + +dn: cn=Developers,ou=Product Development,dc=corp,dc=acme,dc=local +changetype: add +objectclass: top +objectclass: groupofnames +cn: Developers +member: uid=ThornbeA,ou=Product Development,dc=corp,dc=acme,dc=local +member: uid=MattiusG,ou=Product Development,dc=corp,dc=acme,dc=local +member: uid=RuddleJ,ou=Product Development,dc=corp,dc=acme,dc=local +member: uid=EnvironP,ou=Product Development,dc=corp,dc=acme,dc=local +member: uid=Van VeeY,ou=Product Development,dc=corp,dc=acme,dc=local + +dn: cn=Testers,ou=Product Testing,dc=corp,dc=acme,dc=local +changetype: add +objectclass: top +objectclass: groupofnames +cn: Testers +member: uid=BasmadjC,ou=Product Testing,dc=corp,dc=acme,dc=local +member: uid=CuthberA,ou=Product Testing,dc=corp,dc=acme,dc=local +member: uid=Howe-PaA,ou=Product Testing,dc=corp,dc=acme,dc=local +member: uid=ReddingC,ou=Product Testing,dc=corp,dc=acme,dc=local +member: uid=HoaglanF,ou=Product Testing,dc=corp,dc=acme,dc=local +member: uid=CentenoF,ou=Product Testing,dc=corp,dc=acme,dc=local +member: uid=BlumenfH,ou=Product Testing,dc=corp,dc=acme,dc=local +member: uid=VezinaL,ou=Product Testing,dc=corp,dc=acme,dc=local +member: uid=SinananM,ou=Product Testing,dc=corp,dc=acme,dc=local +member: uid=RosserO,ou=Product Testing,dc=corp,dc=acme,dc=local +member: uid=ChiltonP,ou=Product Testing,dc=corp,dc=acme,dc=local +member: uid=PrestonR,ou=Product Testing,dc=corp,dc=acme,dc=local +member: uid=PiaseckW,ou=Product Testing,dc=corp,dc=acme,dc=local +member: uid=BlesiW,ou=Product Testing,dc=corp,dc=acme,dc=local +member: uid=ENGY,ou=Product Testing,dc=corp,dc=acme,dc=local + +dn: cn=Human Resources,ou=Human Resources,dc=corp,dc=acme,dc=local +changetype: add +objectclass: top +objectclass: groupofnames +cn: Human Resources +member: uid=MejiaD,ou=Human Resources,dc=corp,dc=acme,dc=local +member: uid=GolshanD,ou=Human Resources,dc=corp,dc=acme,dc=local +member: uid=MorelliH,ou=Human Resources,dc=corp,dc=acme,dc=local +member: uid=GebhartH,ou=Human Resources,dc=corp,dc=acme,dc=local +member: uid=KlingspK,ou=Human Resources,dc=corp,dc=acme,dc=local +member: uid=KlebschL,ou=Human Resources,dc=corp,dc=acme,dc=local +member: uid=ChauhanM,ou=Human Resources,dc=corp,dc=acme,dc=local +member: uid=CiochonR,ou=Human Resources,dc=corp,dc=acme,dc=local +member: uid=GohR,ou=Human Resources,dc=corp,dc=acme,dc=local +member: uid=StandelT,ou=Human Resources,dc=corp,dc=acme,dc=local +member: uid=RegimbaW,ou=Human Resources,dc=corp,dc=acme,dc=local + +dn: cn=Payroll,ou=Payroll,dc=corp,dc=acme,dc=local +changetype: add +objectclass: top +objectclass: groupofnames +cn: Payroll +member: uid=CharneyR,ou=Payroll,dc=corp,dc=acme,dc=local +member: uid=HoehnF,ou=Payroll,dc=corp,dc=acme,dc=local +member: uid=SmulderG,ou=Payroll,dc=corp,dc=acme,dc=local +member: uid=PereiraG,ou=Payroll,dc=corp,dc=acme,dc=local +member: uid=WortmanJ,ou=Payroll,dc=corp,dc=acme,dc=local +member: uid=LoughJ,ou=Payroll,dc=corp,dc=acme,dc=local +member: uid=BittenbL,ou=Payroll,dc=corp,dc=acme,dc=local + +dn: cn=Janitorial,ou=Janitorial,dc=corp,dc=acme,dc=local +changetype: add +objectclass: top +objectclass: groupofnames +cn: Janitorial +member: uid=FleugelR,ou=Janitorial,dc=corp,dc=acme,dc=local +member: uid=NahmiasA,ou=Janitorial,dc=corp,dc=acme,dc=local +member: uid=KakutaA,ou=Janitorial,dc=corp,dc=acme,dc=local +member: uid=QuinnA,ou=Janitorial,dc=corp,dc=acme,dc=local +member: uid=McMahonD,ou=Janitorial,dc=corp,dc=acme,dc=local +member: uid=AskinsE,ou=Janitorial,dc=corp,dc=acme,dc=local +member: uid=StachowF,ou=Janitorial,dc=corp,dc=acme,dc=local +member: uid=LeoneG,ou=Janitorial,dc=corp,dc=acme,dc=local +member: uid=JankowsG,ou=Janitorial,dc=corp,dc=acme,dc=local +member: uid=TibiL,ou=Janitorial,dc=corp,dc=acme,dc=local +member: uid=SchefflL,ou=Janitorial,dc=corp,dc=acme,dc=local +member: uid=LinderP,ou=Janitorial,dc=corp,dc=acme,dc=local +member: uid=MacLeanP,ou=Janitorial,dc=corp,dc=acme,dc=local +member: uid=WitzmanQ,ou=Janitorial,dc=corp,dc=acme,dc=local +member: uid=ButtreyS,ou=Janitorial,dc=corp,dc=acme,dc=local +member: uid=FrederiS,ou=Janitorial,dc=corp,dc=acme,dc=local +member: uid=VodickaV,ou=Janitorial,dc=corp,dc=acme,dc=local +member: uid=CaruthV,ou=Janitorial,dc=corp,dc=acme,dc=local +member: uid=DeligdiW,ou=Janitorial,dc=corp,dc=acme,dc=local + +dn: cn=Management,ou=Management,dc=corp,dc=acme,dc=local +changetype: add +objectclass: top +objectclass: groupofnames +cn: Management +member: uid=GattrelE,ou=Management,dc=corp,dc=acme,dc=local +member: uid=BoucourA,ou=Management,dc=corp,dc=acme,dc=local +member: uid=XmssuppB,ou=Management,dc=corp,dc=acme,dc=local +member: uid=OmanC,ou=Management,dc=corp,dc=acme,dc=local +member: uid=AcreeD,ou=Management,dc=corp,dc=acme,dc=local +member: uid=MichaloF,ou=Management,dc=corp,dc=acme,dc=local +member: uid=PetrickG,ou=Management,dc=corp,dc=acme,dc=local +member: uid=RoskoG,ou=Management,dc=corp,dc=acme,dc=local +member: uid=AdamsonG,ou=Management,dc=corp,dc=acme,dc=local +member: uid=AdamH,ou=Management,dc=corp,dc=acme,dc=local +member: uid=HaganH,ou=Management,dc=corp,dc=acme,dc=local +member: uid=ThibeauM,ou=Management,dc=corp,dc=acme,dc=local +member: uid=ShifferN,ou=Management,dc=corp,dc=acme,dc=local +member: uid=McElligS,ou=Management,dc=corp,dc=acme,dc=local +member: uid=EverittT,ou=Management,dc=corp,dc=acme,dc=local + +dn: cn=Administrative,ou=Administrative,dc=corp,dc=acme,dc=local +changetype: add +objectclass: top +objectclass: groupofnames +cn: Administrative +member: uid=FujiwarK,ou=Administrative,dc=corp,dc=acme,dc=local +member: uid=BrosselA,ou=Administrative,dc=corp,dc=acme,dc=local +member: uid=ParhamA,ou=Administrative,dc=corp,dc=acme,dc=local +member: uid=HitchcoG,ou=Administrative,dc=corp,dc=acme,dc=local +member: uid=SoongK,ou=Administrative,dc=corp,dc=acme,dc=local +member: uid=RintoulK,ou=Administrative,dc=corp,dc=acme,dc=local +member: uid=KesslerL,ou=Administrative,dc=corp,dc=acme,dc=local +member: uid=LeClairL,ou=Administrative,dc=corp,dc=acme,dc=local +member: uid=RozumnaN,ou=Administrative,dc=corp,dc=acme,dc=local +member: uid=MarouchP,ou=Administrative,dc=corp,dc=acme,dc=local +member: uid=IEMR,ou=Administrative,dc=corp,dc=acme,dc=local +member: uid=EflandS,ou=Administrative,dc=corp,dc=acme,dc=local + +dn: cn=Peons,ou=Peons,dc=corp,dc=acme,dc=local +changetype: add +objectclass: top +objectclass: groupofnames +cn: Peons +member: uid=PiloteT,ou=Peons,dc=corp,dc=acme,dc=local +member: uid=ErkelA,ou=Peons,dc=corp,dc=acme,dc=local +member: uid=CoverdaC,ou=Peons,dc=corp,dc=acme,dc=local +member: uid=CoriatyD,ou=Peons,dc=corp,dc=acme,dc=local +member: uid=NormentD,ou=Peons,dc=corp,dc=acme,dc=local +member: uid=KelleheE,ou=Peons,dc=corp,dc=acme,dc=local +member: uid=SentnerI,ou=Peons,dc=corp,dc=acme,dc=local +member: uid=RouthieI,ou=Peons,dc=corp,dc=acme,dc=local +member: uid=HiltonJ,ou=Peons,dc=corp,dc=acme,dc=local +member: uid=HutsonK,ou=Peons,dc=corp,dc=acme,dc=local +member: uid=BelcherK,ou=Peons,dc=corp,dc=acme,dc=local +member: uid=ChaintrL,ou=Peons,dc=corp,dc=acme,dc=local +member: uid=WinlowO,ou=Peons,dc=corp,dc=acme,dc=local +member: uid=JarchowV,ou=Peons,dc=corp,dc=acme,dc=local + +dn: cn=Planning,ou=Planning,dc=corp,dc=acme,dc=local +changetype: add +objectclass: top +objectclass: groupofnames +cn: Planning +member: uid=LaurentiusW,ou=Planning,dc=corp,dc=acme,dc=local diff --git a/config/mock/confluence-openapi.yaml b/config/mock/confluence-openapi.yaml new file mode 100644 index 0000000..7610cd2 --- /dev/null +++ b/config/mock/confluence-openapi.yaml @@ -0,0 +1,284 @@ +openapi: 3.0.1 +info: + title: The Confluence REST API + description: This document describes the REST API and resources provided by Confluence. The REST APIs are for developers who want to integrate Confluence into their application and for administrators who want to script interactions with the Confluence server.Confluence's REST APIs provide access to resources (data entities) via URI paths. To use a REST API, your application will make an HTTP request and parse the response. The response format is JSON. Your methods will be the standard HTTP methods like GET, PUT, POST and DELETE. Because the REST API is based on open standards, you can use any web development language to access the API. + termsOfService: https://atlassian.com/terms/ + version: 1.0.0 +externalDocs: + description: The online and complete version of the Confluence REST API docs. + url: https://developer.atlassian.com/server/confluence/confluence-server-rest-api/ +servers: + - url: http://your-confluence-server/confluence +security: + - bearerAuth: [] + +paths: + /rest/api/content/{id}/child: + get: + summary: Get content children + description: |- + Returns a map of the direct children of a piece of content. A piece of content + has different types of child content, depending on its type. These are + the default parent-child content type relationships: + + - `page`: child content is `page`, `comment`, `attachment` + + Apps can override these default relationships. Apps can also introduce + new content types that create new parent-child content relationships. + + Note, the map will always include all child content types that are valid + for the content. However, if the content has no instances of a child content + type, the map will contain an empty array for that child content type. + + **[Permissions](https://confluence.atlassian.com/x/_AozKw) required**: 'View' permission for the space, + and permission to view the content if it is a page. + operationId: getContentChildren + parameters: + - name: id + in: path + description: The ID of the content to be queried for its children. + required: true + schema: + type: string + - name: expand + in: query + description: |- + A multi-value parameter indicating which properties of the children to expand, where: + + - `page` returns all child pages of the content. + style: form + explode: false + schema: + type: array + items: + type: string + responses: + '200': + description: Returned if the requested content children are returned. + content: + application/json: + schema: + $ref: '#/components/schemas/ContentChildren' + example: + page: + results: + - id: '1' + title: Page 1 + children: + page: + results: + - id: '11' + title: Page 1.1 + children: + page: + results: [] + _links: + tinyui: /x/Pcz-B11 + - id: '12' + title: Page 1.2 + children: + page: + results: [] + _links: + tinyui: /x/Pcz-B12 + _links: + tinyui: /x/Pcz-B1 + - id: '2' + title: Page 2 + children: + page: + results: [] + _links: + tinyui: /x/Pcz-B2 + '404': + description: |- + Returned if; + + - There is no content with the given ID. + - The calling user does not have permission to view the content. + content: {} + /rest/masterdetail/1.0/detailssummary/lines: + get: + summary: Get page properties master detail summary lines + operationId: getDetailsSummaryLines + parameters: + - name: spaceKey + in: query + required: true + schema: + type: string + - name: cql + in: query + required: true + schema: + type: string + - name: headings + in: query + required: true + schema: + type: string + - name: pageIndex + in: query + schema: + type: string + - name: pageSize + in: query + schema: + type: string + responses: + '200': + description: Returned if the requested details summary lines are returned. + content: + application/json: + schema: + $ref: '#/components/schemas/DetailsSummaryLines' + example: + currentPage: 0 + totalPages: 1 + renderedHeadings: + - Team + detailLines: + - id: 11 + title: Team 1 + details: + -
NamePositionFunctionLocation
John DooManagerProduct OwnerAnywhere
Doo, Jim AssociateDeveloperNowhere
Jane DooAnalystDeveloperHere
+ - id: 12 + title: Team 2 + details: + -
NamePositionFunctionLocation
Jane DooManagerProduct OwnerHere
Doo, John AssociateDeveloperNowhere
Jim DooAnalystDeveloperAnywhere
+ + +components: + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + schemas: + Content: + required: + - status + - type + nullable: true + type: object + additionalProperties: true + properties: + id: + type: string + type: + type: string + description: Can be "page", "blogpost", "attachment" or "content" + status: + type: string + title: + type: string + ancestors: + nullable: true + type: array + items: + $ref: '#/components/schemas/Content' + children: + $ref: '#/components/schemas/ContentChildren' + descendants: + $ref: '#/components/schemas/ContentChildren' + extensions: + type: object + _expandable: + type: object + properties: + childTypes: + type: string + children: + type: string + ancestors: + type: string + version: + type: string + descendants: + type: string + _links: + $ref: '#/components/schemas/GenericLinks' + description: Base object for all content types. + ContentArray: + required: + - _links + - results + - size + type: object + properties: + results: + type: array + items: + $ref: '#/components/schemas/Content' + start: + type: integer + format: int32 + limit: + type: integer + format: int32 + size: + type: integer + format: int32 + _links: + $ref: '#/components/schemas/GenericLinks' + ContentChildren: + type: object + additionalProperties: true + properties: + attachment: + $ref: '#/components/schemas/ContentArray' + comment: + $ref: '#/components/schemas/ContentArray' + page: + $ref: '#/components/schemas/ContentArray' + _expandable: + type: object + additionalProperties: true + properties: + attachment: + type: string + comment: + type: string + page: + type: string + _links: + $ref: '#/components/schemas/GenericLinks' + GenericLinks: + type: object + additionalProperties: + oneOf: + - type: object + additionalProperties: true + - type: string + DetailsSummaryLines: + type: object + properties: + currentPage: + type: integer + format: int32 + totalPages: + type: integer + format: int32 + renderedHeadings: + type: array + items: + type: string + detailLines: + type: array + items: + $ref: '#/components/schemas/DetailsSummaryLine' + DetailsSummaryLine: + type: object + properties: + id: + type: integer + format: int64 + title: + type: string + relativeLink: + type: string + details: + type: array + items: + type: string + additionalProperties: true diff --git a/config/mock/initializerJson.json b/config/mock/initializerJson.json new file mode 100644 index 0000000..41ea9b8 --- /dev/null +++ b/config/mock/initializerJson.json @@ -0,0 +1,3 @@ +{ + "specUrlOrPayload": "/config/confluence-openapi.yaml" +} diff --git a/config/realms/acme.yaml b/config/realms/acme.yaml new file mode 100644 index 0000000..ff303d1 --- /dev/null +++ b/config/realms/acme.yaml @@ -0,0 +1,78 @@ +realm: acme +enabled: true +displayName: ACME +resetPasswordAllowed: true + +components: + org.keycloak.storage.UserStorageProvider: + - name: ACME LDAP + providerId: ldap + subComponents: + org.keycloak.storage.ldap.mappers.LDAPStorageMapper: + - name: "first name" + providerId: user-attribute-ldap-mapper + subComponents: {} + config: + ldap.attribute: ["givenName"] + user.model.attribute: ["firstName"] +# - name: "LDAP Group Mapper" +# providerId: group-ldap-mapper +# subComponents: {} +# config: +# membership.attribute.type: ["DN"] +# group.name.ldap.attribute: ["cn"] +# preserve.group.inheritance: ["false"] +# membership.user.ldap.attribute: ["uid"] +# groups.dn: ["$(env:LDAP_GROUP_DN:-dc=corp,dc=acme,dc=local)"] +# mode: ["READ_ONLY"] +# user.roles.retrieve.strategy: ["LOAD_GROUPS_BY_MEMBER_ATTRIBUTE"] +# ignore.missing.groups: ["true"] +# membership.ldap.attribute: ["member"] +# group.object.classes: ["groupOfNames"] +# memberof.ldap.attribute: ["memberOf"] +# groups.path: ["/"] +# drop.non.existing.groups.during.sync: ["false"] + - name: "Confluence Group Mapper" + providerId: confluence-group-ldap-mapper + subComponents: {} + config: + confluenceContent.baseUrl: ["$(env:CONFLUENCE_URL:-confluence-url)"] + confluenceContent.authToken: ["token"] + confluenceContent.parentPageId: [ "123"] + confluenceContent.pageNesting: ["4"] + confluenceContent.spaceKey: ["TEST"] + confluenceContent.pageLabels: ["label1, label2, label3"] + confluenceContent.pagePropertyName: ["Team"] + confluenceContent.memberColumnIndex: ["1"] + confluenceGroups.dropNonExistingGroups: ["true"] + confluenceGroups.groupsPath: ["/"] + config: + enabled: ["true"] + pagination: ["true"] + fullSyncPeriod: ["-1"] + searchScope: ["2"] + useTruststoreSpi: ["ldapsOnly"] + usersDn: ["$(env:LDAP_USERS_DN:-dc=corp,dc=acme,dc=local)"] + maxLifespan: ["3600000"] + connectionPooling: ["true"] + cachePolicy: ["NO_CACHE"] + priority: ["0"] + importEnabled: ["true"] + useKerberosForPasswordAuthentication: ["false"] + usePasswordModifyExtendedOp: ["true"] + trustEmail: ["false"] + userObjectClasses: ["inetOrgPerson, organizationalPerson"] + bindDn: ["$(env:LDAP_USER:-ldap_user)"] + usernameLDAPAttribute: ["uid"] + changedSyncPeriod: ["-1"] + bindCredential: ["$(env:LDAP_PASSWORD:-ldap_password)"] + rdnLDAPAttribute: ["uid"] + vendor: ["other"] + editMode: ["READ_ONLY"] + uuidLDAPAttribute: ["entryUUID"] + connectionUrl: ["$(env:LDAP_URL:-ldap://localhost:389)"] + syncRegistrations: ["false"] + authType: ["simple"] + batchSizeForSync: ["1000"] + changedSyncEnabled: ["false"] + validatePasswordPolicy: ["false"] diff --git a/config/realms/master.yaml b/config/realms/master.yaml new file mode 100644 index 0000000..482c59d --- /dev/null +++ b/config/realms/master.yaml @@ -0,0 +1,3 @@ +realm: master +enabled: true +adminTheme: "keycloak.v2" diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..6cf686c --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,61 @@ +version: "3.8" +services: + keycloak: + image: keycloak/keycloak:23.0.7 + env_file: + - ./config/global.env + command: [ "--verbose", "start-dev" ] + environment: + DEBUG: "true" + DEBUG_PORT: "*:8787" + KC_LOG_LEVEL: info,org.vaulttec:DEBUG + ports: + - "8080:8080" + - "8787:8787" + volumes: + - ./target/confluence-ldap-group-mapper.jar:/opt/keycloak/providers/confluence-ldap-group-mapper.jar:z + extra_hosts: + - "host.docker.internal:host-gateway" + depends_on: + - openldap + - mockserver + + keycloak-provisioning: + image: adorsys/keycloak-config-cli:5.10.0-23.0.1 + env_file: + - ./config/global.env + volumes: + - ./config/realms/:/config/:z + depends_on: + - keycloak + + openldap: + image: osixia/openldap:1.5.0 + env_file: + - ./config/global.env + command: [ "--copy-service", "--loglevel", "warning" ] + ports: + - "1389:389" + - "1636:636" + volumes: + - ./config/ldap/acme.ldif:/tmp/ldif/acme.ldif:z + + phpldapadmin: + image: osixia/phpldapadmin:latest + environment: + PHPLDAPADMIN_LDAP_HOSTS: "openldap" + PHPLDAPADMIN_HTTPS: "false" + command: [ "--loglevel", "warning" ] + ports: + - "17080:80" + depends_on: + - openldap + + mockserver: + image: mockserver/mockserver:5.15.0 + env_file: + - ./config/global.env + ports: + - "1080:1080" + volumes: + - ./config/mock/:/config/:z diff --git a/mvnw b/mvnw new file mode 100755 index 0000000..8d937f4 --- /dev/null +++ b/mvnw @@ -0,0 +1,308 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, version 3.2.0 +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /usr/local/etc/mavenrc ] ; then + . /usr/local/etc/mavenrc + fi + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "$(uname)" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME + else + JAVA_HOME="/Library/Java/Home"; export JAVA_HOME + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=$(java-config --jre-home) + fi +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$JAVA_HOME" ] && + JAVA_HOME=$(cygpath --unix "$JAVA_HOME") + [ -n "$CLASSPATH" ] && + CLASSPATH=$(cygpath --path --unix "$CLASSPATH") +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] && + JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="$(which javac)" + if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=$(which readlink) + if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then + if $darwin ; then + javaHome="$(dirname "\"$javaExecutable\"")" + javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac" + else + javaExecutable="$(readlink -f "\"$javaExecutable\"")" + fi + javaHome="$(dirname "\"$javaExecutable\"")" + javaHome=$(expr "$javaHome" : '\(.*\)/bin') + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=$(cd "$wdir/.." || exit 1; pwd) + fi + # end of workaround + done + printf '%s' "$(cd "$basedir" || exit 1; pwd)" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + # Remove \r in case we run on Windows within Git Bash + # and check out the repository with auto CRLF management + # enabled. Otherwise, we may read lines that are delimited with + # \r\n and produce $'-Xarg\r' rather than -Xarg due to word + # splitting rules. + tr -s '\r\n' ' ' < "$1" + fi +} + +log() { + if [ "$MVNW_VERBOSE" = true ]; then + printf '%s\n' "$1" + fi +} + +BASE_DIR=$(find_maven_basedir "$(dirname "$0")") +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR +log "$MAVEN_PROJECTBASEDIR" + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" +if [ -r "$wrapperJarPath" ]; then + log "Found $wrapperJarPath" +else + log "Couldn't find $wrapperJarPath, downloading it ..." + + if [ -n "$MVNW_REPOURL" ]; then + wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + else + wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + fi + while IFS="=" read -r key value; do + # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' ) + safeValue=$(echo "$value" | tr -d '\r') + case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;; + esac + done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" + log "Downloading from: $wrapperUrl" + + if $cygwin; then + wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath") + fi + + if command -v wget > /dev/null; then + log "Found wget ... using wget" + [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + else + wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + log "Found curl ... using curl" + [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" + else + curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" + fi + else + log "Falling back to using Java to download" + javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java" + javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaSource=$(cygpath --path --windows "$javaSource") + javaClass=$(cygpath --path --windows "$javaClass") + fi + if [ -e "$javaSource" ]; then + if [ ! -e "$javaClass" ]; then + log " - Compiling MavenWrapperDownloader.java ..." + ("$JAVA_HOME/bin/javac" "$javaSource") + fi + if [ -e "$javaClass" ]; then + log " - Running MavenWrapperDownloader.java ..." + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath" + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +# If specified, validate the SHA-256 sum of the Maven wrapper jar file +wrapperSha256Sum="" +while IFS="=" read -r key value; do + case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;; + esac +done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" +if [ -n "$wrapperSha256Sum" ]; then + wrapperSha256Result=false + if command -v sha256sum > /dev/null; then + if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then + wrapperSha256Result=true + fi + elif command -v shasum > /dev/null; then + if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then + wrapperSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." + echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties." + exit 1 + fi + if [ $wrapperSha256Result = false ]; then + echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2 + echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2 + echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2 + exit 1 + fi +fi + +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$JAVA_HOME" ] && + JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME") + [ -n "$CLASSPATH" ] && + CLASSPATH=$(cygpath --path --windows "$CLASSPATH") + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR") +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +# shellcheck disable=SC2086 # safe args +exec "$JAVACMD" \ + $MAVEN_OPTS \ + $MAVEN_DEBUG_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/mvnw.cmd b/mvnw.cmd new file mode 100644 index 0000000..c4586b5 --- /dev/null +++ b/mvnw.cmd @@ -0,0 +1,205 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Apache Maven Wrapper startup batch script, version 3.2.0 +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* +if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %WRAPPER_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file +SET WRAPPER_SHA_256_SUM="" +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B +) +IF NOT %WRAPPER_SHA_256_SUM%=="" ( + powershell -Command "&{"^ + "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^ + "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^ + " Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^ + " Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^ + " Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^ + " exit 1;"^ + "}"^ + "}" + if ERRORLEVEL 1 goto error +) + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% ^ + %JVM_CONFIG_MAVEN_PROPS% ^ + %MAVEN_OPTS% ^ + %MAVEN_DEBUG_OPTS% ^ + -classpath %WRAPPER_JAR% ^ + "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ + %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" +if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%"=="on" pause + +if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% + +cmd /C exit /B %ERROR_CODE% diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..8435201 --- /dev/null +++ b/pom.xml @@ -0,0 +1,232 @@ + + + 4.0.0 + + org.vaulttec.keycloak + confluence-ldap-group-mapper + 1.0-SNAPSHOT + Keycloak Confluence LDAP Group Mapper + Custom Keycloak LDAP Group Mapper which creates groups and group memberships from Confluence pages and page properties + 2024 + https://github.com/vaulttec/keycloak-confluence-ldap-group-mapper + + + + APACHE 2 + http://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + + + UTF-8 + 17 + 3.12.1 + 3.2.4 + 3.2.5 + 3.2.5 + 24.0.1 + 1.17.2 + 5.10.2 + + 5.11.0 + 5.15.0 + 2.0.12 + 1.5.3 + 1.19.7 + 3.3.0 + 5.4.0 + 3.3.0 + + + + github + https://github.com/vaulttec/keycloak-confluence-ldap-group-mapper/issues + + + + github-actions + https://github.com/vaulttec/keycloak-confluence-ldap-group-mapper/actions + + + + scm:git:https://github.com/vaulttec/keycloak-confluence-ldap-group-mapper.git + scm:git:git@github.com:vaulttec/keycloak-confluence-ldap-group-mapper.git + https://github.com/vaulttec/keycloak-confluence-ldap-group-mapper + HEAD + + + + + tjuerge + Torsten Juergeleit + Vaulttec + + + + + Vaulttec + http://vaulttec.org + + + + + org.keycloak + keycloak-ldap-federation + ${keycloak.version} + provided + + + org.jsoup + jsoup + ${jsoup.version} + + + + org.junit.jupiter + junit-jupiter + ${junit.version} + test + + + org.testcontainers + junit-jupiter + ${testcontainers.version} + test + + + com.github.dasniko + testcontainers-keycloak + ${testcontainers-keycloak.version} + test + + + org.mockito + mockito-core + ${mockito.version} + test + + + org.mock-server + mockserver-netty + ${mock-server.version} + test + + + org.mock-server + mockserver-junit-jupiter + ${mock-server.version} + test + + + org.mock-server + mockserver-client-java + ${mock-server.version} + test + + + io.rest-assured + rest-assured + ${rest-assured.version} + test + + + org.slf4j + jcl-over-slf4j + ${slf4j.version} + test + + + org.slf4j + log4j-over-slf4j + ${slf4j.version} + test + + + ch.qos.logback + logback-classic + ${logback.version} + test + + + org.jboss.shrinkwrap.resolver + shrinkwrap-resolver-impl-maven-archive + ${shrinkwrap-resolver.version} + test + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler.version} + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire.version} + + + -XX:+EnableDynamicAgentLoading + + + + org.apache.maven.plugins + maven-failsafe-plugin + ${maven-failsafe.version} + + + -XX:+EnableDynamicAgentLoading + + + + + integration-test + verify + + + + + + org.apache.maven.plugins + maven-shade-plugin + ${maven-shade.version} + + + + org.keycloak* + + + + + + package + + shade + + + + + + + + ${project.artifactId} + + + org.apache.maven.plugins + maven-failsafe-plugin + + + org.apache.maven.plugins + maven-shade-plugin + + + + + diff --git a/src/main/java/org/vaulttec/keycloak/ldap/mappers/confluence/ConfluenceGroupLDAPStorageMapper.java b/src/main/java/org/vaulttec/keycloak/ldap/mappers/confluence/ConfluenceGroupLDAPStorageMapper.java new file mode 100644 index 0000000..b4e890e --- /dev/null +++ b/src/main/java/org/vaulttec/keycloak/ldap/mappers/confluence/ConfluenceGroupLDAPStorageMapper.java @@ -0,0 +1,245 @@ +package org.vaulttec.keycloak.ldap.mappers.confluence; + +import org.jboss.logging.Logger; +import org.keycloak.component.ComponentModel; +import org.keycloak.models.GroupModel; +import org.keycloak.models.ModelException; +import org.keycloak.models.RealmModel; +import org.keycloak.models.UserModel; +import org.keycloak.models.utils.KeycloakModelUtils; +import org.keycloak.models.utils.UserModelDelegate; +import org.keycloak.storage.ldap.LDAPStorageProvider; +import org.keycloak.storage.ldap.idm.model.LDAPObject; +import org.keycloak.storage.ldap.idm.query.internal.LDAPQuery; +import org.keycloak.storage.ldap.mappers.AbstractLDAPStorageMapper; +import org.keycloak.storage.user.SynchronizationResult; +import org.vaulttec.keycloak.ldap.mappers.confluence.content.ConfluenceContentConfig; +import org.vaulttec.keycloak.ldap.mappers.confluence.content.ConfluencePage; +import org.vaulttec.keycloak.ldap.mappers.confluence.content.ConfluencePageProperty; +import org.vaulttec.keycloak.ldap.mappers.confluence.content.ContentCache; + +import java.util.*; +import java.util.function.Function; +import java.util.stream.Stream; + +public class ConfluenceGroupLDAPStorageMapper extends AbstractLDAPStorageMapper { + private static final Logger LOG = Logger.getLogger(ConfluenceGroupLDAPStorageMapper.class); + public static final String ATTRIBUTE_CONFLUENCE_PAGE_URL = "confluencePageURL"; + private final ConfluenceGroupMapperConfig mapperConfig; + private final ConfluenceContentConfig contentConfig; + private final ConfluenceGroupLDAPStorageMapperFactory factory; + // Flag to avoid syncing multiple times per transaction + private boolean syncFromConfluencePerformedInThisTransaction = false; + + public ConfluenceGroupLDAPStorageMapper(ComponentModel model, LDAPStorageProvider provider, ConfluenceGroupLDAPStorageMapperFactory factory) { + super(model, provider); + this.mapperConfig = new ConfluenceGroupMapperConfig(model); + this.contentConfig = new ConfluenceContentConfig(model); + this.factory = factory; + } + + @Override + public SynchronizationResult syncDataFromFederationProviderToKeycloak(RealmModel realm) { + SynchronizationResult syncResult = new SynchronizationResult() { + + @Override + public String getStatus() { + return String.format("%d imported groups, %d updated groups, %d removed groups", getAdded(), getUpdated(), getRemoved()); + } + + }; + LOG.debugf("Importing groups from Confluence into Keycloak DB. Mapper is [%s], LDAP provider is [%s]", mapperModel.getName(), ldapProvider.getModel().getName()); + ContentCache contentCache = factory.getContentCache(true); + updateKeycloakGroups(realm, contentCache, syncResult); + syncFromConfluencePerformedInThisTransaction = true; + return syncResult; + } + + private void updateKeycloakGroups(RealmModel realm, ContentCache contentCache, SynchronizationResult syncResult) { + Set visitedGroupIds = new HashSet<>(); + for (ConfluencePage page : contentCache.pages()) { + updateKeycloakGroup(realm, page, null, syncResult, visitedGroupIds); + } + if (mapperConfig.isDropNonExistingGroupsDuringSync()) { + dropNonExistingKcGroups(realm, syncResult, visitedGroupIds); + } + } + + private void updateKeycloakGroup(RealmModel realm, ConfluencePage page, GroupModel kcParent, SynchronizationResult syncResult, Set visitedGroupIds) { + String groupName = page.getTitle(); + + // Check if group already exists + GroupModel kcGroup = getKcSubGroups(realm, kcParent) + .filter(g -> Objects.equals(g.getName(), groupName)).findFirst().orElse(null); + if (kcGroup != null) { + LOG.debugf("Updated Keycloak group '%s' from Confluence", kcGroup.getName()); + syncResult.increaseUpdated(); + } else { + kcGroup = createKcGroup(realm, groupName, kcParent); + if (kcGroup.getParent() == null) { + LOG.debugf("Imported top-level group '%s' from Confluence", kcGroup.getName()); + } else { + LOG.debugf("Imported group '%s' from Confluence as child of group '%s'", kcGroup.getName(), kcGroup.getParent().getName()); + } + syncResult.increaseAdded(); + } + updateAttributesOfKCGroup(kcGroup, page); + visitedGroupIds.add(kcGroup.getId()); + + for (ConfluencePage child : page.getChildren()) { + updateKeycloakGroup(realm, child, kcGroup, syncResult, visitedGroupIds); + } + } + + private void updateAttributesOfKCGroup(GroupModel kcGroup, ConfluencePage page) { + kcGroup.setSingleAttribute(ATTRIBUTE_CONFLUENCE_PAGE_URL, contentConfig.getBaseUrl() + page.getRelativeUrl()); + } + + private void dropNonExistingKcGroups(RealmModel realm, SynchronizationResult syncResult, Set visitedGroupIds) { + GroupModel parent = getKcGroupsPathGroup(realm); + getAllKcGroups(realm, parent) + .filter(kcGroup -> !visitedGroupIds.contains(kcGroup.getId())) + .forEach(kcGroup -> { + LOG.debugf("Removing Keycloak group '%s', which doesn't exist in Confluence", kcGroup.getName()); + realm.removeGroup(kcGroup); + syncResult.increaseRemoved(); + }); + } + + @Override + public UserModel proxy(LDAPObject ldapUser, UserModel delegate, RealmModel realm) { + return new UserModelDelegate(delegate) { + + @Override + public void leaveGroup(GroupModel group) { + if (group.getFirstAttribute(ATTRIBUTE_CONFLUENCE_PAGE_URL) != null) { + throw new ModelException("Not possible to leave group maintained by Confluence mapper"); + } else { + super.leaveGroup(group); + } + } + }; + } + + public void onImportUserFromLDAP(LDAPObject ldapUser, UserModel user, RealmModel realm, boolean isCreate) { + String firstName = ldapUser.getAttributeAsString("givenName"); + String lastName = ldapUser.getAttributeAsString("sn"); + if (firstName != null && lastName != null) { + List mappedPages = getMappedPages(firstName, lastName); + if (!mappedPages.isEmpty()) { + GroupModel parent = getKcGroupsPathGroup(realm); + for (ConfluencePage mappedPage : mappedPages) { + GroupModel mappedGroup = findKcGroupOrSyncFromConfluence(realm, mappedPage, parent); + if (mappedGroup != null && !user.isMemberOf(mappedGroup)) { + LOG.debugf("User '%s' joins Confluence group '%s' during import from LDAP", user.getUsername(), mappedGroup.getName()); + user.joinGroup(mappedGroup); + } + } + } + } + } + + private GroupModel findKcGroupOrSyncFromConfluence(RealmModel realm, ConfluencePage page, GroupModel parent) { + GroupModel group = getGroupByConfluencePage(realm, page, parent); + if (group == null && !syncFromConfluencePerformedInThisTransaction) { + syncDataFromFederationProviderToKeycloak(realm); + group = getGroupByConfluencePage(realm, page, parent); + } + return group; + } + + private GroupModel getGroupByConfluencePage(RealmModel realm, ConfluencePage page, GroupModel parent) { + return getAllKcGroups(realm, parent) + .filter(g -> g.getName().equals(page.getTitle())).findFirst().orElse(null); + } + + private List getMappedPages(String firstName, String lastName) { + List mappingPages = new ArrayList<>(); + ContentCache contentCache = factory.getContentCache(false); + List userPageProperties = contentCache.pageProperties().stream() + .filter(p -> p.getValues().stream() + .map(normalizeUsername()) + .anyMatch(username -> username.startsWith(firstName) && username.endsWith(lastName))).toList(); + if (!userPageProperties.isEmpty()) { + for (ConfluencePageProperty userPageProperty : userPageProperties) { + mappingPages.add(contentCache.pagesMap().get(userPageProperty.getId())); + } + } + return mappingPages; + } + + /** + * If username specified as ", " then convert to " ". + */ + public static Function normalizeUsername() { + return username -> { + int delimiterPos = username.indexOf(","); + if (delimiterPos >= 0) { + return username.substring(delimiterPos + 1).trim() + " " + username.substring(0, delimiterPos).trim(); + } + return username; + }; + } + + @Override + public void onRegisterUserToLDAP(LDAPObject ldapUser, UserModel localUser, RealmModel realm) { + } + + @Override + public void beforeLDAPQuery(LDAPQuery query) { + } + + // Confluence groups path operations - copied from org.keycloak.storage.ldap.mappers.membership.group.GroupLDAPStorageMapper + + /** + * Provides KC group defined as groups path or null (top-level group) if corresponding group is not available. + */ + private GroupModel getKcGroupsPathGroup(RealmModel realm) { + return mapperConfig.isTopLevelGroupsPath() ? null : KeycloakModelUtils.findGroupByPath(session, realm, mapperConfig.getGroupsPath()); + } + + /** + * Creates a new KC group from given Confluence group name in given KC parent group or the groups path. + */ + private GroupModel createKcGroup(RealmModel realm, String confluenceGroupName, GroupModel parentGroup) { + + // If no parent group given then use groups path + if (parentGroup == null) { + parentGroup = getKcGroupsPathGroup(realm); + } + return realm.createGroup(confluenceGroupName, parentGroup); + } + + /** + * Provides a list of all KC sub groups from given parent group or from groups path. + */ + private Stream getKcSubGroups(RealmModel realm, GroupModel parentGroup) { + + // If no parent group given then use groups path + if (parentGroup == null) { + parentGroup = getKcGroupsPathGroup(realm); + } + return parentGroup == null ? session.groups().getTopLevelGroupsStream(realm) : + parentGroup.getSubGroupsStream(); + } + + /** + * Provides a stream of all KC groups (with their sub groups) from groups path configured by the "Groups Path" configuration property. + */ + private Stream getAllKcGroups(RealmModel realm, GroupModel topParentGroup) { + Stream allGroups = realm.getGroupsStream(); + if (topParentGroup == null) return allGroups; + + return allGroups.filter(group -> { + // Check if group is descendant of the topParentGroup (which is group configured by "Groups Path") + GroupModel parent = group.getParent(); + while (parent != null) { + if (parent.getId().equals(topParentGroup.getId())) { + return true; + } + parent = parent.getParent(); + } + return false; + }); + } +} diff --git a/src/main/java/org/vaulttec/keycloak/ldap/mappers/confluence/ConfluenceGroupLDAPStorageMapperFactory.java b/src/main/java/org/vaulttec/keycloak/ldap/mappers/confluence/ConfluenceGroupLDAPStorageMapperFactory.java new file mode 100644 index 0000000..797b11d --- /dev/null +++ b/src/main/java/org/vaulttec/keycloak/ldap/mappers/confluence/ConfluenceGroupLDAPStorageMapperFactory.java @@ -0,0 +1,92 @@ +package org.vaulttec.keycloak.ldap.mappers.confluence; + +import org.jboss.logging.Logger; +import org.keycloak.component.ComponentModel; +import org.keycloak.component.ComponentValidationException; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.models.utils.KeycloakModelUtils; +import org.keycloak.provider.ProviderConfigProperty; +import org.keycloak.provider.ProviderConfigurationBuilder; +import org.keycloak.storage.ldap.LDAPStorageProvider; +import org.keycloak.storage.ldap.mappers.AbstractLDAPStorageMapperFactory; +import org.keycloak.storage.ldap.mappers.membership.group.GroupMapperConfig; +import org.vaulttec.keycloak.ldap.mappers.confluence.content.ConfluenceContentConfig; +import org.vaulttec.keycloak.ldap.mappers.confluence.content.ConfluenceContentProvider; +import org.vaulttec.keycloak.ldap.mappers.confluence.content.ContentCache; + +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; + +public class ConfluenceGroupLDAPStorageMapperFactory extends AbstractLDAPStorageMapperFactory { + private static final Logger LOG = Logger.getLogger(ConfluenceGroupLDAPStorageMapperFactory.class); + public static final String PROVIDER_ID = "confluence-group-ldap-mapper"; + private ConfluenceContentProvider contentProvider; + private final AtomicReference contentCache = new AtomicReference<>(); + + @Override + public String getId() { + return PROVIDER_ID; + } + + @Override + public String getHelpText() { + return "Used to map groups and group memberships from Confluence pages and page properties"; + } + + @Override + public void onCreate(KeycloakSession session, RealmModel realm, ComponentModel model) { + this.contentProvider = new ConfluenceContentProvider(session, model); + getContentCache(true); + } + + @Override + public List getConfigProperties() { + return ProviderConfigurationBuilder.create() + .property().name(ConfluenceContentConfig.BASE_URL).label("Base URL").helpText("Base URL of the Confluence Server (with context path - if any)").type(ProviderConfigProperty.STRING_TYPE).required(true).add() + .property().name(ConfluenceContentConfig.AUTH_TOKEN).label("Bearer Token").helpText("Personal access token for API authentication").type(ProviderConfigProperty.PASSWORD).secret(true).required(true).add() + .property().name(ConfluenceContentConfig.PARENT_PAGE_ID).label("Parent Page ID").helpText("ID of parent page the child pages are retrieved from").type(ProviderConfigProperty.STRING_TYPE).required(true).add() + .property().name(ConfluenceContentConfig.PAGE_NESTING).label("Page Nesting Depth").helpText("Max depth of nested child pages").type(ProviderConfigProperty.STRING_TYPE).defaultValue(ConfluenceContentConfig.DEFAULT_PAGE_NESTING).required(true).add() + .property().name(ConfluenceContentConfig.SPACE_KEY).label("Space Key").helpText("Key of Confluence space the pages are belonging to").type(ProviderConfigProperty.STRING_TYPE).required(true).add() + .property().name(ConfluenceContentConfig.PAGE_LABELS).label("Page Label(s)").helpText("Comma-separated list of labels required for the pages").type(ProviderConfigProperty.STRING_TYPE).add() + .property().name(ConfluenceContentConfig.PAGE_PROPERTY_NAME).label("Page Property Name").helpText("Name of page property with the table holding the group members ").type(ProviderConfigProperty.STRING_TYPE).required(true).add() + .property().name(ConfluenceContentConfig.MEMBER_COLUMN_INDEX).label("Member Column Index").helpText("Index of table column with group members").type(ProviderConfigProperty.STRING_TYPE).defaultValue("1").required(true).add() + .property().name(ConfluenceGroupMapperConfig.DROP_NON_EXISTING_GROUPS_DURING_SYNC).label("Drop non-existing Groups").helpText("During sync of groups from Confluence to Keycloak, we will keep just those Keycloak groups, which still exists in Confluence. Rest will be deleted.").type(ProviderConfigProperty.BOOLEAN_TYPE).defaultValue(true).add() + .property().name(ConfluenceGroupMapperConfig.GROUPS_PATH).label("Groups Path").helpText("Keycloak group path the Confluence groups are added to").type(ProviderConfigProperty.STRING_TYPE).defaultValue(ConfluenceGroupMapperConfig.DEFAULT_GROUPS_PATH).required(true).add() + .build(); + } + + @Override + public void validateConfiguration(KeycloakSession session, RealmModel realm, ComponentModel config) throws ComponentValidationException { + checkMandatoryConfigAttribute(ConfluenceContentConfig.BASE_URL, "Base URL", config); + checkMandatoryConfigAttribute(ConfluenceContentConfig.AUTH_TOKEN, "Bearer Token", config); + checkMandatoryConfigAttribute(ConfluenceContentConfig.PARENT_PAGE_ID, "Parent Page ID", config); + checkMandatoryConfigAttribute(ConfluenceContentConfig.PAGE_NESTING, "Page Nesting Depth", config); + checkMandatoryConfigAttribute(ConfluenceContentConfig.SPACE_KEY, "Space Key", config); + checkMandatoryConfigAttribute(ConfluenceContentConfig.PAGE_PROPERTY_NAME, "Page Property Name", config); + checkMandatoryConfigAttribute(ConfluenceContentConfig.MEMBER_COLUMN_INDEX, "Member Column Index", config); + checkMandatoryConfigAttribute(ConfluenceGroupMapperConfig.GROUPS_PATH, "Groups Path", config); + + String baseUrl = new ConfluenceContentConfig(config).getBaseUrl(); + if (baseUrl.trim().endsWith("/")) { + throw new ComponentValidationException("No trailing slash in Base URL allowed"); + } + String groupsPath = new GroupMapperConfig(config).getGroupsPath(); + if (!ConfluenceGroupMapperConfig.DEFAULT_GROUPS_PATH.equals(groupsPath) && KeycloakModelUtils.findGroupByPath(session, realm, groupsPath) == null) { + throw new ComponentValidationException("ldapErrorMissingGroupsPathGroup"); + } + } + + @Override + public ConfluenceGroupLDAPStorageMapper createMapper(ComponentModel model, LDAPStorageProvider provider) { + return new ConfluenceGroupLDAPStorageMapper(model, provider, this); + } + + /* package */ ContentCache getContentCache(boolean refresh) { + if (refresh) { + LOG.debug("Refreshing content cache"); + contentCache.set(ContentCache.of(contentProvider)); + } + return contentCache.get(); + } +} diff --git a/src/main/java/org/vaulttec/keycloak/ldap/mappers/confluence/ConfluenceGroupMapperConfig.java b/src/main/java/org/vaulttec/keycloak/ldap/mappers/confluence/ConfluenceGroupMapperConfig.java new file mode 100644 index 0000000..31f47de --- /dev/null +++ b/src/main/java/org/vaulttec/keycloak/ldap/mappers/confluence/ConfluenceGroupMapperConfig.java @@ -0,0 +1,34 @@ +package org.vaulttec.keycloak.ldap.mappers.confluence; + +import org.keycloak.common.util.ObjectUtil; +import org.keycloak.component.ComponentModel; + +import static org.keycloak.models.utils.KeycloakModelUtils.GROUP_PATH_SEPARATOR; + +public class ConfluenceGroupMapperConfig { + // During sync of groups from Confluence to Keycloak, we will keep just those Keycloak groups, which still exists in Confluence. Rest will be deleted + public static final String DROP_NON_EXISTING_GROUPS_DURING_SYNC = "confluenceGroups.dropNonExistingGroups"; + + // Keycloak group path the Confluence groups are added to (default: top level "/") + public static final String GROUPS_PATH = "confluenceGroups.groupsPath"; + public static final String DEFAULT_GROUPS_PATH = GROUP_PATH_SEPARATOR; + + private final ComponentModel model; + + public ConfluenceGroupMapperConfig(ComponentModel model) { + this.model = model; + } + + public boolean isDropNonExistingGroupsDuringSync() { + return model.get(DROP_NON_EXISTING_GROUPS_DURING_SYNC, false); + } + + public String getGroupsPath() { + String groupsPath = model.get(GROUPS_PATH); + return ObjectUtil.isBlank(groupsPath) ? DEFAULT_GROUPS_PATH : groupsPath.trim(); + } + + public boolean isTopLevelGroupsPath() { + return GROUP_PATH_SEPARATOR.equals(getGroupsPath()); + } +} diff --git a/src/main/java/org/vaulttec/keycloak/ldap/mappers/confluence/content/ConfluenceContentConfig.java b/src/main/java/org/vaulttec/keycloak/ldap/mappers/confluence/content/ConfluenceContentConfig.java new file mode 100644 index 0000000..10292e0 --- /dev/null +++ b/src/main/java/org/vaulttec/keycloak/ldap/mappers/confluence/content/ConfluenceContentConfig.java @@ -0,0 +1,53 @@ +package org.vaulttec.keycloak.ldap.mappers.confluence.content; + +import org.keycloak.component.ComponentModel; + +public class ConfluenceContentConfig { + public static final String BASE_URL = "confluenceContent.baseUrl"; + public static final String AUTH_TOKEN = "confluenceContent.authToken"; + public static final String PARENT_PAGE_ID = "confluenceContent.parentPageId"; + public static final String PAGE_NESTING = "confluenceContent.pageNesting"; + public static final int DEFAULT_PAGE_NESTING = 3; + public static final String SPACE_KEY = "confluenceContent.spaceKey"; + public static final String PAGE_PROPERTY_NAME = "confluenceContent.pagePropertyName"; + public static final String PAGE_LABELS = "confluenceContent.pageLabels"; + public static final String MEMBER_COLUMN_INDEX = "confluenceContent.memberColumnIndex"; + public static final int DEFAULT_MEMBER_COLUMN_INDEX = 1; + private final ComponentModel model; + + public ConfluenceContentConfig(ComponentModel model) { + this.model = model; + } + + public String getBaseUrl() { + return model.get(BASE_URL).trim(); + } + + public String getAuthToken() { + return model.get(AUTH_TOKEN).trim(); + } + + public String getParentPageId() { + return model.get(PARENT_PAGE_ID).trim(); + } + + public int getPageNesting() { + return model.get(PAGE_NESTING, DEFAULT_PAGE_NESTING); + } + + public String getSpaceKey() { + return model.get(SPACE_KEY).trim(); + } + + public String getPagePropertyName() { + return model.get(PAGE_PROPERTY_NAME).trim(); + } + + public String getPageLabels() { + return model.get(PAGE_LABELS).trim(); + } + + public int getMemberColumnIndex() { + return model.get(MEMBER_COLUMN_INDEX, DEFAULT_MEMBER_COLUMN_INDEX); + } +} diff --git a/src/main/java/org/vaulttec/keycloak/ldap/mappers/confluence/content/ConfluenceContentProvider.java b/src/main/java/org/vaulttec/keycloak/ldap/mappers/confluence/content/ConfluenceContentProvider.java new file mode 100644 index 0000000..0327a8e --- /dev/null +++ b/src/main/java/org/vaulttec/keycloak/ldap/mappers/confluence/content/ConfluenceContentProvider.java @@ -0,0 +1,67 @@ +package org.vaulttec.keycloak.ldap.mappers.confluence.content; + +import com.fasterxml.jackson.databind.JsonNode; +import org.apache.http.impl.client.CloseableHttpClient; +import org.jboss.logging.Logger; +import org.keycloak.broker.provider.util.SimpleHttp; +import org.keycloak.component.ComponentModel; +import org.keycloak.connections.httpclient.HttpClientProvider; +import org.keycloak.models.KeycloakSession; + +import java.io.IOException; +import java.util.*; +import java.util.stream.Collectors; + +public class ConfluenceContentProvider { + + private static final Logger LOG = Logger.getLogger(ConfluenceContentProvider.class); + + private final CloseableHttpClient httpClient; + private final ConfluenceContentConfig config; + + public ConfluenceContentProvider(KeycloakSession session, ComponentModel model) { + this.config = new ConfluenceContentConfig(model); + this.httpClient = session.getProvider(HttpClientProvider.class).getHttpClient(); + } + + public List getPages() { + try { + SimpleHttp simpleHttp = SimpleHttp.doGet(config.getBaseUrl() + "/rest/api/content/" + config.getParentPageId() + "/child", httpClient) + .auth(config.getAuthToken()) + .param("expand", "page" + ".child.page".repeat(config.getPageNesting() - 1)); + List children = ConfluencePage.getChildren(simpleHttp.asJson()); + LOG.debugf("Retrieved %s child pages from parent page %s", children.size(), config.getParentPageId()); + return children; + } catch (IOException e) { + LOG.errorf(e, "Retrieving child page from %s failed", config.getBaseUrl()); + } + return Collections.emptyList(); + } + + public List getPageProperties() { + List pageProperties = new ArrayList<>(); + try { + for (int pageIndex = 0, totalPages = 1; totalPages > pageIndex; pageIndex++) { + SimpleHttp simpleHttp = SimpleHttp.doGet(config.getBaseUrl() + "/rest/masterdetail/1.0/detailssummary/lines", httpClient) + .auth(config.getAuthToken()) + .param("spaceKey", config.getSpaceKey()) + .param("cql", "type=page AND " + Arrays.stream(config.getPageLabels().split(",")) + .map(label -> "label='" + label.trim() + "'").collect(Collectors.joining(" AND "))) + .param("headings", config.getPagePropertyName()).param("pageIndex", String.valueOf(pageIndex)) + .param("pageSize", "500"); + JsonNode node = simpleHttp.asJson(); + if (node.has("totalPages") && node.has("detailLines")) { + totalPages = node.get("totalPages").asInt(); + pageProperties.addAll(ConfluencePageProperty.getPageProperties(node)); + } + } + for (ConfluencePageProperty pageProperty : pageProperties) { + pageProperty.setValues(config.getMemberColumnIndex()); + } + LOG.debugf("Retrieved %s page properties from space %s", pageProperties.size(), config.getSpaceKey()); + } catch (IOException e) { + LOG.errorf(e, "Retrieving page properties from %s failed", config.getBaseUrl()); + } + return pageProperties; + } +} diff --git a/src/main/java/org/vaulttec/keycloak/ldap/mappers/confluence/content/ConfluencePage.java b/src/main/java/org/vaulttec/keycloak/ldap/mappers/confluence/content/ConfluencePage.java new file mode 100644 index 0000000..8627eeb --- /dev/null +++ b/src/main/java/org/vaulttec/keycloak/ldap/mappers/confluence/content/ConfluencePage.java @@ -0,0 +1,64 @@ +package org.vaulttec.keycloak.ldap.mappers.confluence.content; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +@JsonIgnoreProperties(ignoreUnknown = true) +public class ConfluencePage { + private static final ObjectMapper MAPPER = new ObjectMapper(); + @JsonProperty + private String id; + @JsonProperty + private String title; + private String relativeUrl; + private List children; + + @JsonProperty("_links") + protected void unnestLinks(JsonNode node) { + if (node.has("tinyui")) { + relativeUrl = node.get("tinyui").textValue(); + } + } + + @JsonProperty("children") + protected void unnestChildren(JsonNode node) { + children = getChildren(node); + } + + public String getId() { + return id; + } + + public String getTitle() { + return title; + } + + public String getRelativeUrl() { + return relativeUrl; + } + + public List getChildren() { + return children; + } + + /* package */ static List getChildren(JsonNode node) { + if (node.has("page") && node.get("page").has("results")) { + return MAPPER.convertValue(node.get("page").get("results"), new TypeReference<>() {}); + } + return Collections.emptyList(); + } + + public static void mapChildren(ConfluencePage page, Map pagesMap) { + pagesMap.put(page.getId(), page); + for (ConfluencePage child : page.getChildren()) { + mapChildren(child, pagesMap); + } + } +} diff --git a/src/main/java/org/vaulttec/keycloak/ldap/mappers/confluence/content/ConfluencePageProperty.java b/src/main/java/org/vaulttec/keycloak/ldap/mappers/confluence/content/ConfluencePageProperty.java new file mode 100644 index 0000000..c2b6650 --- /dev/null +++ b/src/main/java/org/vaulttec/keycloak/ldap/mappers/confluence/content/ConfluencePageProperty.java @@ -0,0 +1,47 @@ +package org.vaulttec.keycloak.ldap.mappers.confluence.content; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +@JsonIgnoreProperties(ignoreUnknown = true) +public class ConfluencePageProperty { + private static final ObjectMapper MAPPER = new ObjectMapper(); + @JsonProperty + private String id; + @JsonProperty + private List details; + private List values; + + public String getId() { + return id; + } + + /* package */ void setValues(int columnIndex) { + values = new ArrayList<>(); + for (String detail : details) { + Elements elements = Jsoup.parse(detail).selectXpath("//tr/td[position()=" + columnIndex + "]"); + elements.stream().filter(Element::hasText).forEach(e -> values.add(e.text().trim())); + } + } + + public List getValues() { + return values; + } + + /* package */ static List getPageProperties(JsonNode node) { + if (node.has("detailLines")) { + return MAPPER.convertValue(node.get("detailLines"), new TypeReference<>() {}); + } + return Collections.emptyList(); + } +} diff --git a/src/main/java/org/vaulttec/keycloak/ldap/mappers/confluence/content/ContentCache.java b/src/main/java/org/vaulttec/keycloak/ldap/mappers/confluence/content/ContentCache.java new file mode 100644 index 0000000..9a9c48a --- /dev/null +++ b/src/main/java/org/vaulttec/keycloak/ldap/mappers/confluence/content/ContentCache.java @@ -0,0 +1,16 @@ +package org.vaulttec.keycloak.ldap.mappers.confluence.content; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public record ContentCache(List pages, Map pagesMap, + List pageProperties) { + public static ContentCache of(ConfluenceContentProvider contentProvider) { + List pages = contentProvider.getPages(); + Map pagesMap = new HashMap<>(); + pages.forEach(p -> ConfluencePage.mapChildren(p, pagesMap)); + List pageProperties = contentProvider.getPageProperties(); + return new ContentCache(pages, pagesMap, pageProperties); + } +} diff --git a/src/main/resources/META-INF/services/org.keycloak.storage.ldap.mappers.LDAPStorageMapperFactory b/src/main/resources/META-INF/services/org.keycloak.storage.ldap.mappers.LDAPStorageMapperFactory new file mode 100644 index 0000000..41e7288 --- /dev/null +++ b/src/main/resources/META-INF/services/org.keycloak.storage.ldap.mappers.LDAPStorageMapperFactory @@ -0,0 +1 @@ +org.vaulttec.keycloak.ldap.mappers.confluence.ConfluenceGroupLDAPStorageMapperFactory diff --git a/src/test/java/org/vaulttec/keycloak/ldap/mappers/confluence/ConfluenceGroupLDAPStorageMapperIT.java b/src/test/java/org/vaulttec/keycloak/ldap/mappers/confluence/ConfluenceGroupLDAPStorageMapperIT.java new file mode 100644 index 0000000..b77d14e --- /dev/null +++ b/src/test/java/org/vaulttec/keycloak/ldap/mappers/confluence/ConfluenceGroupLDAPStorageMapperIT.java @@ -0,0 +1,63 @@ +package org.vaulttec.keycloak.ldap.mappers.confluence; + +import dasniko.testcontainers.keycloak.KeycloakContainer; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.keycloak.admin.client.Keycloak; +import org.keycloak.admin.client.resource.UserResource; +import org.keycloak.admin.client.resource.UsersResource; +import org.keycloak.representations.idm.GroupRepresentation; +import org.keycloak.representations.idm.UserRepresentation; + +import java.util.List; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class ConfluenceGroupLDAPStorageMapperIT { + public static final String REALM = "acme"; + public static final KeycloakEnvironment KEYCLOAK_ENVIRONMENT = new KeycloakEnvironment(); + + @BeforeAll + public static void beforeAll() { + KEYCLOAK_ENVIRONMENT.start(); + } + + @AfterAll + public static void afterAll() { + KEYCLOAK_ENVIRONMENT.stop(); + } + + @ParameterizedTest + @ValueSource(strings = { KeycloakContainer.MASTER_REALM, REALM}) + public void testRealms(String realm) { + String accountServiceUrl = given().when().get(KEYCLOAK_ENVIRONMENT.getAuthServerUrl() + "/realms/" + realm) + .then().statusCode(200).body("realm", equalTo(realm)) + .extract().path("account-service"); + + given().when().get(accountServiceUrl).then().statusCode(200); + } + + @ParameterizedTest + @ValueSource(strings = { "dooj1:johndoo", "dooj2:janedoo", "dooj3:jimdoo"}) + public void testAccessingUsersAsAdmin(String usernameAndMailname) { + String[] splittedParameter = usernameAndMailname.split(":"); + Keycloak kcAdmin = KEYCLOAK_ENVIRONMENT.getAdminClient(); + UsersResource usersResource = kcAdmin.realm(REALM).users(); + List users = usersResource.search(splittedParameter[0]); + assertThat(users, is(not(empty()))); + + String userId = users.get(0).getId(); + UserResource userResource = usersResource.get(userId); + assertThat(userResource.toRepresentation().getEmail(), is(splittedParameter[1] + "@ns-mail8.com")); + + List groups = userResource.groups(); + assertEquals(2, groups.size()); + assertTrue(groups.stream().allMatch(group -> "Page 1.1".equals(group.getName()) || "Page 1.2".equals(group.getName()))); + } +} \ No newline at end of file diff --git a/src/test/java/org/vaulttec/keycloak/ldap/mappers/confluence/KeycloakEnvironment.java b/src/test/java/org/vaulttec/keycloak/ldap/mappers/confluence/KeycloakEnvironment.java new file mode 100644 index 0000000..fd4d370 --- /dev/null +++ b/src/test/java/org/vaulttec/keycloak/ldap/mappers/confluence/KeycloakEnvironment.java @@ -0,0 +1,186 @@ +package org.vaulttec.keycloak.ldap.mappers.confluence; + +import dasniko.testcontainers.keycloak.KeycloakContainer; +import org.jboss.shrinkwrap.resolver.api.maven.Maven; +import org.keycloak.admin.client.Keycloak; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testcontainers.containers.BindMode; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.Network; +import org.testcontainers.containers.output.Slf4jLogConsumer; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.images.builder.ImageFromDockerfile; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.stream.Collectors; + +public class KeycloakEnvironment { + private static final Logger LOG = LoggerFactory.getLogger(KeycloakEnvironment.class); + public static final String GLOBAL_ENV_PATH = "config/global.env"; + public static final String KEYCLOAK_IMAGE = "keycloak/keycloak:23.0.7"; + public static final String KEYCLOAK_CONFIG_CLI_IMAGE = "adorsys/keycloak-config-cli:5.10.0-23.0.1"; + public static final String OPENLDAP_IMAGE = "osixia/openldap:1.5.0"; + public static final String MOCK_SERVER_IMAGE = "mockserver/mockserver:5.15.0"; + + private Network network; + private KeycloakContainer keycloak; + private GenericContainer keycloakConfigCli; + private GenericContainer openLDAP; + private GenericContainer mockServer; + + public KeycloakContainer getKeycloak() { + return keycloak; + } + + public GenericContainer getKeycloakConfigCli() { + return keycloakConfigCli; + } + + public GenericContainer getOpenLDAP() { + return openLDAP; + } + + public GenericContainer getMockServer() { + return mockServer; + } + + public Keycloak getAdminClient() { + return keycloak.getKeycloakAdminClient(); + } + + public String getAuthServerUrl() { + return keycloak.getAuthServerUrl(); + } + + public String getAdminUsername() { + return keycloak.getAdminUsername(); + } + + public String getAdminPassword() { + return keycloak.getAdminPassword(); + } + + public void start() { + network = Network.newNetwork(); + + openLDAP = createOpenLDAPContainer().withNetwork(network); + openLDAP.start(); + openLDAP.followOutput(new Slf4jLogConsumer(LOG)); + + mockServer = createMockServerContainer().withNetwork(network); + mockServer.start(); + mockServer.followOutput(new Slf4jLogConsumer(LOG)); + + keycloak = createKeycloakContainer().withNetwork(network); + keycloak.start(); + keycloak.followOutput(new Slf4jLogConsumer(LOG)); + + keycloakConfigCli = createKeycloakConfigCliContainer().withNetwork(network); + keycloakConfigCli.start(); + keycloakConfigCli.followOutput(new Slf4jLogConsumer(LOG)); + } + + public void stop() { + if (keycloak != null) { + keycloak.stop(); + } + if (keycloakConfigCli != null) { + keycloakConfigCli.stop(); + } + if (mockServer != null) { + mockServer.stop(); + } + if (openLDAP != null) { + openLDAP.stop(); + } + if (network != null) { + network.close(); + } + } + + public static KeycloakContainer createKeycloakContainer() { + return createKeycloakContainer(null); + } + + public static KeycloakContainer createKeycloakContainer(String realmImportFileName) { + return createKeycloakContainer(KEYCLOAK_IMAGE, realmImportFileName); + } + + public static KeycloakContainer createKeycloakContainer(String imageName, String realmImportFileName) { + List dependencies = Maven.resolver() + .loadPomFromFile("./pom.xml") + .resolve("org.jsoup:jsoup") + .withoutTransitivity().asList(File.class); + + KeycloakContainer keycloakContainer; + if (imageName != null) { + keycloakContainer = new KeycloakContainer(imageName); + } else { + // building custom Keycloak docker image with additional libraries + String customDockerFileName = "Dockerfile-Keycloak"; + ImageFromDockerfile imageFromDockerfile = new ImageFromDockerfile(); + imageFromDockerfile.withDockerfile(Paths.get(customDockerFileName)); + keycloakContainer = new KeycloakContainer(); + keycloakContainer.setImage(imageFromDockerfile); + } + if (realmImportFileName != null) { + keycloakContainer.withRealmImportFile(realmImportFileName); + } + Map globalEnv = readEnvFile(GLOBAL_ENV_PATH); + keycloakContainer.withEnv(globalEnv) + .withNetworkAliases("keycloak") + .withEnv("DEBUG", "true") + .withEnv("DEBUG_PORT", "*:8787") + .withContextPath(globalEnv.getOrDefault("KC_HTTP_RELATIVE_PATH", "/")) + .withProviderLibsFrom(dependencies) + .withProviderClassesFrom("target/classes"); + return keycloakContainer; + } + + public static GenericContainer createKeycloakConfigCliContainer() { + return new GenericContainer<>(KEYCLOAK_CONFIG_CLI_IMAGE) + .withNetworkAliases("keycloak-provisioning") + .withEnv(readEnvFile(GLOBAL_ENV_PATH)) + .withFileSystemBind("config/realms", "/config", BindMode.READ_ONLY) + .waitingFor(Wait.forLogMessage(".*keycloak-config-cli running in.*", 1)); + } + + public static GenericContainer createOpenLDAPContainer() { + return new GenericContainer<>(OPENLDAP_IMAGE) + .withNetworkAliases("openldap") + .withEnv(readEnvFile(GLOBAL_ENV_PATH)) + .withFileSystemBind("./config/ldap/acme.ldif", "/tmp/ldif/acme.ldif", BindMode.READ_ONLY) + .withCommand( "--copy-service --loglevel warning"); + } + + public static GenericContainer createMockServerContainer() { + return new GenericContainer<>(MOCK_SERVER_IMAGE) + .withNetworkAliases("mockserver") + .withEnv(readEnvFile(GLOBAL_ENV_PATH)) + .withFileSystemBind("./config/mock", "/config", BindMode.READ_ONLY) + .withExposedPorts(1080); + } + + public static Map readEnvFile(String filePath) { + Map map = new HashMap<>(); + try (FileInputStream inputStream = new FileInputStream(filePath)) { + Properties properties = new Properties(); + properties.load(inputStream); + map.putAll(properties.entrySet() + .stream() + .collect(Collectors.toMap(e -> e.getKey().toString(), + e -> e.getValue().toString()))); + } catch (IOException e) { + LOG.error("Error reading env file '" + filePath + "'", e); + } + return map; + } +} diff --git a/src/test/java/org/vaulttec/keycloak/ldap/mappers/confluence/content/ConfluenceContentProviderIT.java b/src/test/java/org/vaulttec/keycloak/ldap/mappers/confluence/content/ConfluenceContentProviderIT.java new file mode 100644 index 0000000..6d68f60 --- /dev/null +++ b/src/test/java/org/vaulttec/keycloak/ldap/mappers/confluence/content/ConfluenceContentProviderIT.java @@ -0,0 +1,84 @@ +package org.vaulttec.keycloak.ldap.mappers.confluence.content; + +import org.apache.http.client.utils.URIBuilder; +import org.apache.http.impl.client.HttpClientBuilder; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.keycloak.component.ComponentModel; +import org.keycloak.connections.httpclient.HttpClientProvider; +import org.keycloak.models.KeycloakSession; +import org.mockserver.configuration.Configuration; +import org.mockserver.integration.ClientAndServer; +import org.slf4j.event.Level; + +import java.net.URL; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.mockserver.mock.OpenAPIExpectation.openAPIExpectation; +import static org.vaulttec.keycloak.ldap.mappers.confluence.content.ConfluenceContentConfig.*; + +public class ConfluenceContentProviderIT { + private static ClientAndServer mockServer; + private static ConfluenceContentProvider provider; + + @BeforeAll + static void setup() throws Exception { + Configuration config = new Configuration().logLevel(Level.WARN); + mockServer = ClientAndServer.startClientAndServer(config); + mockServer.upsert(openAPIExpectation("config/mock/confluence-openapi.yaml")); + URL mockEndpoint = new URIBuilder("http://localhost").setPort(mockServer.getPort()).setPath("confluence").build().toURL(); + + HttpClientProvider clientProvider = mock(HttpClientProvider.class); + when(clientProvider.getHttpClient()).thenReturn(HttpClientBuilder.create().build()); + KeycloakSession session = mock(KeycloakSession.class); + when(session.getProvider(HttpClientProvider.class)).thenReturn(clientProvider); + + ComponentModel model = mock(ComponentModel.class); + when(model.get(BASE_URL)).thenReturn(mockEndpoint.toString()); + when(model.get(AUTH_TOKEN)).thenReturn("token"); + when(model.get(PARENT_PAGE_ID)).thenReturn("1234"); + when(model.get(PAGE_NESTING, DEFAULT_PAGE_NESTING)).thenReturn(4); + when(model.get(SPACE_KEY)).thenReturn("TEST"); + when(model.get(PAGE_LABELS)).thenReturn("label1 , label2 , lbl-test"); + when(model.get(PAGE_PROPERTY_NAME)).thenReturn("Team"); + when(model.get(MEMBER_COLUMN_INDEX, DEFAULT_MEMBER_COLUMN_INDEX)).thenReturn(1); + provider = new ConfluenceContentProvider(session, model); + } + + @AfterAll + static void shutdown() { + mockServer.stop(); + } + + @Test + public void testGetPagesWithTitle() { + List pages = provider.getPages(); + assertNotNull(pages); + assertEquals(2, pages.size()); + assertEquals("Page 1", pages.get(0).getTitle()); + assertEquals(2, pages.get(0).getChildren().size()); + assertEquals("Page 1.1", pages.get(0).getChildren().get(0).getTitle()); + assertEquals("Page 1.2", pages.get(0).getChildren().get(1).getTitle()); + assertEquals("Page 2", pages.get(1).getTitle()); + } + + @Test + public void testGetPagePropertiesWithValues() { + List properties = provider.getPageProperties(); + assertNotNull(properties); + assertEquals(2, properties.size()); + assertEquals(3, properties.get(0).getValues().size()); + assertEquals("John Doo", properties.get(0).getValues().get(0)); + assertEquals("Doo, Jim", properties.get(0).getValues().get(1)); + assertEquals("Jane Doo", properties.get(0).getValues().get(2)); + assertEquals(3, properties.get(1).getValues().size()); + assertEquals("Jane Doo", properties.get(1).getValues().get(0)); + assertEquals("Doo, John", properties.get(1).getValues().get(1)); + assertEquals("Jim Doo", properties.get(1).getValues().get(2)); + } +} \ No newline at end of file diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml new file mode 100644 index 0000000..e95f3cd --- /dev/null +++ b/src/test/resources/logback-test.xml @@ -0,0 +1,19 @@ + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n + + + + + + + + + + + + + + + \ No newline at end of file