Skip to content

Commit

Permalink
Cleanup Documents
Browse files Browse the repository at this point in the history
  • Loading branch information
RockfordWei committed Jan 31, 2017
1 parent e16dad3 commit dde4c61
Show file tree
Hide file tree
Showing 3 changed files with 384 additions and 71 deletions.
196 changes: 148 additions & 48 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ This package builds with Swift Package Manager and is part of the [Perfect](http

Ensure you have installed and activated the latest Swift 3.0 tool chain.

*Caution*: for the reason that LDAP is widely using in many different operating systems with variable implementations, API marked with (⚠️EXPERIMENTAL⚠️) indicates that this method might not be fully applicable to certain context. However, as an open source software library, you may modify the source code to meet a specific requirement.

## Quick Start

Add the following dependency to your project's Package.swift file:
Expand All @@ -59,9 +61,63 @@ Then import PerfectLDAP to your source code:
import PerfectLDAP
```

## Connect to LDAP Server

You can create actual connections as need with or without login credential. The full API is `LDAP(url:String, loginData: Login?, codePage: Iconv.CodePage)`. The `codePage` option is for those servers applying character set other than .UTF8, e.g., set `codePage: .GB2312` to connect to LDAP server in Simplified Chinese.

### TLS Option

PerfectLDAP provides TLS options for network security considerations, i.e, you can choose either `ldap://` or `ldaps://` for connections, as demo below:

``` swift
// this will connect to a 389 port without any encryption
let ld = try LDAP(url: "ldap://perfect.com")
```
or,

``` swift
// this will connect to a 636 port with certificates
let ld = try LDAP(url: "ldaps://perfect.com")
```

### Connection Timeout

Once connected, LDAP object could be set with timeout option, and the timing unit is second:

``` swift
// set the timeout for communication. In this example, connection will be timeout in ten seconds.
connection.timeout = 10
```

### Login or Anonymous

Many servers mandate login before performing any actual LDAP operations, however, PerfectLDAP provides multiple login options as demo below:

``` swift
// this snippet demonstrate how to connect to LDAP server with a login credential
// NOTE: this kind of connection will block the thread until server return or timeout.
// create login credential
let credential = LDAP.login( ... )
let connection = try LDAP(url: "ldaps://...", loginData: login)
```
Aside the above synchronous login option, a two phased threading login process could also bring more controls to the application:

``` swift
// first create a connection
let connection = try LDAP(url: "ldaps:// ...")

// setup login info
let credential = LDAP.login( ... )

// login in a separated thread
connection.login(info: credential) { err in
// if err is not nil, then something must be wrong in the login process.
}
```

## Login Options

PerfectLDAP provides a special object called `LDAP.login` to store essential account information for LDAP connections and the form of constructor is subject to the authentication types:
PerfectLDAP provides a special object called `LDAP.Login` to store essential account information for LDAP connections and the form of constructor is subject to the authentication types:

### Simple Login

Expand All @@ -71,15 +127,15 @@ To use simple login method, simply call `LDAP.login(binddn: String, password: St
let credential = LDAP.Login(binddn: "CN=judy,CN=Users,DC=perfect,DC=com", password: "0penLDAP")
```

### Digest-MD5 (*EXPERIMENTAL*)
### Digest-MD5 (⚠️EXPERIMENTAL⚠️)

To apply Digest-MD5 interactive login, call `LDAP.login(authname: String, user: String, password: String, realm: String)` as demo below:
``` swift
let credential = LDAP.Login(authname: "judy", user: "DN:CN=judy,CN=Users,DC=perfect,DC=com", password: "0penLDAP", realm: "PERFECT.COM")
```
*NOTE* The `authname` is equivalent to `SASL_CB_AUTHNAME` and `user` is actually the macro of `SASL_CB_USER`. If any parameter above is not applicable to your case, simply assign an empty string "" to ignore it.
*⚠️NOTE⚠️* The `authname` is equivalent to `SASL_CB_AUTHNAME` and `user` is actually the macro of `SASL_CB_USER`. If any parameter above is not applicable to your case, simply assign an empty string "" to ignore it.

### GSSAPI and GSS-SPNEGO (*EXPERIMENTAL*)
### GSSAPI and GSS-SPNEGO (⚠️EXPERIMENTAL⚠️)

To apply GSSAPI / GSS-SPNEGO authentication, call `LDAP.login(mechanism: AuthType)` to construct a login credential:

Expand All @@ -94,82 +150,126 @@ or
let credential = LDAP.login(mechanism: .SPNEGO)
```

## Connect to LDAP Server
## Search

You can create actual connections as need with or without login credential. The full API is `LDAP(url:String, loginData: Login?, codePage: Iconv.CodePage)`.
PerfectLDAP provides asynchronous and synchronous version of searching API with the same parameters:

### TLS Option
### Synchronous Search

You can choose either `ldap://` or `ldaps://` for connections, as demo below:
Synchronous search will block the thread until server returns, the full api is `LDAP.search(base:String, filter:String, scope:Scope, attributes: [String], sortedBy: String) throws -> [String:[String:Any]]`. Here is an example:

``` swift
// this will connect to a 389 port without any encryption
let ld = try LDAP(url: "ldap://perfect.com")
// perform an ldap search synchronously, which will return a full set of attributes
// with a natural (unsorted) order, in form of a dictionary.
let res = try connection.search(base: "CN=Users,DC=perfect,DC=com", filter:"(objectclass=*)")

print(res)
```
or,

### Asynchronous Search

Asynchronous search allows performing search in an independent thread. Once completed, the thread will call back with the result set in a dictionary. Full api of asynchronous search is `LDAP.search(base:String, filter:String, scope:Scope, attributes: [String], sortedBy: String, completion: @escaping ([String:[String:Any]])-> Void)`. The equivalent example is:

``` swift
// this will connect to a 636 port with certificates
let ld = try LDAP(url: "ldaps://perfect.com")
// perform an ldap search asynchronously, which will return a full set of attributes
// with a natural (unsorted) order, in form of a dictionary.
connection.search(base: "CN=Users,DC=perfect,DC=com", filter:"(objectclass=*)") {
res in
print(res)
}
```

### Login or Anonymous
### Parameters of Search
- base: String, search base domain (dn), default = ""
- filter: String, the filter of query, default is `"(objectclass=*)"`, means all possible results
- scope: Searching Scope, i.e., .BASE, .SINGLE_LEVEL, .SUBTREE or .CHILDREN
- sortedBy: a sorting string, may also be generated by `LDAP.sortingString()`
- completion: callback with a parameter of dictionary, empty if failed

Connection with login credential will block the main thread until timeout.
#### Server Side Sort (⚠️EXPERIMENTAL⚠️)
The `sortedBy` parameters is a string that indicates the remote server to perform search with a sorted set. PerfectLDAP provides a more verbal way to build such a string, i.e, an array of tuples to describe what attributes would control the result set:

``` swift
// this snippet demonstrate how to connect to LDAP server with a login credential
// create login credential
let url = "ldaps://..."
let credential = LDAP.login( ... )
let connection = try LDAP(url: url, loginData: login)
// each tuple consists two parts: the sorting field and its order - .ASC or .DSC
let sort = LDAP.sortingString(sortedBy: [("description", .ASC)])
```
However, a two phased threading login process could also bring more controls to the application:

### Limitation of Searching Result

Once connected, LDAP object could be set with an limitation option - `LDAP.limitation`. It is an integer which specifies the maximum number of entries that can be returned on a search operation.

``` swift
// first create a connection
let connection = try LDAP(url: "ldaps:// ...")
// set the limitation for searching result set. In this example, only the first 1000 entries will return.
connection.limitation = 1000
```

// set the timeout for communication. In this example, connection will be timeout in ten seconds.
connection.timeout = 10
## Attribute Operations

// setup login info
let credential = LDAP.login( ... )
PerfectLDAP provides add() / modify() and delete() for attributes operations with both synchronous and asynchronous options.

// login in a separated thread
connection.login(info: credential) { err in
// if err is not nil, then something must be wrong in the login process.
### Add Attributes (⚠️EXPERIMENTAL⚠️)

Function `LDAP.add()` can add attributes to a specific DN with parameters below:
- distinguishedName: String, specific DN
- attributes:[String:[String]], attributes as an dictionary to add. In this dictionary, every attribute, as a unique key in the dictionary, could have a series of values as an array.

Both asynchronous add() and synchronous add() share the same parameters above, take example:

``` swift
// try an add() synchronously.
do {
try connection.add(distinguishedName: "CN=judy,CN=User,DC=perfect,DC=com", attributes: ["mail":["[email protected]", "[email protected]"]])
}catch (let err) {
// failed for some reason
}

// try and add() asynchronously:
connection.add(distinguishedName: "CN=judy,CN=User,DC=perfect,DC=com", attributes: ["mail":["[email protected]", "[email protected]"]]) { err in
// if nothing wrong, err will be nil
}
```

## Search
### Modify Attributes

PerfectLDAP provides asynchronous and synchronous version of searching API with the same parameters:
Function `LDAP.modify()` can modify attributes from a specific DN with parameters below:
- distinguishedName: String, specific DN
- attributes:[String:[String]], attributes as an dictionary to modify. In this dictionary, every attribute, as a unique key in the dictionary, could have a series of values as an array.

### Synchronous Search
Both asynchronous modify() and synchronous modify() share the same parameters above, take example:

``` swift
LDAP.search(base:String, filter:String, scope:Scope, attributes: [String], sortedBy: String) throws -> [String:[String:Any]]
```
// try an modify() synchronously.
do {
try connection.modify(distinguishedName: "CN=judy,CN=User,DC=perfect,DC=com", attributes: ["codePage":["437"]])
}catch (let err) {
// failed for some reason
}

### Asynchronous Search
``` swift
LDAP.search(base:String, filter:String, scope:Scope, attributes: [String], sortedBy: String, , completion: @escaping ([String:[String:Any]])-> Void)
// try and modify() asynchronously:
connection.modify(distinguishedName: "CN=judy,CN=User,DC=perfect,DC=com", attributes:["codePage":["437"]]) { err in
// if nothing wrong, err will be nil
}
```

### Parameters of Search
- base: String, search base domain (dn), default = ""
- filter: String, the filter of query, default = "(objectclass=*)", means all possible results
- scope: Searching Scope, i.e., .BASE, .SINGLE_LEVEL, .SUBTREE or .CHILDREN
- sortedBy: a sorting string, may also be generated by LDAP.sortingString()
- completion: callback with a parameter of dictionary, empty if failed
### Delete Attributes (⚠️EXPERIMENTAL⚠️)

### Server Side Sort (*EXPERIMENTAL*)
The `sortedBy` parameters is a string that indicates the remote server to perform search with a sorted set. PerfectLDAP provides a more verbal way to build such a string, i.e, an array of tuples to describe what attributes would control the result set:
Function `LDAP.delete()` can delete attributes from a specific DN with only one parameter:
- distinguishedName: String, specific DN

Both asynchronous delete() and synchronous delete() share the same parameter above, take example:

``` swift
// each tuple consists two parts: the sorting field and its order - .ASC or .DSC
let sort = LDAP.sortingString(sortedBy: [("description", .ASC)])
// try an delete() synchronously.
do {
try connection.delete(distinguishedName: "CN=judy,CN=User,DC=perfect,DC=com")
}catch (let err) {
// failed for some reason
}

// try and delete() asynchronously:
connection.delete(distinguishedName: "CN=judy,CN=User,DC=perfect,DC=com") { err in
// if nothing wrong, err will be nil
}
```

## Issues
Expand Down
Loading

0 comments on commit dde4c61

Please sign in to comment.