Active Directory/Connect

From Rosetta Code
Revision as of 14:22, 26 September 2021 by PureFox (talk | contribs) (Added Wren)
Task
Active Directory/Connect
You are encouraged to solve this task according to the task description, using any language you may know.

The task is to establish a connection to an Active Directory or Lightweight Directory Access Protocol server.

AutoHotkey

Works with: AutoHotkey_L
Translation of: VBScript

<lang AutoHotkey>objConn := CreateObject("ADODB.Connection") objCmd := CreateObject("ADODB.Command") objConn.Provider := "ADsDSOObject" objConn.Open()</lang>

AutoIt

Works with: AutoIt

<lang AutoIt> #include <AD.au3> _AD_Open()</lang>

C

With OpenLDAP: <lang C>#include <ldap.h> ... char *name, *password; ... LDAP *ld = ldap_init("ldap.somewhere.com", 389); ldap_simple_bind_s(ld, name, password); ... after done with it... ldap_unbind(ld);</lang>

C#

<lang csharp> // Requires adding a reference to System.DirectoryServices var objDE = new System.DirectoryServices.DirectoryEntry("LDAP://DC=onecity,DC=corp,DC=fabrikam,DC=com"); </lang>

ColdFusion

<lang cfm> <cfldap server = "#someip#" action="query" start="somestart#" username = "#someusername#" password = "#somepassowrd#" name = "results" scope="subtree" attributes = "#attributeslist#" > </lang>

D

Based on dopenldap. <lang d> import openldap; import std.stdio;

void main() {

 auto ldap = LDAP("ldap://localhost");
 auto r = ldap.search_s("dc=example,dc=com", LDAP_SCOPE_SUBTREE, "(uid=%s)".format("test"));
 int b = ldap.bind_s(r[0].dn, "password");
 scope(exit) ldap.unbind;
 if (b)
 {
   writeln("error on binding");
   return;
 }
 // do something
 ...
   

} </lang>

Erlang

This needs a test case. Is there a LDAP server available? <lang Erlang> -module(ldap_example). -export( [main/1] ).

main( [Host, DN, Password] ) ->

{ok, Handle} = eldap:open( [Host] ),
ok = eldap:simple_bind( Handle, DN, Password ),
eldap:close( Handle ).

</lang>

F#

Translation of: C_sharp

For Active Directory we use the library System.DirectoryServices

<lang fsharp>let adObject = new System.DirectoryServices.DirectoryEntry("LDAP://DC=onecity,DC=corp,DC=fabrikam,DC=com")</lang>

For your average LDAP server we use System.DirectoryServices.Protocol

For a minimal example we make an anonymous connect to the local machine on the well-known LDAP port 389 <lang fsharp>let ldapServer = new System.DirectoryServices.Protocols.LdapDirectoryIdentifier("127.0.0.1") let connect = new System.DirectoryServices.Protocols.LdapConnection(ldapServer) connect.Bind()</lang>

Go


There are a large number of third-party LDAP libraries for Go. This uses one of the simpler ones and the code below is largely taken from the example on its main page. <lang go>package main

import (

   "log"
   "github.com/jtblin/go-ldap-client"

)

func main() {

   client := &ldap.LDAPClient{
       Base:         "dc=example,dc=com",
       Host:         "ldap.example.com",
       Port:         389,
       UseSSL:       false,
       BindDN:       "uid=readonlyuser,ou=People,dc=example,dc=com",
       BindPassword: "readonlypassword",
       UserFilter:   "(uid=%s)",
       GroupFilter:  "(memberUid=%s)",
       Attributes:   []string{"givenName", "sn", "mail", "uid"},
   }
   defer client.Close()
   err := client.Connect()
   if err != nil { 
       log.Fatalf("Failed to connect : %+v", err)
   }
   // Do something

}</lang>

Haskell

Example uses the ldap-client package:

