From 972f4b686cfe5c14ac447b8d20eb1f82f07f76fc Mon Sep 17 00:00:00 2001 From: Nuvindu Date: Mon, 15 Jul 2024 10:03:29 +0530 Subject: [PATCH 01/10] Fix value conversion error in `LdapResponse` --- ballerina/types.bal | 10 +++++----- .../src/main/java/io/ballerina/lib/ldap/Client.java | 3 ++- native/src/main/java/io/ballerina/lib/ldap/Utils.java | 11 +++++++++++ 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/ballerina/types.bal b/ballerina/types.bal index c1214f7..c3252b5 100644 --- a/ballerina/types.bal +++ b/ballerina/types.bal @@ -33,13 +33,13 @@ public type ConnectionConfig record {| # + resultCode - The operation status of the response # + diagnosticMessage - The diagnostic message from the response # + operationType - The protocol operation type -# + refferal - The referral URIs +# + referral - The referral URIs public type LdapResponse record {| - string matchedDN; + string? matchedDN; Status resultCode; - string diagnosticMessage; - string operationType; - string[] refferal; + string? diagnosticMessage; + string? operationType; + string[]? referral; |}; # LDAP search result type. diff --git a/native/src/main/java/io/ballerina/lib/ldap/Client.java b/native/src/main/java/io/ballerina/lib/ldap/Client.java index 4723e85..e08cded 100644 --- a/native/src/main/java/io/ballerina/lib/ldap/Client.java +++ b/native/src/main/java/io/ballerina/lib/ldap/Client.java @@ -62,6 +62,7 @@ import static io.ballerina.lib.ldap.Utils.LDAP_CONNECTION_CLOSED_ERROR; import static io.ballerina.lib.ldap.Utils.convertObjectGUIDToString; import static io.ballerina.lib.ldap.Utils.convertObjectSidToString; +import static io.ballerina.lib.ldap.Utils.convertToBArray; import static io.ballerina.lib.ldap.Utils.convertToStringArray; import static io.ballerina.lib.ldap.Utils.getSearchScope; @@ -312,7 +313,7 @@ public static BMap generateLdapResponse(LDAPResult ldapResult) response.put(RESULT_STATUS, StringUtils.fromString(ldapResult.getResultCode().getName().toUpperCase(Locale.ROOT))); response.put(DIAGNOSTIC_MESSAGE, StringUtils.fromString(ldapResult.getDiagnosticMessage())); - response.put(REFERRAL, convertToStringArray(ldapResult.getReferralURLs())); + response.put(REFERRAL, convertToBArray(ldapResult.getReferralURLs())); response.put(OPERATION_TYPE, StringUtils.fromString(ldapResult.getOperationType().name())); return response; } diff --git a/native/src/main/java/io/ballerina/lib/ldap/Utils.java b/native/src/main/java/io/ballerina/lib/ldap/Utils.java index 34b1dd5..81857fb 100644 --- a/native/src/main/java/io/ballerina/lib/ldap/Utils.java +++ b/native/src/main/java/io/ballerina/lib/ldap/Utils.java @@ -155,6 +155,17 @@ public static String[] convertToStringArray(Object[] objectArray) { .toArray(String[]::new); } + public static BArray convertToBArray(Object[] objectArray) { + if (Objects.isNull(objectArray)) { + return ValueCreator.createArrayValue(new BString[]{}); + } + BString[] array = Arrays.stream(objectArray) + .filter(Objects::nonNull) + .map(object -> StringUtils.fromString(object.toString())) + .toArray(BString[]::new); + return ValueCreator.createArrayValue(array); + } + public static SearchScope getSearchScope(BString scope) { return switch (scope.getValue()) { case "SUB" -> SearchScope.SUB; From bc01350bbb5f5f9590bb6b6cc8f596e42a22688f Mon Sep 17 00:00:00 2001 From: Nuvindu Date: Mon, 15 Jul 2024 10:04:13 +0530 Subject: [PATCH 02/10] Add a new example for the ldap package --- .../access-directory-server/Ballerina.toml | 8 +++ examples/access-directory-server/README.md | 30 ++++++++++ examples/access-directory-server/main.bal | 60 +++++++++++++++++++ .../resources/docker-compose.yml | 14 +++++ 4 files changed, 112 insertions(+) create mode 100644 examples/access-directory-server/Ballerina.toml create mode 100644 examples/access-directory-server/README.md create mode 100644 examples/access-directory-server/main.bal create mode 100644 examples/access-directory-server/resources/docker-compose.yml diff --git a/examples/access-directory-server/Ballerina.toml b/examples/access-directory-server/Ballerina.toml new file mode 100644 index 0000000..3e3cce5 --- /dev/null +++ b/examples/access-directory-server/Ballerina.toml @@ -0,0 +1,8 @@ +[package] +org = "ballerina" +name = "access_directory_server" +version = "0.1.0" +distribution = "2201.8.0" + +[build-options] +observabilityIncluded = true diff --git a/examples/access-directory-server/README.md b/examples/access-directory-server/README.md new file mode 100644 index 0000000..011fb38 --- /dev/null +++ b/examples/access-directory-server/README.md @@ -0,0 +1,30 @@ +# Employee Directory and Authentication System + +This example demonstrates using the Ballerina LDAP module to integrate with a directory server to manage employees in a corporation. The functionalities include authenticating with the directory server, adding a new user, searching for users, updating user details, and deleting a user. + +## Running an Example + +### 1. Start the directory server + +Run the following docker command to start the LDAP server. + +```sh +cd resources +docker compose up +``` + +### 2. Run the Ballerina project + +Execute the following commands to build an example from the source: + +* To build an example: + + ```bash + bal build + ``` + +* To run an example: + + ```bash + bal run + ``` diff --git a/examples/access-directory-server/main.bal b/examples/access-directory-server/main.bal new file mode 100644 index 0000000..6316f88 --- /dev/null +++ b/examples/access-directory-server/main.bal @@ -0,0 +1,60 @@ +// Copyright (c) 2024 WSO2 LLC. (http://www.wso2.com). +// +// WSO2 LLC. 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. + +import ballerina/ldap; +import ballerina/io; + +configurable string hostName = ?; +configurable int port = ?; +configurable string domainName = ?; +configurable string password = ?; +configurable string userDN = ?; + +public function main() returns error? { + // Authenticate using the directory server credentials. + ldap:Client ldapClient = check new ({ + hostName, + port, + domainName, + password + }); + + ldap:Entry employee = { + "objectClass": ["top", "person"], + "sn": "New User" + }; + + // Add a new employee to the directory server. + ldap:LdapResponse addResponse = check ldapClient->add("cn=User,dc=mycompany,dc=com", employee); + io:println(addResponse); + + // Search for the employee. + Employee[] result = check ldapClient->searchWithType("dc=mycompany,dc=com", "(sn=New User)", ldap:SUB); + io:println(result); + + // Update the employee. + ldap:LdapResponse updateResponse = check ldapClient->modify("cn=User,dc=mycompany,dc=com", { "sn": "Updated User" }); + io:println(updateResponse); + + // Delete the employee. + ldap:LdapResponse response = check ldapClient->delete("CN=User,dc=mycompany,dc=com"); + io:println(response); +} + +type Employee record { + string[] objectClass; + string sn; +}; diff --git a/examples/access-directory-server/resources/docker-compose.yml b/examples/access-directory-server/resources/docker-compose.yml new file mode 100644 index 0000000..4b78124 --- /dev/null +++ b/examples/access-directory-server/resources/docker-compose.yml @@ -0,0 +1,14 @@ +version: '3.7' + +services: + ldap_server: + image: osixia/openldap:latest + container_name: my-openldap-container + environment: + LDAP_ORGANISATION: "My Company" + LDAP_DOMAIN: "mycompany.com" + LDAP_ADMIN_PASSWORD: "adminpassword" + ports: + - "389:389" + - "636:636" + command: --copy-service From 8b19f9b90ff8e77d9ce5fcf6eea4697a89b5fc9b Mon Sep 17 00:00:00 2001 From: Nuvindu Date: Mon, 15 Jul 2024 10:04:38 +0530 Subject: [PATCH 03/10] Add a Gradle build file to build examples --- examples/build.gradle | 94 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 examples/build.gradle diff --git a/examples/build.gradle b/examples/build.gradle new file mode 100644 index 0000000..37acf23 --- /dev/null +++ b/examples/build.gradle @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com) + * + * WSO2 LLC. 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. + */ + +import org.apache.tools.ant.taskdefs.condition.Os + +description = 'Ballerina - LDAP Examples' + +def ballerinaDist = "${project.rootDir}/target/ballerina-runtime" +def examples = ["access-directory-server"] +def graalvmFlag = "" + +tasks.register('clean') { + examples.forEach { example -> + delete "${projectDir}/${example}/target" + delete "${projectDir}/${example}/Dependencies.toml" + } +} + +task testExamples { + if (project.hasProperty("balGraalVMTest")) { + graalvmFlag = "--graalvm" + } + doLast { + examples.each { example -> + try { + exec { + workingDir project.projectDir + if (Os.isFamily(Os.FAMILY_WINDOWS)) { + commandLine 'cmd', '/c', "${ballerinaDist}/bin/bal.bat test ${graalvmFlag} ${example} && exit %%ERRORLEVEL%%" + } else { + commandLine 'sh', '-c', "${ballerinaDist}/bin/bal test ${graalvmFlag} ${example}" + } + } + } catch (Exception e) { + println("Example '${example}' Build failed: " + e.message) + throw e + } + } + } +} + +task buildExamples { + gradle.taskGraph.whenReady { graph -> + if (graph.hasTask(":ldap-examples:test")) { + buildExamples.enabled = false + } else { + testExamples.enabled = false + } + } + doLast { + examples.each { example -> + try { + exec { + workingDir project.projectDir + if (Os.isFamily(Os.FAMILY_WINDOWS)) { + commandLine 'cmd', '/c', "${ballerinaDist}/bin/bal.bat build ${example} && exit %%ERRORLEVEL%%" + } else { + commandLine 'sh', '-c', "${ballerinaDist}/bin/bal build ${example}" + } + } + } catch (Exception e) { + println("Example '${example}' Build failed: " + e.message) + throw e + } + } + } +} + +task build { + dependsOn buildExamples + +} + +task test { + dependsOn testExamples +} + +buildExamples.dependsOn ":ldap-ballerina:build" +testExamples.dependsOn ":ldap-ballerina:build" From 92c374a3ace0baddb52cce0a13dfbbb7b37a1284 Mon Sep 17 00:00:00 2001 From: Nuvindu Date: Mon, 15 Jul 2024 10:04:55 +0530 Subject: [PATCH 04/10] Modify Gradle files to build examples --- build.gradle | 1 + settings.gradle | 2 ++ 2 files changed, 3 insertions(+) diff --git a/build.gradle b/build.gradle index 0ba3fa9..25ff235 100644 --- a/build.gradle +++ b/build.gradle @@ -80,6 +80,7 @@ subprojects { task build { dependsOn(":ldap-native:build") dependsOn(":ldap-ballerina:build") + dependsOn(":ldap-examples:build") } def moduleVersion = project.version.replace("-SNAPSHOT", "") diff --git a/settings.gradle b/settings.gradle index 87a72f2..3c21644 100644 --- a/settings.gradle +++ b/settings.gradle @@ -36,10 +36,12 @@ rootProject.name = 'module-ballerina-ldap' include ":checkstyle" include ":${projectName}-native" include ":${projectName}-ballerina" +include ":${projectName}-examples" project(':checkstyle').projectDir = file("build-config${File.separator}checkstyle") project(":${projectName}-native").projectDir = file('native') project(":${projectName}-ballerina").projectDir = file('ballerina') +project(":${projectName}-examples").projectDir = file('examples') gradleEnterprise { buildScan { From 976a202478fde06f5ec0a478b27bb2a2ffaa7b65 Mon Sep 17 00:00:00 2001 From: Nuvindu Date: Sun, 21 Jul 2024 15:55:31 +0530 Subject: [PATCH 05/10] Add new example as a library management system --- .../library-managment-system/Ballerina.toml | 5 ++ examples/library-managment-system/main.bal | 58 +++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 examples/library-managment-system/Ballerina.toml create mode 100644 examples/library-managment-system/main.bal diff --git a/examples/library-managment-system/Ballerina.toml b/examples/library-managment-system/Ballerina.toml new file mode 100644 index 0000000..fa7ba38 --- /dev/null +++ b/examples/library-managment-system/Ballerina.toml @@ -0,0 +1,5 @@ +[package] +org = "ballerina" +name = "library_managment_system" +version = "0.1.0" +distribution = "2201.8.0" diff --git a/examples/library-managment-system/main.bal b/examples/library-managment-system/main.bal new file mode 100644 index 0000000..e8be246 --- /dev/null +++ b/examples/library-managment-system/main.bal @@ -0,0 +1,58 @@ +// Copyright (c) 2024 WSO2 LLC. (http://www.wso2.com). +// +// WSO2 LLC. 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. + +import ballerina/ldap; +import ballerina/io; + +configurable string hostName = ?; +configurable int port = ?; +configurable string domainName = ?; +configurable string password = ?; + +public function main() returns error? { + // Authenticate using the directory server credentials. + ldap:Client ldapClient = check new ({ + hostName, + port, + domainName, + password + }); + + ldap:Entry user = { + "objectClass": "inetOrgPerson", + "sn": "Alice", + "cn": "Alice", + "uid": "alice", + "displayName": "Alice Parker" + }; + + // Add a new user to the library system. + _ = check ldapClient->add("uid=alice,ou=Users,dc=library,dc=org", user); + + // Search for a book. + ldap:SearchResult result = check ldapClient->search("ou=Books,dc=library,dc=org", "(cn=Dracula)", ldap:SUB); + io:println(result.entries); + + ldap:Entry updateBook = { + "member": "uid=alice,ou=Users,dc=library,dc=org" + }; + + // Updates the book. + _ = check ldapClient->modify("cn=Dracula,ou=Books,dc=library,dc=org", updateBook); + + result = check ldapClient->search("ou=Books,dc=library,dc=org", "(cn=Dracula)", ldap:SUB); + io:println(result.entries); +} From 3754e7cd5e91f5db84f1790ef099c11617b87738 Mon Sep 17 00:00:00 2001 From: Nuvindu Date: Sun, 21 Jul 2024 15:56:02 +0530 Subject: [PATCH 06/10] Add a docker yaml file with configs for the example --- .../resources/bootstrap.ldif | 43 +++++++++++++++++++ .../resources/docker-compose.yml | 16 +++++++ 2 files changed, 59 insertions(+) create mode 100644 examples/library-managment-system/resources/bootstrap.ldif create mode 100644 examples/library-managment-system/resources/docker-compose.yml diff --git a/examples/library-managment-system/resources/bootstrap.ldif b/examples/library-managment-system/resources/bootstrap.ldif new file mode 100644 index 0000000..924de65 --- /dev/null +++ b/examples/library-managment-system/resources/bootstrap.ldif @@ -0,0 +1,43 @@ +dn: ou=Users,dc=library,dc=org +changetype: add +objectClass: organizationalUnit +ou: Users + +dn: ou=Groups,dc=library,dc=org +changetype: add +objectClass: organizationalUnit +ou: Groups + +dn: ou=Books,dc=library,dc=org +changetype: add +objectClass: organizationalUnit +ou: Books + +dn: uid=john,ou=Users,dc=library,dc=org +changetype: add +objectClass: inetOrgPerson +cn: John +givenName: John +sn: John +uid: john +displayName: John Doe +mail: johndoe@gmail.com +userPassword: johndoe@123 + +dn: cn=Users,ou=Groups,dc=library,dc=org +changetype: add +cn: Users +objectClass: groupOfNames +member: uid=john,ou=Users,dc=library,dc=org + +dn: cn=Books,ou=Groups,dc=library,dc=org +changetype: add +cn: Books +objectClass: groupOfNames +member: uid=john,ou=Users,dc=library,dc=org + +dn: cn=Dracula,ou=Books,dc=library,dc=org +changetype: add +objectClass: groupOfNames +cn: Dracula +member: uid=john,ou=Users,dc=library,dc=org diff --git a/examples/library-managment-system/resources/docker-compose.yml b/examples/library-managment-system/resources/docker-compose.yml new file mode 100644 index 0000000..c57735a --- /dev/null +++ b/examples/library-managment-system/resources/docker-compose.yml @@ -0,0 +1,16 @@ +version: '3.7' + +services: + ldap_server: + image: osixia/openldap:latest + container_name: my-openldap-container + environment: + LDAP_ORGANISATION: "Library" + LDAP_DOMAIN: "library.org" + LDAP_ADMIN_PASSWORD: "adminpassword" + ports: + - "389:389" + - "636:636" + command: --copy-service + volumes: + - ./bootstrap.ldif:/container/service/slapd/assets/config/bootstrap/ldif/50-bootstrap.ldif From 0dc4846db7c4b0ca8591ca7d56792c2e16caed25 Mon Sep 17 00:00:00 2001 From: Nuvindu Date: Sun, 21 Jul 2024 15:56:29 +0530 Subject: [PATCH 07/10] Add documentation for the example --- examples/library-managment-system/README.md | 30 +++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 examples/library-managment-system/README.md diff --git a/examples/library-managment-system/README.md b/examples/library-managment-system/README.md new file mode 100644 index 0000000..48d7655 --- /dev/null +++ b/examples/library-managment-system/README.md @@ -0,0 +1,30 @@ +# Employee Directory and Authentication System + +This example demonstrates using the Ballerina LDAP module to integrate with a directory server for managing users in a library system. The functionalities include adding new users, searching for books, and updating books. + +## Running an Example + +### 1. Start the directory server + +Run the following docker command to start the LDAP server. + +```sh +cd resources +docker-compose up +``` + +### 2. Run the Ballerina project + +Execute the following commands to build an example from the source: + +* To build an example: + + ```bash + bal build + ``` + +* To run an example: + + ```bash + bal run + ``` From 254b1cb47c7c9f5e3b19363e6e71c86bad51ac17 Mon Sep 17 00:00:00 2001 From: Nuvindu Date: Sun, 21 Jul 2024 16:27:19 +0530 Subject: [PATCH 08/10] Add symlinks to the examples --- examples/access-directory-server/.github/README.md | 1 + .../{README.md => access-directory-server.md} | 0 examples/library-managment-system/.github/README.md | 1 + .../{README.md => library-managment-system.md} | 0 4 files changed, 2 insertions(+) create mode 120000 examples/access-directory-server/.github/README.md rename examples/access-directory-server/{README.md => access-directory-server.md} (100%) create mode 120000 examples/library-managment-system/.github/README.md rename examples/library-managment-system/{README.md => library-managment-system.md} (100%) diff --git a/examples/access-directory-server/.github/README.md b/examples/access-directory-server/.github/README.md new file mode 120000 index 0000000..dcd056d --- /dev/null +++ b/examples/access-directory-server/.github/README.md @@ -0,0 +1 @@ +../access-directory-server.md \ No newline at end of file diff --git a/examples/access-directory-server/README.md b/examples/access-directory-server/access-directory-server.md similarity index 100% rename from examples/access-directory-server/README.md rename to examples/access-directory-server/access-directory-server.md diff --git a/examples/library-managment-system/.github/README.md b/examples/library-managment-system/.github/README.md new file mode 120000 index 0000000..db6b39b --- /dev/null +++ b/examples/library-managment-system/.github/README.md @@ -0,0 +1 @@ +../library-managment-system.md \ No newline at end of file diff --git a/examples/library-managment-system/README.md b/examples/library-managment-system/library-managment-system.md similarity index 100% rename from examples/library-managment-system/README.md rename to examples/library-managment-system/library-managment-system.md From ef49bfde0b7443dc681fd9d6575d3e2a372756a1 Mon Sep 17 00:00:00 2001 From: Nuvindu Date: Wed, 31 Jul 2024 11:36:37 +0530 Subject: [PATCH 09/10] Apply review suggestions --- examples/access-directory-server/main.bal | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/access-directory-server/main.bal b/examples/access-directory-server/main.bal index 6316f88..5d5e3f3 100644 --- a/examples/access-directory-server/main.bal +++ b/examples/access-directory-server/main.bal @@ -23,6 +23,11 @@ configurable string domainName = ?; configurable string password = ?; configurable string userDN = ?; +type Employee record { + string[] objectClass; + string sn; +}; + public function main() returns error? { // Authenticate using the directory server credentials. ldap:Client ldapClient = check new ({ @@ -53,8 +58,3 @@ public function main() returns error? { ldap:LdapResponse response = check ldapClient->delete("CN=User,dc=mycompany,dc=com"); io:println(response); } - -type Employee record { - string[] objectClass; - string sn; -}; From 5735344f75b2674458fc7a97da34fa09c2aa6e19 Mon Sep 17 00:00:00 2001 From: Nuvindu Date: Wed, 7 Aug 2024 19:00:22 +0530 Subject: [PATCH 10/10] Remove unnecessary comments in examples --- examples/access-directory-server/main.bal | 1 - examples/library-managment-system/main.bal | 1 - 2 files changed, 2 deletions(-) diff --git a/examples/access-directory-server/main.bal b/examples/access-directory-server/main.bal index 5d5e3f3..b8e8523 100644 --- a/examples/access-directory-server/main.bal +++ b/examples/access-directory-server/main.bal @@ -29,7 +29,6 @@ type Employee record { }; public function main() returns error? { - // Authenticate using the directory server credentials. ldap:Client ldapClient = check new ({ hostName, port, diff --git a/examples/library-managment-system/main.bal b/examples/library-managment-system/main.bal index e8be246..a3e2c30 100644 --- a/examples/library-managment-system/main.bal +++ b/examples/library-managment-system/main.bal @@ -23,7 +23,6 @@ configurable string domainName = ?; configurable string password = ?; public function main() returns error? { - // Authenticate using the directory server credentials. ldap:Client ldapClient = check new ({ hostName, port,