<lang haskell>{-# LANGUAGE OverloadedStrings #-}

module Main (main) where

import Data.Foldable (for_) import qualified Data.Text.Encoding as Text (encodeUtf8) import Ldap.Client (Attr(..), Filter(..)) import qualified Ldap.Client as Ldap (Dn(..), Host(..), search, with, typesOnly)

main :: IO () main = do

   entries <- Ldap.with (Ldap.Plain "localhost") 389 $ \ldap ->
       Ldap.search ldap (Ldap.Dn "o=example.com") (Ldap.typesOnly True) (Attr "uid" := Text.encodeUtf8 "user") []
   for_ entries $ \entry ->
       print entry</lang>

Java

This code uses the Apache Directory third-party library.

<lang java>import java.io.IOException; import org.apache.directory.api.ldap.model.exception.LdapException; import org.apache.directory.ldap.client.api.LdapConnection; import org.apache.directory.ldap.client.api.LdapNetworkConnection;

public class LdapConnectionDemo {

   public static void main(String[] args) throws LdapException, IOException {
       try (LdapConnection connection = new LdapNetworkConnection("localhost", 10389)) {
           connection.bind();
           connection.unBind();
       }
   }

}</lang>


Julia

<lang julia>using LDAPClient

conn = LDAPClient.LDAPConnection("ldap://localhost:10389") LDAPClient.simple_bind(conn, "user", "password") LDAPClient.unbind(conn) </lang>

Kotlin

<lang scala> import org.apache.directory.api.ldap.model.exception.LdapException import org.apache.directory.ldap.client.api.LdapNetworkConnection import java.io.IOException import java.util.logging.Level import java.util.logging.Logger

class LDAP(map: Map<String, String>) {

   fun run() {
       var connection: LdapNetworkConnection? = null
       try {
           if (info) log.info("LDAP Connection to $hostname on port $port")
           connection = LdapNetworkConnection(hostname, port.toInt())
           try {
               if (info) log.info("LDAP bind")
               connection.bind()
           } catch (e: LdapException) {
               log.severe(e.toString())
           }
           try {
               if (info) log.info("LDAP unbind")
               connection.unBind()
           } catch (e: LdapException) {
               log.severe(e.toString())
           }
       } finally {
           try {
               if (info) log.info("LDAP close connection")
               connection!!.close()
           } catch (e: IOException) {
               log.severe(e.toString())
           }
       }
   }
   private val log = Logger.getLogger(LDAP::class.java.name)
   private val info = log.isLoggable(Level.INFO)
   private val hostname: String by map
   private val port: String by map

}

fun main(args: Array<String>) = LDAP(mapOf("hostname" to "localhost", "port" to "10389")).run() </lang>

NetRexx

Uses the Apache LDAP API, connecting to a local ApacheDS LDAP directory server. <lang NetRexx>/* NetRexx */ options replace format comments java crossref symbols binary

import org.apache.directory.ldap.client.api.LdapConnection import org.apache.directory.ldap.client.api.LdapNetworkConnection import org.apache.directory.shared.ldap.model.exception.LdapException import org.slf4j.Logger import org.slf4j.LoggerFactory

class RDirectoryLDAP public

 properties constant
   log_ = LoggerFactory.getLogger(RDirectoryLDAP.class)
 properties private static
   connection = LdapConnection null
 method main(args = String[]) public static
   ldapHostName = String "localhost"
   ldapPort = int 10389
   if log_.isInfoEnabled() then log_.info("LDAP Connection to" ldapHostName "on port" ldapPort)
   connection = LdapNetworkConnection(ldapHostName, ldapPort)
   do
     if log_.isTraceEnabled() then log_.trace("LDAP bind")
     connection.bind()
     if log_.isTraceEnabled() then log_.trace("LDAP unbind")
     connection.unBind()
   catch lex = LdapException
     log_.error("LDAP Error", Throwable lex)
   catch iox = IOException
     log_.error("I/O Error", Throwable iox)
   finally
     do
     if connection \= null then connection.close()
     catch iox = IOException
       log_.error("I/O Error on connection.close()", Throwable iox)
     end
   end
   return

</lang>

Sample log4j.xml configuration file: <lang xml><?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd"> <log4j:configuration xmlns:log4j='http://jakarta.apache.org/log4j/'>

 <appender name="stdout" class="org.apache.log4j.ConsoleAppender">
   <param name="Target" value="System.out" />
   <layout class="org.apache.log4j.PatternLayout">
     <param name="ConversionPattern" value="[%d{HH:mm:ss}] %-5p [%c] - %m%n" />
   </layout>
 </appender>
 <logger name="org.apache.directory.shared.ldap.name">
   <level value="warn" />
 </logger>
 <logger name="org.apache.directory.shared.codec">
   <level value="warn" />
 </logger>
 <logger name="org.apache.directory.shared.asn1">
   <level value="warn" />
 </logger>
 <root>
   <level value="info" />
   <appender-ref ref="stdout" />
 </root>

</log4j:configuration> </lang>

Output:

[08:40:05] INFO  [RDirectoryLDAP] - LDAP Connection to localhost on port 10389

Perl

LDAP Modules <lang perl> use Net::LDAP;

my $ldap = Net::LDAP->new('ldap://ldap.example.com') or die $@; my $mesg = $ldap->bind( $bind_dn, password => $bind_pass ); </lang>

Phix

Translation of: C

Note that builtins\ldap.e is incomplete, windows-only (depends on wldap32.dll), and largely untested. This has been tested against a random 7-year-old list of public ldap servers, getting mixed errors of LDAP_SERVER_DOWN/LDAP_INVALID_CREDENTIALS/LDAP_INVALID_DN_SYNTAX/LDAP_CONFIDENTIALITY_REQUIRED.

include builtins/ldap.e
 
constant servers = {
"ldap.somewhere.com",
}
--...
string name="name", password="passwd"
--...
for i=1 to length(servers) do
    atom ld = ldap_init(servers[i])
    integer res = ldap_simple_bind_s(ld, name, password)
    printf(1,"%s: %d [%s]\n",{servers[i],res,ldap_err_desc(res)})
    --... after done with it...
    ldap_unbind(ld)
end for
Output:
ldap.somewhere.com: 81 [LDAP_SERVER_DOWN]

PHP

PHP LDAP Reference <lang php><?php $ldap = ldap_connect($hostname, $port); $success = ldap_bind($ldap, $username, $password);</lang>

PicoLisp

<lang PicoLisp>(unless (=0 (setq Ldap (native "libldap.so" "ldap_open" 'N "example.com" 389)))

  (quit "Can't open LDAP") )

(native "libldap.so" "ldap_simple_bind_s" 'I Ldap "user" "password")</lang>

Python

Works with: Python version 2.6
Library: python-ldap

python-ldap Documentation

<lang python>import ldap

l = ldap.initialize("ldap://ldap.example.com") try:

   l.protocol_version = ldap.VERSION3
   l.set_option(ldap.OPT_REFERRALS, 0)
   bind = l.simple_bind_s("me@example.com", "password")

finally:

   l.unbind()

</lang>

Racket

This version uses the ldap package, and was tested against OpenLDAP (with real values): <lang racket>#lang racket (require net/ldap) (ldap-authenticate "ldap.somewhere.com" 389 "uid=username,ou=people,dc=somewhere,dc=com" password)</lang>

Translation of: C

This is a direct translation of the C code -- I have no idea how to try it out since I don't have a working ldap server... So take it as a stub that waits for someone who can try it to do so. (And it's a low level thing anyway, there's an ldap package for Racket which I can't try for a similar reason.)

<lang racket>#lang racket

(require ffi/unsafe ffi/unsafe/define)

(define-ffi-definer defldap (ffi-lib "libldap")) (defldap ldap_init (_fun _string _int -> _pointer)) (defldap ldap_unbind (_fun _pointer -> _void)) (defldap ldap_simple_bind_s (_fun _pointer _string _string -> _int)) (defldap ldap_err2string (_fun _int -> _string))

(define name ...) (define password ...) (define ld (ldap_init "ldap.somewhere.com" 389)) (ldap_simple_bind_s ld name password)

(ldap_unbind ld)</lang>

Raku

(formerly Perl 6)

Using module LMDB - bindings to the openLDAP library. Requires an LDAP instance.

<lang perl6>use LMDB;

my %DB := LMDB::DB.open(:path<some-dir>, %connection-parameters); </lang>

%DB may be accessed, read from and written to like a native hash.

Ruby

Similar to Tcl, assume the AD server talks LDAP.

There are many Ruby LDAP packages ([1]) -- this solution uses Net::LDAP ("Pure Ruby LDAP Tools" on RubyForge, gem name "ruby-net-ldap")

Library: RubyGems

<lang ruby>require 'rubygems' require 'net/ldap' ldap = Net::LDAP.new(:host => 'ldap.example.com', :base => 'o=companyname') ldap.authenticate('bind_dn', 'bind_pass')</lang>

Run BASIC

This example is incorrect. Please fix the code and remove this message.

Details: Active Directory has nothing to do with the local file system

<lang runbasic>print shell$("dir") ' shell out to the os and print it</lang>

Rust

This solution uses the popular ldap3 crate. <lang rust> let conn = ldap3::LdapConn::new("ldap://ldap.example.com")?; conn.simple_bind("bind_dn", "bind_pass")?.success()?; </lang>

Scala

 <lang scala>import java.io.IOException

import org.apache.directory.api.ldap.model.exception.LdapException import org.apache.directory.ldap.client.api.{LdapConnection, LdapNetworkConnection}

object LdapConnectionDemo {

 @throws[LdapException]
 @throws[IOException]
 def main(args: Array[String]): Unit = {
   try {
     val connection: LdapConnection = new LdapNetworkConnection("localhost", 10389)
     try {
       connection.bind()
       connection.unBind()
     } finally if (connection != null) connection.close()
   }
 }

}</lang>

smart BASIC

This example is incorrect. Please fix the code and remove this message.

Details: Active Directory has nothing to do with the local file system

smart BASIC uses three separate commands to list the current directory, folder and files respectively. <lang qbasic>PRINT "Current directory: ";CURRENT_DIR$() PRINT PRINT "Folders:" PRINT DIR "/" LIST DIRS a$,c FOR n = 0 TO c-1 PRINT ,a$(n) NEXT n PRINT PRINT "Files:" PRINT DIR "/" LIST FILES a$,c FOR n = 0 TO c-1 PRINT ,a$(n) NEXT n</lang>

Tcl

This does not use SSPI/Kerberos yet, so your AD would need to allow simple ldap access. <lang tcl>package require ldap set conn [ldap::connect $host $port] ldap::bind $conn $user $password</lang>

VBScript

Creating the normal connection to AD <lang vbscript>Set objConn = CreateObject("ADODB.Connection") Set objCmd = CreateObject("ADODB.Command") objConn.Provider = "ADsDSOObject" objConn.Open</lang>

Wren

Translation of: C
Library: OpenLDAP

As it's not currently possible for Wren-cli to access OpenLDAP directly, we embed a Wren script in a C application to complete this task. <lang ecmascript>/* active_directory_connect.wren */

foreign class LDAP {

   construct init(host, port) {}
   foreign simpleBindS(name, password)
   foreign unbind()

}

class C {

   foreign static getInput(maxSize)

}

var name = "" while (name == "") {

   System.write("Enter name : ")
   name = C.getInput(40)

}

var password = "" while (password == "") {

   System.write("Enter password : ")
   password = C.getInput(40)

}

var ld = LDAP.new("ldap.somewhere.com", 389) ld.simpleBindS(ld, name, password)

// do something here

ld.unbind()</lang>
We now embed this in the following C program, compile and run it. <lang c>#include <stdio.h>

  1. include <stdio_ext.h>
  2. include <stdlib.h>
  3. include <string.h>
  4. include <ldap.h>
  5. include "wren.h"

/* C <=> Wren interface functions */

void C_ldapAllocate(WrenVM* vm) {

   LDAP** pldap = (LDAP**)wrenSetSlotNewForeign(vm, 0, 0, sizeof(LDAP*));
   char *host = (char *)wrenGetSlotString(vm, 1);
   int port = (int)wrenGetSlotDouble(vm, 2);
   *pldap = ldap_init(host, port);

}

void C_simpleBindS(WrenVM* vm) {

   LDAP* ldap = *(LDAP**)wrenGetSlotForeign(vm, 0};
   const char *name = wrenGetSlotString(vm, 1);
   const char *password = wrenGetSlotString(vm, 2);
   ldap_simple_bind_s(ldap, name, password);

}

void C_unbind(WrenVM* vm) {

   LDAP* ldap = *(LDAP**)wrenGetSlotForeign(vm, 0};
   ldap_unbind(ldap);

}

void C_getInput(WrenVM* vm) {

   int maxSize = (int)wrenGetSlotDouble(vm, 1) + 2;
   char input[maxSize];
   fgets(input, maxSize, stdin);
   __fpurge(stdin);
   input[strcspn(input, "\n")] = 0;
   wrenSetSlotString(vm, 0, (const char*)input);

}

WrenForeignClassMethods bindForeignClass(WrenVM* vm, const char* module, const char* className) {

   WrenForeignClassMethods methods;
   methods.finalize = NULL;
   if (strcmp(className, "LDAP") == 0) {
       methods.allocate = C_ldapAllocate;
   } 
   return methods;

}

WrenForeignMethodFn bindForeignMethod(

   WrenVM* vm,
   const char* module,
   const char* className,
   bool isStatic,
   const char* signature) {
   if (strcmp(module, "main") == 0) {
       if (strcmp(className, "LDAP") == 0) {
           if (!isStatic && strcmp(signature, "simpleBindS(_)") == 0) return C_simpleBindS;
           if (!isStatic && strcmp(signature, "unbind()") == 0) return C_unbind;
       } else if (strcmp(className, "C") == 0) {
           if (isStatic && strcmp(signature, "getInput(_)") == 0) return C_getInput;
       }
   }
   return NULL;

}

static void writeFn(WrenVM* vm, const char* text) {

   printf("%s", text);

}

void errorFn(WrenVM* vm, WrenErrorType errorType, const char* module, const int line, const char* msg) {

   switch (errorType) {
       case WREN_ERROR_COMPILE:
           printf("[%s line %d] [Error] %s\n", module, line, msg);
           break;
       case WREN_ERROR_STACK_TRACE:
           printf("[%s line %d] in %s\n", module, line, msg);
           break;
       case WREN_ERROR_RUNTIME:
           printf("[Runtime Error] %s\n", msg);
           break;
   }

}

char *readFile(const char *fileName) {

   FILE *f = fopen(fileName, "r");
   fseek(f, 0, SEEK_END);
   long fsize = ftell(f);
   rewind(f);
   char *script = malloc(fsize + 1);
   fread(script, 1, fsize, f);
   fclose(f);
   script[fsize] = 0;
   return script;

}

int main(int argc, char **argv) {

   WrenConfiguration config;
   wrenInitConfiguration(&config);
   config.writeFn = &writeFn;
   config.errorFn = &errorFn;
   config.bindForeignClassFn = &bindForeignClass;
   config.bindForeignMethodFn = &bindForeignMethod;
   WrenVM* vm = wrenNewVM(&config);
   const char* module = "main";
   const char* fileName = "active_directory_connect.wren";
   char *script = readFile(fileName);
   WrenInterpretResult result = wrenInterpret(vm, module, script);
   switch (result) {
       case WREN_RESULT_COMPILE_ERROR:
           printf("Compile Error!\n");
           break;
       case WREN_RESULT_RUNTIME_ERROR:
           printf("Runtime Error!\n");
           break;
       case WREN_RESULT_SUCCESS:
           break;
   }
   wrenFreeVM(vm);
   free(script);
   return 0;

}</lang>