You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by ap...@apache.org on 2013/07/25 14:28:34 UTC

[1/4] Merge LDAPPlugin

Updated Branches:
  refs/heads/ldapplugin 1f64354ec -> eaa414337


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/ui/index.jsp
----------------------------------------------------------------------
diff --git a/ui/index.jsp b/ui/index.jsp
index af5fa37..813dbcb 100644
--- a/ui/index.jsp
+++ b/ui/index.jsp
@@ -238,10 +238,10 @@ under the License.
             <!-- Step 6: Network -->
             <div class="step network always-load" wizard-step-id="network">
               <!-- 5a: Network description -->
-              <div class="wizard-step-conditional nothing-to-select">     
+              <div class="wizard-step-conditional nothing-to-select">
 								<p id="from_instance_page_1"><fmt:message key="message.zone.no.network.selection"/></p>
 								<p id="from_instance_page_2"><fmt:message key="message.please.proceed"/></p>
-								<p id="from_vpc_tier"></p>     										
+								<p id="from_vpc_tier"></p>
               </div>
 
               <!-- 5b: Select network -->
@@ -432,7 +432,7 @@ under the License.
                       <a href="5"><fmt:message key="label.edit"/></a>
                     </div>
                   </div>
-                  
+
                   <!-- Security groups -->
                   <div class="select odd">
                     <div class="name">
@@ -469,6 +469,37 @@ under the License.
           <div class="button next"><span><fmt:message key="label.next"/></span></div>
         </div>
       </div>
+      <!-- Accounts wizard -->
+      <div class="multi-wizard accounts-wizard">
+        <form>
+          <div class="steps">
+              <div class="content">
+                <div class="select-container ldap-account-choice">
+                  <table>
+                    <thead>
+                      <tr>
+                        <th style="width:40px">Select</th>
+                        <th style="width:110px">Realname</th>
+                        <th style="width:70px">Username</th>
+                        <th>Email</th>
+                      </tr>
+                    </thead>
+                    <tbody>
+                    </tbody>
+                  </table>
+                </div>
+              </div>
+              <div class="content input-area">
+                <div class="select-container manual-account-details">
+                </div>
+              </div>
+          </div>
+        </form>
+        <div class="buttons">
+          <div class="button cancel"><span><fmt:message key="label.cancel"/></span></div>
+          <div class="button next"><span><fmt:message key="label.add"/></span></div>
+        </div>
+      </div>
       <!-- Zone wizard -->
       <div class="multi-wizard zone-wizard">
         <div class="progress">
@@ -505,7 +536,7 @@ under the License.
                   </div>
                   <div class="select-area advanced-zone disabled">
                     <div class="desc">
-                      <fmt:message key="message.desc.advanced.zone"/>  
+                      <fmt:message key="message.desc.advanced.zone"/>
 										</div>
                     <input type="radio" name="network-model" value="Advanced" />
                     <label><fmt:message key="label.advanced"/></label>
@@ -533,7 +564,7 @@ under the License.
           <div class="setup-zone" zone-wizard-form="zone"
                zone-wizard-step-id="addZone">
             <div class="info-desc">
-              <fmt:message key="message.desc.zone"/> 
+              <fmt:message key="message.desc.zone"/>
 						</div>
             <div class="content input-area">
               <div class="select-container"></div>
@@ -752,7 +783,7 @@ under the License.
               <li class="secondary-storage"><fmt:message key="label.secondary.storage"/></li>
             </ul>
             <div class="info-desc">
-              <fmt:message key="message.desc.host"/>         
+              <fmt:message key="message.desc.host"/>
 						</div>
             <div class="content input-area">
               <div class="select-container"></div>
@@ -976,7 +1007,7 @@ under the License.
             <span><fmt:message key="label.refresh"/></span>
           </div>
           <div id="update_ssl_button" class="button action main-action reduced-hide lock" title="Updates your Console Proxy SSL Certificate">
-            <span class="icon">&nbsp;</span>            
+            <span class="icon">&nbsp;</span>
             <span><fmt:message key="label.update.ssl.cert"/></span>
           </div>
         </div>
@@ -1050,7 +1081,7 @@ under the License.
               <span class="button view-all hosts"
                     view-all-title="<fmt:message key="label.virtual.routers"/>"
                     view-all-target="virtualRouters"><fmt:message key="label.view.all"/></span>
-            </li> 
+            </li>
           </ul>
         </div>
       </div>
@@ -1225,7 +1256,7 @@ under the License.
             <div class="title">
               <span></span>
             </div>
-            
+
              <div class="button fetch-latest">
                <span><fmt:message key="label.fetch.latest"/></span>
               </div>
@@ -1618,7 +1649,7 @@ under the License.
     <script src="lib/jquery.js" type="text/javascript"></script>
     <script src="lib/jquery.easing.js" type="text/javascript"></script>
     <script src="lib/jquery.validate.js" type="text/javascript"></script>
-    <script src="lib/jquery-ui/js/jquery-ui.js" type="text/javascript"></script>		
+    <script src="lib/jquery-ui/js/jquery-ui.js" type="text/javascript"></script>
     <script src="lib/date.js" type="text/javascript"></script>
     <script src="lib/jquery.cookies.js" type="text/javascript"></script>
     <script src="lib/jquery.md5.js" type="text/javascript" ></script>
@@ -1642,7 +1673,7 @@ under the License.
     <script type="text/javascript" src="scripts/ui/utils.js?t=<%=now%>"></script>
     <script type="text/javascript" src="scripts/ui/events.js?t=<%=now%>"></script>
     <script type="text/javascript" src="scripts/ui/dialog.js?t=<%=now%>"></script>
-    
+
     <script type="text/javascript" src="scripts/ui/widgets/multiEdit.js?t=<%=now%>"></script>
     <script type="text/javascript" src="scripts/ui/widgets/overlay.js?t=<%=now%>"></script>
     <script type="text/javascript" src="scripts/ui/widgets/dataTable.js?t=<%=now%>"></script>
@@ -1650,7 +1681,7 @@ under the License.
     <script type="text/javascript" src="scripts/ui/widgets/listView.js?t=<%=now%>"></script>
     <script type="text/javascript" src="scripts/ui/widgets/detailView.js?t=<%=now%>"></script>
     <script type="text/javascript" src="scripts/ui/widgets/treeView.js?t=<%=now%>"></script>
-    <script type="text/javascript" src="scripts/ui/widgets/notifications.js?t=<%=now%>"></script> 
+    <script type="text/javascript" src="scripts/ui/widgets/notifications.js?t=<%=now%>"></script>
     <script type="text/javascript" src="scripts/ui/widgets/tagger.js?t=<%=now%>"></script>
     <script type="text/javascript" src="scripts/ui/widgets/toolTip.js?t=<%=now%>"></script>
     <script type="text/javascript" src="scripts/cloud.core.callbacks.js?t=<%=now%>"></script>
@@ -1671,8 +1702,8 @@ under the License.
     <script type="text/javascript" src="scripts/dashboard.js?t=<%=now%>"></script>
     <script type="text/javascript" src="scripts/ui-custom/instanceWizard.js?t=<%=now%>"></script>
     <script type="text/javascript" src="scripts/instanceWizard.js?t=<%=now%>"></script>
-    <script type="text/javascript" src="scripts/affinity.js?t=<%=now%>"></script>  
-    <script type="text/javascript" src="scripts/ui-custom/affinity.js?t=<%=now%>"></script>  
+    <script type="text/javascript" src="scripts/affinity.js?t=<%=now%>"></script>
+    <script type="text/javascript" src="scripts/ui-custom/affinity.js?t=<%=now%>"></script>
     <script type="text/javascript" src="scripts/instances.js?t=<%=now%>"></script>
     <script type="text/javascript" src="scripts/events.js?t=<%=now%>"></script>
     <script type="text/javascript" src="scripts/regions.js?t=<%=now%>"></script>
@@ -1687,6 +1718,8 @@ under the License.
     <script type="text/javascript" src="scripts/ui-custom/uploadVolume.js?t=<%=now%>"></script>
     <script type="text/javascript" src="scripts/storage.js?t=<%=now%>"></script>
     <script type="text/javascript" src="scripts/templates.js?t=<%=now%>"></script>
+    <script type="text/javascript" src="scripts/accountsWizard.js?t=<%=now%>"></script>
+    <script type="text/javascript" src="scripts/ui-custom/accountsWizard.js?t=<%=now%>"></script>
     <script type="text/javascript" src="scripts/accounts.js?t=<%=now%>"></script>
     <script type="text/javascript" src="scripts/configuration.js?t=<%=now%>"></script>
     <script type="text/javascript" src="scripts/globalSettings.js?t=<%=now%>"></script>

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/ui/scripts/accounts.js
----------------------------------------------------------------------
diff --git a/ui/scripts/accounts.js b/ui/scripts/accounts.js
index bf0eaf4..e3e28e4 100644
--- a/ui/scripts/accounts.js
+++ b/ui/scripts/accounts.js
@@ -76,7 +76,7 @@
                                     return 'label.add.account';
                                 }
                             },
-
+                            /*
                             createForm: {
                                 title: 'label.add.account',
                                 desc: 'label.add.account',
@@ -284,14 +284,21 @@
                                     }
                                 });
                             },
-
+                            */
                             notification: {
                                 poll: function(args) {
                                     args.complete({
                                         actionFilter: accountActionfilter
                                     });
                                 }
-                            }
+                            },
+
+                            action: {
+                                custom: cloudStack.uiCustom.accountsWizard(
+                                    cloudStack.accountsWizard
+                                )
+                            },
+
                         }
                     },
 

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/ui/scripts/accountsWizard.js
----------------------------------------------------------------------
diff --git a/ui/scripts/accountsWizard.js b/ui/scripts/accountsWizard.js
new file mode 100644
index 0000000..a350085
--- /dev/null
+++ b/ui/scripts/accountsWizard.js
@@ -0,0 +1,158 @@
+// 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.
+
+(function(cloudStack, $) {
+    cloudStack.accountsWizard = {
+
+        manuallyInputtedAccountInformation: {
+            domainid: {
+                label: 'label.domain',
+                docID: 'helpAccountDomain',
+                validation: {
+                    required: true
+                },
+                select: function(args) {
+                    var data = {};
+
+                    if (args.context.users) { // In accounts section
+                        data.listAll = true;
+                    } else if (args.context.domains) { // In domain section (use specific domain)
+                        data.id = args.context.domains[0].id;
+                    }
+
+                    $.ajax({
+                        url: createURL("listDomains"),
+                        data: data,
+                        dataType: "json",
+                        async: false,
+                        success: function(json) {
+                            var items = [];
+                            domainObjs = json.listdomainsresponse.domain;
+                            $(domainObjs).each(function() {
+                                items.push({
+                                    id: this.id,
+                                    description: this.path
+                                });
+
+                                if (this.level == 0)
+                                    rootDomainId = this.id;
+                            });
+                            args.response.success({
+                                data: items
+                            });
+                        }
+                    });
+                }
+            },
+            account: {
+                label: 'label.account',
+                docID: 'helpAccountAccount',
+                validation: {
+                    required: true
+                },
+            },
+            accounttype: {
+                label: 'label.type',
+                docID: 'helpAccountType',
+                validation: {
+                    required: true
+                },
+                select: function(args) {
+                    var items = [];
+                    items.push({
+                        id: 0,
+                        description: "User"
+                    }); //regular-user
+                    items.push({
+                        id: 1,
+                        description: "Admin"
+                    }); //root-admin
+                    args.response.success({
+                        data: items
+                    });
+                }
+            },
+            timezone: {
+                label: 'label.timezone',
+                docID: 'helpAccountTimezone',
+                select: function(args) {
+                    var items = [];
+                    items.push({
+                        id: "",
+                        description: ""
+                    });
+                    for (var p in timezoneMap)
+                        items.push({
+                            id: p,
+                            description: timezoneMap[p]
+                        });
+                    args.response.success({
+                        data: items
+                    });
+                }
+            },
+            networkdomain: {
+                label: 'label.network.domain',
+                docID: 'helpAccountNetworkDomain',
+                validation: {
+                    required: false
+                }
+            }
+        },
+
+        action: function(args) {
+            var array1 = [];
+
+            array1.push("&username=" + args.data.username);
+            array1.push("&domainid=" + args.data.domainid);
+
+            if (args.data.account != null && args.data.account.length != 0) {
+                array1.push("&account=" + args.data.account);
+            }
+
+            if (args.data.accounttype == "1" && args.data.domainid != rootDomainId) {
+                args.data.accounttype = "2";
+            }
+            array1.push("&accountType=" + args.data.accounttype);
+
+            if (args.data.timezone != null && args.data.timezone.length != 0) {
+                array1.push("&timezone=" + args.data.timezone);
+            }
+
+            if (args.data.networkdomain != null && args.data.networkdomain != 0) {
+                array1.push("&networkDomain=" + args.data.networkdomain);
+            }
+
+            console.log(array1.join(""));
+            console.log(args.data);
+
+		$.ajax({
+			url: createURL("ldapCreateAccount" + array1.join("")),
+			dataType: "json",
+			success: function(json) {
+				var item = json.createaccountresponse.account;
+				args.response.success({
+					data: item
+				});
+			},
+			error: function(XMLHttpResponse) {
+				args.response.error(parseXMLHttpResponse(XMLHttpResponse));
+			}
+		});
+        }
+    }
+}(cloudStack, jQuery));

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/ui/scripts/globalSettings.js
----------------------------------------------------------------------
diff --git a/ui/scripts/globalSettings.js b/ui/scripts/globalSettings.js
index 947a3b2..48b71d4 100644
--- a/ui/scripts/globalSettings.js
+++ b/ui/scripts/globalSettings.js
@@ -99,7 +99,6 @@
                     }
                 }
             },
-
             ldapConfiguration: {
                 type: 'select',
                 title: 'LDAP Configuration',
@@ -110,29 +109,18 @@
                         hostname: {
                             label: 'Hostname'
                         },
-                        queryfilter: {
-                            label: 'Query Filter'
-                        },
-                        searchbase: {
-                            label: 'Search Base'
-                        },
                         port: {
                             label: 'LDAP Port'
                         },
-                        ssl: {
-                            label: 'SSL'
-
-                        }
-
                     },
                     dataProvider: function(args) {
                         var data = {};
                         listViewDataProvider(args, data);
                         $.ajax({
-                            url: createURL('ldapConfig&listall=true'), //Need a list LDAP configuration API call which needs to be implemented
+                            url: createURL('listLdapConfigurations'),
                             data: data,
                             success: function(json) {
-                                var items = json.ldapconfigresponse.ldapconfig;
+                                var items = json.ldapconfigurationresponse.LdapConfiguration;
                                 args.response.success({
                                     data: items
                                 });
@@ -142,12 +130,9 @@
                             }
                         });
                     },
-
                     detailView: {
                         name: 'label.details',
                         actions: {
-
-                            // Remove LDAP
                             remove: {
                                 label: 'Remove LDAP',
                                 messages: {
@@ -159,192 +144,96 @@
                                     }
                                 },
                                 action: function(args) {
-
                                     $.ajax({
-                                        url: createURL("ldapRemove"),
+                                        url: createURL("deleteLdapConfiguration&hostname=" + args.context.ldapConfiguration[0].hostname),
                                         success: function(json) {
-
                                             args.response.success();
-
                                         }
-
                                     });
                                     $(window).trigger('cloudStack.fullRefresh');
-
                                 }
                             }
                         },
-
                         tabs: {
-
                             details: {
                                 title: 'LDAP Configuration Details',
                                 fields: [{
                                     hostname: {
                                         label: 'Hostname'
                                     },
-                                    description: {
-                                        label: 'label.description'
-                                    },
-                                    ssl: {
-                                        label: 'SSL'
+                                    port: {
+                                        label: 'Port'
                                     }
                                 }],
                                 dataProvider: function(args) {
+                                    var items = [];
+                                    console.log(args);
                                     $.ajax({
-                                        url: createURL("ldapConfig&listAll=true"),
+                                        url: createURL("listLdapConfigurations&hostname=" + args.context.ldapConfiguration[0].hostname),
                                         dataType: "json",
                                         async: true,
                                         success: function(json) {
-                                            var item = json.ldapconfigresponse.ldapconfig;
+                                            var item = json.ldapconfigurationresponse.LdapConfiguration;
                                             args.response.success({
-                                                data: item
+                                                data: item[0]
                                             });
                                         }
                                     });
                                 }
-
                             }
-
                         }
                     },
-
                     actions: {
                         add: {
-
                             label: 'Configure LDAP',
-
                             messages: {
                                 confirm: function(args) {
                                     return 'Do you really want to configure LDAP ? ';
                                 },
                                 notification: function(args) {
-                                    return 'LDAP configured';
+                                    console.log(args);
+                                    return 'Successfully added a new LDAP server';
                                 }
                             },
-
                             createForm: {
-
                                 title: 'Configure LDAP',
                                 fields: {
-                                    name: {
-                                        label: 'Bind DN',
-                                        validation: {
-                                            required: true
-                                        }
-                                    },
-                                    password: {
-                                        label: 'Bind Password',
-                                        validation: {
-                                            required: true
-                                        },
-                                        isPassword: true
-                                    },
                                     hostname: {
                                         label: 'Hostname',
                                         validation: {
                                             required: true
                                         }
                                     },
-                                    queryfilter: {
-                                        label: 'Query Filter',
-                                        validation: {
-                                            required: true
-                                        },
-                                        docID: 'helpLdapQueryFilter'
-                                    },
-                                    searchbase: {
-                                        label: 'SearchBase',
-                                        validation: {
-                                            required: true
-                                        }
-                                    },
-                                    ssl: {
-                                        label: 'SSL',
-                                        isBoolean: true,
-                                        isChecked: false
-
-                                    },
                                     port: {
                                         label: 'Port',
-                                        defaultValue: '389'
-                                    },
-                                    truststore: {
-                                        label: 'Trust Store',
-                                        isHidden: true,
-                                        dependsOn: 'ssl',
-                                        validation: {
-                                            required: true
-                                        }
-                                    },
-                                    truststorepassword: {
-                                        label: 'Trust Store Password',
-                                        isHidden: true,
-                                        dependsOn: 'ssl',
                                         validation: {
                                             required: true
                                         }
                                     }
-
                                 }
-
-
                             },
-
-
                             action: function(args) {
                                 var array = [];
-                                array.push("&binddn=" + todb(args.data.name));
-                                array.push("&bindpass=" + todb(args.data.password));
                                 array.push("&hostname=" + todb(args.data.hostname));
-                                array.push("&searchbase=" + todb(args.data.searchbase));
-                                array.push("&queryfilter=" + todb(args.data.queryfilter).replace('&amp;', '%26'));
-                                array.push("&port=" + todb(args.data.port));
-
-                                if (args.$form.find('.form-item[rel=ssl]').find('input[type=checkbox]').is(':Checked') == true) {
-
-                                    array.push("&ssl=true");
-                                    if (args.data.truststore != "")
-                                        array.push("&truststore=" + todb(args.data.truststore));
-
-                                    if (args.data.truststorepassword != "")
-                                        array.push("&truststorepass=" + todb(args.data.truststorepassword));
-
-                                } else
-                                    array.push("&ssl=false");
-
+                                array.push("&port=" + todb(args.data.port));;
                                 $.ajax({
-                                    url: createURL("ldapConfig" + array.join("")),
+                                    url: createURL("addLdapConfiguration" + array.join("")),
                                     dataType: "json",
-                                    type: "POST",                               
+                                    async: true,
                                     success: function(json) {
-                                        var items = json.ldapconfigresponse.ldapconfig;
+                                        var items = json.ldapconfigurationresponse.LdapAddConfiguration;
                                         args.response.success({
                                             data: items
                                         });
-
                                     },
-
                                     error: function(json) {
                                         args.response.error(parseXMLHttpResponse(json));
-
                                     }
-
-
                                 });
-
-
                             }
                         }
-
                     }
-
-
-
                 }
-
-
-
             },
             hypervisorCapabilities: {
                 type: 'select',

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/ui/scripts/ui-custom/accountsWizard.js
----------------------------------------------------------------------
diff --git a/ui/scripts/ui-custom/accountsWizard.js b/ui/scripts/ui-custom/accountsWizard.js
new file mode 100644
index 0000000..4037788
--- /dev/null
+++ b/ui/scripts/ui-custom/accountsWizard.js
@@ -0,0 +1,116 @@
+// 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.
+
+(function($, cloudStack) {
+    cloudStack.uiCustom.accountsWizard = function(args) {
+        return function(listViewArgs) {
+            var context = listViewArgs.context;
+
+            var accountsWizard = function(data) {
+                var $wizard = $('#template').find('div.accounts-wizard').clone();
+                var $form = $wizard.find('form');
+
+                var close = function() {
+                    $wizard.dialog('destroy');
+                    $('div.overlay').fadeOut(function() {
+                        $('div.overlay').remove();
+                    });
+                };
+
+                var completeAction = function() {
+                    var data = cloudStack.serializeForm($form);
+                    args.action({
+                        context: context,
+                        data: data,
+                        response: {
+                            success: function(args) {
+                                $('.list-view').listView('refresh');
+                                close();
+                            },
+                            error: function(message) {
+                                close();
+                                if(message) {
+                                    cloudStack.dialog.notice({
+                                        message: message
+                                    });
+                                }
+                            }
+                        }
+                    });
+                }
+
+                $wizard.click(function(event) {
+                    var $target = $(event.target);
+                    if ($target.closest('div.button.next').size()) {
+                        $form.validate();
+                        if ($form.valid()) {
+                            completeAction();
+                            return true;
+                        } else {
+                            return false;
+                        }
+                    }
+
+                    if ($target.closest('div.button.cancel').size()) {
+                        close();
+                        return false;
+                    }
+                });
+
+                var form = cloudStack.dialog.createForm({
+                    context: context,
+                    noDialog: true,
+                    form: {
+                        title: '',
+                        fields: args.manuallyInputtedAccountInformation
+                    }
+                });
+
+                var $manualDetails = form.$formContainer.find('form .form-item');
+                $wizard.find('.manual-account-details').append($manualDetails);
+
+                var $table = $wizard.find('.ldap-account-choice tbody');
+
+                $.ajax({
+                    url: createURL("listAllLdapUsers"),
+                    dataType: "json",
+                    async: false,
+                    success: function(json) {
+                        $(json.ldapuserresponse.LdapUser).each(function() {
+                            var result = $("<tr>");
+                            result.append("<td><input type=\"radio\" class=\"required\" name=\"username\" value=\"" + this.username + "\"></td>");
+                            result.append("<td>" + this.firstname + " " + this.lastname + "</td>");
+                            result.append("<td>" + this.username + "</td>");
+                            result.append("<td>" + this.email + "</td>");
+                            $table.append(result);
+                        })
+                    }
+                });
+
+                return $wizard.dialog({
+                    title: _l('label.add.account'),
+                    width: 800,
+                    height: 500,
+                    closeOnEscape: false,
+                    zIndex: 5000
+                }).closest('.ui-dialog').overlay();
+            }
+
+            accountsWizard(args);
+        };
+    };
+})(jQuery, cloudStack);


[2/4] Merge LDAPPlugin

Posted by ap...@apache.org.
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapManagerImplSpec.groovy
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapManagerImplSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapManagerImplSpec.groovy
new file mode 100644
index 0000000..548057c
--- /dev/null
+++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapManagerImplSpec.groovy
@@ -0,0 +1,319 @@
+// 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.
+package groovy.org.apache.cloudstack.ldap
+
+import javax.naming.NamingException
+import javax.naming.ldap.InitialLdapContext
+
+import org.apache.cloudstack.api.command.LdapListConfigurationCmd
+import org.apache.cloudstack.ldap.*
+import org.apache.cloudstack.ldap.dao.LdapConfigurationDaoImpl
+
+import com.cloud.exception.InvalidParameterValueException
+import com.cloud.utils.Pair
+
+class LdapManagerImplSpec extends spock.lang.Specification {
+    def "Test that addConfiguration fails when a duplicate configuration exists"() {
+        given:
+        def ldapConfigurationDao = Mock(LdapConfigurationDaoImpl)
+        def ldapContextFactory = Mock(LdapContextFactory)
+        def ldapUserManager = Mock(LdapUserManager)
+        ldapConfigurationDao.findByHostname(_) >> new LdapConfigurationVO("localhost", 389)
+        def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManager)
+        when:
+        ldapManager.addConfiguration("localhost", 389)
+        then:
+        thrown InvalidParameterValueException
+    }
+
+    def "Test that addConfiguration fails when a binding fails"() {
+        given:
+        def ldapConfigurationDao = Mock(LdapConfigurationDaoImpl)
+        def ldapContextFactory = Mock(LdapContextFactory)
+        def ldapUserManager = Mock(LdapUserManager)
+        ldapContextFactory.createBindContext(_) >> { throw new NamingException() }
+        def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManager)
+        when:
+        ldapManager.addConfiguration("localhost", 389)
+        then:
+        thrown InvalidParameterValueException
+    }
+
+    def "Test successfully addConfiguration"() {
+        given:
+        def ldapConfigurationDao = Mock(LdapConfigurationDaoImpl)
+        def ldapContextFactory = Mock(LdapContextFactory)
+        def ldapUserManager = Mock(LdapUserManager)
+        ldapContextFactory.createBindContext(_) >> null
+        ldapConfigurationDao.persist(_) >> null
+        def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManager)
+        when:
+        def result = ldapManager.addConfiguration("localhost", 389)
+        then:
+        result.hostname == "localhost"
+        result.port == 389
+    }
+
+    def "Test successful failed result from deleteConfiguration due to configuration not existing"() {
+        given:
+        def ldapConfigurationDao = Mock(LdapConfigurationDaoImpl)
+        def ldapContextFactory = Mock(LdapContextFactory)
+        def ldapUserManager = Mock(LdapUserManager)
+        ldapConfigurationDao.findByHostname(_) >> null
+        def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManager)
+        when:
+        ldapManager.deleteConfiguration("localhost")
+        then:
+        thrown InvalidParameterValueException
+    }
+
+    def "Test successful result from deleteConfiguration"() {
+        given:
+        def ldapConfigurationDao = Mock(LdapConfigurationDaoImpl)
+        def ldapContextFactory = Mock(LdapContextFactory)
+        def ldapUserManager = Mock(LdapUserManager)
+        ldapConfigurationDao.findByHostname(_) >> {
+            def configuration = new LdapConfigurationVO("localhost", 389)
+            configuration.setId(0);
+            return configuration;
+        }
+        ldapConfigurationDao.remove(_) >> null
+        def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManager)
+        when:
+        def result = ldapManager.deleteConfiguration("localhost")
+        then:
+        result.hostname == "localhost"
+        result.port == 389
+    }
+
+    def "Test successful failed result from canAuthenticate due to user not found"() {
+        given:
+        def ldapConfigurationDao = Mock(LdapConfigurationDaoImpl)
+        def ldapContextFactory = Mock(LdapContextFactory)
+        def ldapUserManager = Mock(LdapUserManager)
+        def ldapManager = Spy(LdapManagerImpl, constructorArgs: [ldapConfigurationDao, ldapContextFactory, ldapUserManager])
+        ldapManager.getUser(_) >> { throw new NamingException() }
+        when:
+        def result = ldapManager.canAuthenticate("rmurphy", "password")
+        then:
+        result == false
+    }
+
+    def "Test successful failed result from canAuthenticate due to bad password"() {
+        given:
+        def ldapConfigurationDao = Mock(LdapConfigurationDaoImpl)
+        def ldapContextFactory = Mock(LdapContextFactory)
+        ldapContextFactory.createUserContext(_, _) >> { throw new NamingException() }
+        def ldapUserManager = Mock(LdapUserManager)
+        def ldapManager = Spy(LdapManagerImpl, constructorArgs: [ldapConfigurationDao, ldapContextFactory, ldapUserManager])
+        ldapManager.getUser(_) >> { new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org") }
+        when:
+        def result = ldapManager.canAuthenticate("rmurphy", "password")
+        then:
+        result == false
+    }
+
+    def "Test successful result from canAuthenticate"() {
+        given:
+        def ldapConfigurationDao = Mock(LdapConfigurationDaoImpl)
+        def ldapContextFactory = Mock(LdapContextFactory)
+        ldapContextFactory.createUserContext(_, _) >> null
+        def ldapUserManager = Mock(LdapUserManager)
+        def ldapManager = Spy(LdapManagerImpl, constructorArgs: [ldapConfigurationDao, ldapContextFactory, ldapUserManager])
+        ldapManager.getUser(_) >> { new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org") }
+        when:
+        def result = ldapManager.canAuthenticate("rmurphy", "password")
+        then:
+        result == true
+    }
+
+    def "Test successful closing of context"() {
+        given:
+        def ldapConfigurationDao = Mock(LdapConfigurationDaoImpl)
+        def ldapContextFactory = Mock(LdapContextFactory)
+        def ldapUserManager = Mock(LdapUserManager)
+        def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManager)
+        when:
+        def context = Mock(InitialLdapContext)
+        ldapManager.closeContext(context)
+        then:
+        context.defaultInitCtx == null
+    }
+
+    def "Test successful failing to close of context"() {
+        given:
+        def ldapConfigurationDao = Mock(LdapConfigurationDaoImpl)
+        def ldapContextFactory = Mock(LdapContextFactory)
+        def ldapUserManager = Mock(LdapUserManager)
+        def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManager)
+        when:
+        def context = Mock(InitialLdapContext)
+        context.close() >> { throw new NamingException() }
+        ldapManager.closeContext(context)
+        then:
+        context.defaultInitCtx == null
+    }
+
+    def "Test LdapConfigurationResponse generation"() {
+        given:
+        def ldapConfigurationDao = Mock(LdapConfigurationDaoImpl)
+        def ldapContextFactory = Mock(LdapContextFactory)
+        def ldapUserManager = Mock(LdapUserManager)
+        def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManager)
+        when:
+        def result = ldapManager.createLdapConfigurationResponse(new LdapConfigurationVO("localhost", 389))
+        then:
+        result.hostname == "localhost"
+        result.port == 389
+    }
+
+    def "Test LdapUserResponse generation"() {
+        given:
+        def ldapConfigurationDao = Mock(LdapConfigurationDaoImpl)
+        def ldapContextFactory = Mock(LdapContextFactory)
+        def ldapUserManager = Mock(LdapUserManager)
+        def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManager)
+        when:
+        def result = ldapManager.createLdapUserResponse(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org"))
+        then:
+        result.username == "rmurphy"
+        result.email == "rmurphy@test.com"
+        result.firstname == "Ryan"
+        result.lastname == "Murphy"
+        result.principal == "cn=rmurphy,dc=cloudstack,dc=org"
+    }
+
+    def "Test that getCommands isn't empty"() {
+        given:
+        def ldapConfigurationDao = Mock(LdapConfigurationDaoImpl)
+        def ldapContextFactory = Mock(LdapContextFactory)
+        def ldapUserManager = Mock(LdapUserManager)
+        def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManager)
+        when:
+        def result = ldapManager.getCommands()
+        then:
+        result.size() > 0
+    }
+
+    def "Test failing of getUser due to bind issue"() {
+        given:
+        def ldapConfigurationDao = Mock(LdapConfigurationDaoImpl)
+        def ldapContextFactory = Mock(LdapContextFactory)
+        def ldapUserManager = Mock(LdapUserManager)
+        ldapContextFactory.createBindContext() >> { throw new NamingException() }
+        def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManager)
+        when:
+        ldapManager.getUser("rmurphy")
+        then:
+        thrown NamingException
+    }
+
+    def "Test success of getUser"() {
+        given:
+        def ldapConfigurationDao = Mock(LdapConfigurationDaoImpl)
+        def ldapContextFactory = Mock(LdapContextFactory)
+        def ldapUserManager = Mock(LdapUserManager)
+        ldapContextFactory.createBindContext() >> null
+        ldapUserManager.getUser(_, _) >> new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org")
+        def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManager)
+        when:
+        def result = ldapManager.getUser("rmurphy")
+        then:
+        result.username == "rmurphy"
+        result.email == "rmurphy@test.com"
+        result.firstname == "Ryan"
+        result.lastname == "Murphy"
+        result.principal == "cn=rmurphy,dc=cloudstack,dc=org"
+    }
+
+    def "Test failing of getUsers due to bind issue"() {
+        given:
+        def ldapConfigurationDao = Mock(LdapConfigurationDaoImpl)
+        def ldapContextFactory = Mock(LdapContextFactory)
+        def ldapUserManager = Mock(LdapUserManager)
+        ldapContextFactory.createBindContext() >> { throw new NamingException() }
+        def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManager)
+        when:
+        ldapManager.getUsers()
+        then:
+        thrown NoLdapUserMatchingQueryException
+    }
+
+    def "Test success getUsers"() {
+        given:
+        def ldapConfigurationDao = Mock(LdapConfigurationDaoImpl)
+        def ldapContextFactory = Mock(LdapContextFactory)
+        def ldapUserManager = Mock(LdapUserManager)
+        ldapContextFactory.createBindContext() >> null
+        List<LdapUser> users = new ArrayList<>();
+        users.add(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org"))
+        ldapUserManager.getUsers(_) >> users;
+        def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManager)
+        when:
+        def result = ldapManager.getUsers()
+        then:
+        result.size() > 0;
+    }
+
+    def "Testing of listConfigurations"() {
+        given:
+        def ldapConfigurationDao = Mock(LdapConfigurationDaoImpl)
+        def ldapContextFactory = Mock(LdapContextFactory)
+        def ldapUserManager = Mock(LdapUserManager)
+        List<LdapConfigurationVO> ldapConfigurationList = new ArrayList()
+        ldapConfigurationList.add(new LdapConfigurationVO("localhost", 389))
+        Pair<List<LdapConfigurationVO>, Integer> configurations = new Pair<List<LdapConfigurationVO>, Integer>();
+        configurations.set(ldapConfigurationList, ldapConfigurationList.size())
+        ldapConfigurationDao.searchConfigurations(_, _) >> configurations
+        def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManager)
+        when:
+        def result = ldapManager.listConfigurations(new LdapListConfigurationCmd())
+        then:
+        result.second() > 0
+    }
+
+    def "Test failing of searchUsers due to a failure to bind"() {
+        given:
+        def ldapConfigurationDao = Mock(LdapConfigurationDaoImpl)
+        def ldapContextFactory = Mock(LdapContextFactory)
+        def ldapUserManager = Mock(LdapUserManager)
+        ldapContextFactory.createBindContext() >> { throw new NamingException() }
+        def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManager)
+        when:
+        ldapManager.searchUsers("rmurphy")
+        then:
+        thrown NoLdapUserMatchingQueryException
+    }
+
+    def "Test successful result from searchUsers"() {
+        given:
+        def ldapConfigurationDao = Mock(LdapConfigurationDaoImpl)
+        def ldapContextFactory = Mock(LdapContextFactory)
+        def ldapUserManager = Mock(LdapUserManager)
+        ldapContextFactory.createBindContext() >> null;
+
+        List<LdapUser> users = new ArrayList<LdapUser>();
+        users.add(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org"))
+        ldapUserManager.getUsers(_, _) >> users;
+
+        def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManager)
+        when:
+        def result = ldapManager.searchUsers("rmurphy");
+        then:
+        result.size() > 0;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapSearchUserCmdSpec.groovy
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapSearchUserCmdSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapSearchUserCmdSpec.groovy
new file mode 100644
index 0000000..b23d7c2
--- /dev/null
+++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapSearchUserCmdSpec.groovy
@@ -0,0 +1,72 @@
+// 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.
+package groovy.org.apache.cloudstack.ldap
+
+import org.apache.cloudstack.api.ServerApiException
+import org.apache.cloudstack.api.command.LdapUserSearchCmd
+import org.apache.cloudstack.api.response.LdapUserResponse
+import org.apache.cloudstack.ldap.LdapManager
+import org.apache.cloudstack.ldap.LdapUser
+import org.apache.cloudstack.ldap.NoLdapUserMatchingQueryException
+
+class LdapSearchUserCmdSpec extends spock.lang.Specification {
+    def "Test successful response from execute"() {
+        given:
+        def ldapManager = Mock(LdapManager)
+        List<LdapUser> users = new ArrayList()
+        users.add(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org"))
+        ldapManager.searchUsers(_) >> users
+        LdapUserResponse response = new LdapUserResponse("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org")
+        ldapManager.createLdapUserResponse(_) >> response
+        def ldapUserSearchCmd = new LdapUserSearchCmd(ldapManager)
+        when:
+        ldapUserSearchCmd.execute()
+        then:
+        ldapUserSearchCmd.responseObject.getResponses().size() != 0
+    }
+
+    def "Test successful empty response from execute"() {
+        given:
+        def ldapManager = Mock(LdapManager)
+        ldapManager.searchUsers(_) >> {throw new NoLdapUserMatchingQueryException()}
+        def ldapUserSearchCmd = new LdapUserSearchCmd(ldapManager)
+        when:
+        ldapUserSearchCmd.execute()
+        then:
+        ldapUserSearchCmd.responseObject.getResponses().size() == 0
+    }
+
+    def "Test getEntityOwnerId is 0"() {
+        given:
+        def ldapManager = Mock(LdapManager)
+        def ldapUserSearchCmd = new LdapUserSearchCmd(ldapManager)
+        when:
+        long ownerId = ldapUserSearchCmd.getEntityOwnerId()
+        then:
+        ownerId == 1
+    }
+
+    def "Test successful return of getCommandName"() {
+        given:
+        def ldapManager = Mock(LdapManager)
+        def ldapUserSearchCmd = new LdapUserSearchCmd(ldapManager)
+        when:
+        String commandName = ldapUserSearchCmd.getCommandName()
+        then:
+        commandName == "ldapuserresponse"
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapUserManagerSpec.groovy
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapUserManagerSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapUserManagerSpec.groovy
new file mode 100644
index 0000000..2d31aa6
--- /dev/null
+++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapUserManagerSpec.groovy
@@ -0,0 +1,207 @@
+// 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.
+package groovy.org.apache.cloudstack.ldap
+
+import org.apache.cloudstack.ldap.LdapConfiguration
+import org.apache.cloudstack.ldap.LdapUserManager
+import spock.lang.Shared
+
+import javax.naming.NamingException
+import javax.naming.directory.Attribute
+import javax.naming.directory.Attributes
+import javax.naming.directory.SearchControls
+import javax.naming.directory.SearchResult
+import javax.naming.ldap.LdapContext
+
+class LdapUserManagerSpec extends spock.lang.Specification {
+
+    @Shared
+    private def ldapConfiguration
+
+    @Shared
+    private def username
+
+    @Shared
+    private def email
+
+    @Shared
+    private def firstname
+
+    @Shared
+    private def lastname
+
+    @Shared
+    private def principal
+
+    def setupSpec() {
+        ldapConfiguration = Mock(LdapConfiguration)
+
+        ldapConfiguration.getScope() >> SearchControls.SUBTREE_SCOPE
+        ldapConfiguration.getReturnAttributes() >> ["uid", "mail", "cn"]
+        ldapConfiguration.getUsernameAttribute() >> "uid"
+        ldapConfiguration.getEmailAttribute() >> "mail"
+        ldapConfiguration.getFirstnameAttribute() >> "givenname"
+        ldapConfiguration.getLastnameAttribute() >> "sn"
+        ldapConfiguration.getBaseDn() >> "dc=cloudstack,dc=org"
+
+        username = "rmurphy"
+        email = "rmurphy@test.com"
+        firstname = "Ryan"
+        lastname = "Murphy"
+        principal = "cn=" + username + "," + ldapConfiguration.getBaseDn()
+    }
+
+    def "Test that a newly created Ldap User Manager is not null"() {
+        given: "You have created a new Ldap user manager object"
+        def result = new LdapUserManager();
+        expect: "The result is not null"
+        result != null
+    }
+
+    def "Test successfully creating an Ldap User from Search result"() {
+        given:
+        def attributes = createUserAttributes(username, email, firstname, lastname)
+        def search = createSearchResult(attributes)
+        def userManager = new LdapUserManager(ldapConfiguration)
+        def result = userManager.createUser(search)
+
+        expect:
+
+        result.username == username
+        result.email == email
+        result.firstname == firstname
+        result.lastname == lastname
+        result.principal == principal
+    }
+
+    def "Test successfully returning an Ldap user from a get user request"() {
+        given:
+
+        def userManager = new LdapUserManager(ldapConfiguration)
+
+        when:
+        def result = userManager.getUser(username, createContext())
+
+        then:
+        result.username == username
+        result.email == email
+        result.firstname == firstname
+        result.lastname == lastname
+        result.principal == principal
+    }
+
+    def "Test successfully returning a list from get users"() {
+        given:
+
+        def userManager = new LdapUserManager(ldapConfiguration)
+
+        when:
+        def result = userManager.getUsers(username, createContext())
+
+        then:
+        result.size() == 1
+    }
+
+    def "Test successfully returning a list from get users when no username is given"() {
+        given:
+
+        def userManager = new LdapUserManager(ldapConfiguration)
+
+        when:
+        def result = userManager.getUsers(createContext())
+
+        then:
+        result.size() == 1
+    }
+
+    def "Test successfully throwing an exception when no users are found with getUser"() {
+        given:
+
+        def searchUsersResults = new BasicNamingEnumerationImpl()
+
+        def context = Mock(LdapContext)
+        context.search(_, _, _) >> searchUsersResults;
+
+        def userManager = new LdapUserManager(ldapConfiguration)
+
+        when:
+        def result = userManager.getUser(username, context)
+
+        then:
+        thrown NamingException
+    }
+
+    def "Test successfully returning a NamingEnumeration from searchUsers"() {
+        given:
+        def userManager = new LdapUserManager(ldapConfiguration)
+
+        when:
+        def result = userManager.searchUsers(createContext())
+
+        then:
+        result.next().getName() + "," + ldapConfiguration.getBaseDn() == principal
+    }
+
+    private def createContext() {
+
+        Attributes attributes = createUserAttributes(username, email, firstname, lastname)
+        SearchResult searchResults = createSearchResult(attributes)
+        def searchUsersResults = new BasicNamingEnumerationImpl()
+        searchUsersResults.add(searchResults);
+
+        def context = Mock(LdapContext)
+        context.search(_, _, _) >> searchUsersResults;
+
+        return context
+    }
+
+    private SearchResult createSearchResult(attributes) {
+        def search = Mock(SearchResult)
+
+        search.getName() >> "cn=" + attributes.getAt("uid").get();
+
+        search.getAttributes() >> attributes
+
+        return search
+    }
+
+    private Attributes createUserAttributes(String username, String email, String firstname, String lastname) {
+        def attributes = Mock(Attributes)
+
+        def nameAttribute = Mock(Attribute)
+        nameAttribute.getId() >> "uid"
+        nameAttribute.get() >> username
+        attributes.get("uid") >> nameAttribute
+
+        def mailAttribute = Mock(Attribute)
+        mailAttribute.getId() >> "mail"
+        mailAttribute.get() >> email
+        attributes.get("mail") >> mailAttribute
+
+        def givennameAttribute = Mock(Attribute)
+        givennameAttribute.getId() >> "givenname"
+        givennameAttribute.get() >> firstname
+        attributes.get("givenname") >> givennameAttribute
+
+        def snAttribute = Mock(Attribute)
+        snAttribute.getId() >> "sn"
+        snAttribute.get() >> lastname
+        attributes.get("sn") >> snAttribute
+
+        return attributes
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapUserResponseSpec.groovy
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapUserResponseSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapUserResponseSpec.groovy
new file mode 100644
index 0000000..aa7d5a3
--- /dev/null
+++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapUserResponseSpec.groovy
@@ -0,0 +1,67 @@
+// 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.
+package groovy.org.apache.cloudstack.ldap
+
+import org.apache.cloudstack.api.response.LdapUserResponse
+
+
+class LdapUserResponseSpec extends spock.lang.Specification {
+    def "Testing succcessful setting of LdapUserResponse email"() {
+        given:
+        LdapUserResponse response = new LdapUserResponse();
+        when:
+        response.setEmail("rmurphy@test.com");
+        then:
+        response.getEmail() == "rmurphy@test.com";
+    }
+
+    def "Testing successful setting of LdapUserResponse principal"() {
+        given:
+        LdapUserResponse response = new LdapUserResponse()
+        when:
+        response.setPrincipal("dc=cloudstack,dc=org")
+        then:
+        response.getPrincipal() == "dc=cloudstack,dc=org"
+    }
+
+    def "Testing successful setting of LdapUserResponse username"() {
+        given:
+        LdapUserResponse response = new LdapUserResponse()
+        when:
+        response.setUsername("rmurphy")
+        then:
+        response.getUsername() == "rmurphy"
+    }
+
+    def "Testing successful setting of LdapUserResponse firstname"() {
+        given:
+        LdapUserResponse response = new LdapUserResponse()
+        when:
+        response.setFirstname("Ryan")
+        then:
+        response.getFirstname() == "Ryan"
+    }
+
+    def "Testing successful setting of LdapUserResponse lastname"() {
+        given:
+        LdapUserResponse response = new LdapUserResponse()
+        when:
+        response.setLastname("Murphy")
+        then:
+        response.getLastname() == "Murphy"
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapUserSpec.groovy
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapUserSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapUserSpec.groovy
new file mode 100644
index 0000000..cf2f9ec
--- /dev/null
+++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapUserSpec.groovy
@@ -0,0 +1,79 @@
+// 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.
+package groovy.org.apache.cloudstack.ldap
+
+import org.apache.cloudstack.ldap.LdapUser
+
+class LdapUserSpec extends spock.lang.Specification {
+
+    def "Testing that the username is correctly set with the ldap object"() {
+        given: "You have created a LDAP user object with a username"
+        def user = new LdapUser(username, "", "", "","")
+        expect: "The username is equal to the given data source"
+        user.getUsername() == username
+        where: "The username is set to "
+        username << ["", null, "rmurphy"]
+    }
+
+    def "Testing the email is correctly set with the ldap object"() {
+        given: "You have created a LDAP user object with a email"
+        def user = new LdapUser("", email, "", "","")
+        expect: "The email is equal to the given data source"
+        user.getEmail() == email
+        where: "The email is set to "
+        email << ["", null, "test@test.com"]
+    }
+
+    def "Testing the firstname is correctly set with the ldap object"() {
+        given: "You have created a LDAP user object with a firstname"
+        def user = new LdapUser("", "", firstname, "", "")
+        expect: "The firstname is equal to the given data source"
+        user.getFirstname() == firstname
+        where: "The firstname is set to "
+        firstname << ["", null, "Ryan"]
+    }
+
+    def "Testing the lastname is correctly set with the ldap object"() {
+        given: "You have created a LDAP user object with a lastname"
+        def user = new LdapUser("", "", "", lastname, "")
+        expect: "The lastname is equal to the given data source"
+        user.getLastname() == lastname
+        where: "The lastname is set to "
+        lastname << ["", null, "Murphy"]
+    }
+
+    def "Testing the principal is correctly set with the ldap object"() {
+        given: "You have created a LDAP user object with a principal"
+        def user = new LdapUser("", "", "", "", principal)
+        expect: "The principal is equal to the given data source"
+        user.getPrincipal() == principal
+        where: "The username is set to "
+        principal << ["", null, "cn=rmurphy,dc=cloudstack,dc=org"]
+    }
+
+    def "Testing that LdapUser successfully gives the correct result for a compare to"() {
+        given: "You have created two LDAP user objects"
+        def userA = new LdapUser(usernameA, "", "", "", "")
+        def userB = new LdapUser(usernameB, "", "", "", "")
+        expect: "That when compared the result is less than or equal to 0"
+        userA.compareTo(userB) <= 0
+        where: "The following values are used"
+        usernameA | usernameB
+        "A"       | "B"
+        "A"       | "A"
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapUtilsSpec.groovy
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapUtilsSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapUtilsSpec.groovy
new file mode 100644
index 0000000..7fc05a9
--- /dev/null
+++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapUtilsSpec.groovy
@@ -0,0 +1,68 @@
+// 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.
+package groovy.org.apache.cloudstack.ldap
+
+import org.apache.cloudstack.ldap.LdapUtils
+
+import javax.naming.directory.Attribute
+import javax.naming.directory.Attributes
+
+class LdapUtilsSpec extends spock.lang.Specification {
+    def "Testing that a Ldap Search Filter is correctly escaped"() {
+        given: "You have some input from a user"
+
+        expect: "That the input is escaped"
+        LdapUtils.escapeLDAPSearchFilter(input) == result
+
+        where: "The following inputs are given "
+        input                                       | result
+        "Hi This is a test #çà"                     | "Hi This is a test #çà"
+        "Hi (This) = is * a \\ test # ç à ô \u0000" | "Hi \\28This\\29 = is \\2a a \\5c test # ç à ô \\00"
+    }
+
+    def "Testing than an attribute is successfully returned"() {
+        given: "You have an attributes object with some attribute"
+        def attributes = Mock(Attributes)
+        def attribute = Mock(Attribute)
+        attribute.getId() >> name
+        attribute.get() >> value
+        attributes.get(name) >> attribute
+
+        when: "You get the attribute"
+        String foundValue = LdapUtils.getAttributeValue(attributes, name)
+
+        then: "Its value equals uid"
+        foundValue == value
+
+        where:
+        name    | value
+        "uid"   | "rmurphy"
+        "email" | "rmurphy@test.com"
+    }
+
+    def "Testing than an attribute is not successfully returned"() {
+        given: "You have an attributes object with some attribute"
+        def attributes = Mock(Attributes)
+        attributes.get("uid") >> null
+
+        when: "You get the attribute"
+        String foundValue = LdapUtils.getAttributeValue(attributes, "uid")
+
+        then: "Its value equals uid"
+        foundValue == null
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/NoLdapUserMatchingQueryExceptionSpec.groovy
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/NoLdapUserMatchingQueryExceptionSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/NoLdapUserMatchingQueryExceptionSpec.groovy
new file mode 100644
index 0000000..4c0cc4b
--- /dev/null
+++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/NoLdapUserMatchingQueryExceptionSpec.groovy
@@ -0,0 +1,30 @@
+// 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.
+package groovy.org.apache.cloudstack.ldap
+
+import org.apache.cloudstack.ldap.NoLdapUserMatchingQueryException
+
+class NoLdapUserMatchingQueryExceptionSpec extends spock.lang.Specification {
+    def "Test that the query is correctly set within the No LDAP user matching query exception object"() {
+        given: "You have created an No LDAP user matching query exception object with a query set"
+        def exception = new NoLdapUserMatchingQueryException(query)
+        expect: "The username is equal to the given data source"
+        exception.getQuery() == query
+        where: "The username is set to "
+        query << ["", null, "murp*"]
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/NoSuchLdapUserExceptionSpec.groovy
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/NoSuchLdapUserExceptionSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/NoSuchLdapUserExceptionSpec.groovy
new file mode 100644
index 0000000..dbdf646
--- /dev/null
+++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/NoSuchLdapUserExceptionSpec.groovy
@@ -0,0 +1,30 @@
+// 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.
+package groovy.org.apache.cloudstack.ldap
+
+import org.apache.cloudstack.ldap.NoSuchLdapUserException;
+
+class NoSuchLdapUserExceptionSpec extends spock.lang.Specification {
+    def "Test that the username is correctly set within the No such LDAP user exception object"() {
+        given: "You have created an No such LDAP user exception object with the username set"
+        def exception = new NoSuchLdapUserException(username)
+        expect: "The username is equal to the given data source"
+        exception.getUsername() == username
+        where: "The username is set to "
+        username << ["", null, "rmurphy"]
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/plugins/user-authenticators/ldap/test/resources/cloudstack.org.ldif
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/ldap/test/resources/cloudstack.org.ldif b/plugins/user-authenticators/ldap/test/resources/cloudstack.org.ldif
new file mode 100644
index 0000000..b20de81
--- /dev/null
+++ b/plugins/user-authenticators/ldap/test/resources/cloudstack.org.ldif
@@ -0,0 +1,19 @@
+version: 1
+
+dn: dc=cloudstack,dc=org
+objectClass: dcObject
+objectClass: organization
+dc: cloudstack
+o: cloudstack
+
+dn: cn=Ryan Murphy,dc=cloudstack,dc=org
+objectClass: inetOrgPerson
+objectClass: organizationalPerson
+objectClass: person
+objectClass: top
+cn: Ryan Murphy
+sn: Murphy
+givenName: Ryan
+mail: rmurphy@cloudstack.org
+uid: rmurphy
+userpassword:: cGFzc3dvcmQ=

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/server/src/com/cloud/api/ApiResponseHelper.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java
index e49e169..f98a3ef 100755
--- a/server/src/com/cloud/api/ApiResponseHelper.java
+++ b/server/src/com/cloud/api/ApiResponseHelper.java
@@ -78,7 +78,6 @@ import org.apache.cloudstack.api.response.LBHealthCheckPolicyResponse;
 import org.apache.cloudstack.api.response.LBHealthCheckResponse;
 import org.apache.cloudstack.api.response.LBStickinessPolicyResponse;
 import org.apache.cloudstack.api.response.LBStickinessResponse;
-import org.apache.cloudstack.api.response.LDAPConfigResponse;
 import org.apache.cloudstack.api.response.LoadBalancerResponse;
 import org.apache.cloudstack.api.response.NetworkACLItemResponse;
 import org.apache.cloudstack.api.response.NetworkACLResponse;
@@ -2808,20 +2807,6 @@ public class ApiResponseHelper implements ResponseGenerator {
     }
 
     @Override
-    public LDAPConfigResponse createLDAPConfigResponse(String hostname, Integer port, Boolean useSSL, String queryFilter, String searchBase,
-            String bindDN) {
-        LDAPConfigResponse lr = new LDAPConfigResponse();
-        lr.setHostname(hostname);
-        lr.setPort(port.toString());
-        lr.setUseSSL(useSSL.toString());
-        lr.setQueryFilter(queryFilter);
-        lr.setBindDN(bindDN);
-        lr.setSearchBase(searchBase);
-        lr.setObjectName("ldapconfig");
-        return lr;
-    }
-
-    @Override
     public StorageNetworkIpRangeResponse createStorageNetworkIpRangeResponse(StorageNetworkIpRange result) {
         StorageNetworkIpRangeResponse response = new StorageNetworkIpRangeResponse();
         response.setUuid(result.getUuid());

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/server/src/com/cloud/configuration/Config.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java
index c815c77..1ec0576 100755
--- a/server/src/com/cloud/configuration/Config.java
+++ b/server/src/com/cloud/configuration/Config.java
@@ -424,6 +424,16 @@ public enum Config {
     // object store
     S3EnableRRS("Advanced", ManagementServer.class, Boolean.class, "s3.rrs.enabled", "false", "enable s3 reduced redundancy storage", null),
 
+        // Ldap
+    LdapBasedn("Advanced", ManagementServer.class, String.class, "ldap.basedn", null, "Sets the basedn for LDAP", null),
+    LdapBindPassword("Advanced", ManagementServer.class, String.class, "ldap.bind.password", null, "Sets the bind password for LDAP", null),
+    LdapBindPrincipal("Advanced", ManagementServer.class, String.class, "ldap.bind.principal", null, "Sets the bind principal for LDAP", null),
+    LdapEmailAttribute("Advanced", ManagementServer.class, String.class, "ldap.email.attribute", "mail", "Sets the email attribute used within LDAP", null),
+    LdapFirstnameAttribute("Advanced", ManagementServer.class, String.class, "ldap.firstname.attribute", "givenname", "Sets the firstname attribute used within LDAP", null),
+    LdapLastnameAttribute("Advanced", ManagementServer.class, String.class, "ldap.lastname.attribute", "sn", "Sets the lastname attribute used within LDAP", null),
+    LdapUsernameAttribute("Advanced", ManagementServer.class, String.class, "ldap.username.attribute", "uid", "Sets the username attribute used within LDAP", null),
+    LdapUserObject("Advanced", ManagementServer.class, String.class, "ldap.user.object", "inetOrgPerson", "Sets the object type of users within LDAP", null),
+
 	// VMSnapshots
     VMSnapshotMax("Advanced", VMSnapshotManager.class, Integer.class, "vmsnapshot.max", "10", "Maximum vm snapshots for a vm", null),
     VMSnapshotCreateWait("Advanced", VMSnapshotManager.class, Integer.class, "vmsnapshot.create.wait", "1800", "In second, timeout for create vm snapshot", null),
@@ -616,4 +626,4 @@ public enum Config {
     public static List<Config> getConfigListByScope(String scope) {
         return _scopeLevelConfigsMap.get(scope);
     }
-}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/server/src/com/cloud/configuration/ConfigurationManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java
index b2dedb3..1243fb8 100755
--- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java
+++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java
@@ -43,10 +43,7 @@ import org.apache.log4j.Logger;
 import org.springframework.stereotype.Component;
 
 import org.apache.cloudstack.acl.SecurityChecker;
-import org.apache.cloudstack.api.ApiConstants.LDAPParams;
 import org.apache.cloudstack.api.command.admin.config.UpdateCfgCmd;
-import org.apache.cloudstack.api.command.admin.ldap.LDAPConfigCmd;
-import org.apache.cloudstack.api.command.admin.ldap.LDAPRemoveCmd;
 import org.apache.cloudstack.api.command.admin.network.CreateNetworkOfferingCmd;
 import org.apache.cloudstack.api.command.admin.network.DeleteNetworkOfferingCmd;
 import org.apache.cloudstack.api.command.admin.network.UpdateNetworkOfferingCmd;
@@ -1544,175 +1541,6 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
 
     @Override
     @DB
-    public boolean removeLDAP(LDAPRemoveCmd cmd) {
-        _configDao.expunge(LDAPParams.hostname.toString());
-        _configDao.expunge(LDAPParams.port.toString());
-        _configDao.expunge(LDAPParams.queryfilter.toString());
-        _configDao.expunge(LDAPParams.searchbase.toString());
-        _configDao.expunge(LDAPParams.usessl.toString());
-        _configDao.expunge(LDAPParams.dn.toString());
-        _configDao.expunge(LDAPParams.passwd.toString());
-        _configDao.expunge(LDAPParams.truststore.toString());
-        _configDao.expunge(LDAPParams.truststorepass.toString());
-        return true;
-    }
-
-    @Override
-    @DB
-    public LDAPConfigCmd listLDAPConfig(LDAPConfigCmd cmd) {
-        String hostname = _configDao.getValue(LDAPParams.hostname.toString());
-        cmd.setHostname(hostname == null ? "" : hostname);
-        String port = _configDao.getValue(LDAPParams.port.toString());
-        cmd.setPort(port == null ? 0 : Integer.valueOf(port));
-        String queryFilter = _configDao.getValue(LDAPParams.queryfilter.toString());
-        cmd.setQueryFilter(queryFilter == null ? "" : queryFilter);
-        String searchBase = _configDao.getValue(LDAPParams.searchbase.toString());
-        cmd.setSearchBase(searchBase == null ? "" : searchBase);
-        String useSSL = _configDao.getValue(LDAPParams.usessl.toString());
-        cmd.setUseSSL(useSSL == null ? Boolean.FALSE : Boolean.valueOf(useSSL));
-        String binddn = _configDao.getValue(LDAPParams.dn.toString());
-        cmd.setBindDN(binddn == null ? "" : binddn);
-        String truststore = _configDao.getValue(LDAPParams.truststore.toString());
-        cmd.setTrustStore(truststore == null ? "" : truststore);
-        return cmd;
-    }
-
-    @Override
-    @DB
-    public boolean updateLDAP(LDAPConfigCmd cmd) {
-        try {
-            // set the ldap details in the zone details table with a zone id of
-            // -12
-            String hostname = cmd.getHostname();
-            Integer port = cmd.getPort();
-            String queryFilter = cmd.getQueryFilter();
-            String searchBase = cmd.getSearchBase();
-            Boolean useSSL = cmd.getUseSSL();
-            String bindDN = cmd.getBindDN();
-            String bindPasswd = cmd.getBindPassword();
-            String trustStore = cmd.getTrustStore();
-            String trustStorePassword = cmd.getTrustStorePassword();
-
-            if (bindDN != null && bindPasswd == null) {
-                throw new InvalidParameterValueException(
-                        "If you specify a bind name then you need to provide bind password too.");
-            }
-
-            // check query filter if it contains valid substitution
-            if (!queryFilter.contains("%u") && !queryFilter.contains("%n") && !queryFilter.contains("%e")) {
-                throw new InvalidParameterValueException(
-                        "QueryFilter should contain at least one of the substitutions: %u, %n or %e: " + queryFilter);
-            }
-
-            // check if the info is correct
-            Hashtable<String, String> env = new Hashtable<String, String>(11);
-            env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
-            String protocol = "ldap://";
-            if (useSSL) {
-                env.put(Context.SECURITY_PROTOCOL, "ssl");
-                protocol = "ldaps://";
-                if (trustStore == null || trustStorePassword == null) {
-                    throw new InvalidParameterValueException(
-                            "If you plan to use SSL then you need to configure the trust store.");
-                }
-                System.setProperty("javax.net.ssl.trustStore", trustStore);
-                System.setProperty("javax.net.ssl.trustStorePassword", trustStorePassword);
-            }
-            env.put(Context.PROVIDER_URL, protocol + hostname + ":" + port);
-            if (bindDN != null && bindPasswd != null) {
-                env.put(Context.SECURITY_AUTHENTICATION, "simple");
-                env.put(Context.SECURITY_PRINCIPAL, bindDN);
-                env.put(Context.SECURITY_CREDENTIALS, bindPasswd);
-            }
-            // Create the initial context
-            DirContext ctx = new InitialDirContext(env);
-            ctx.close();
-
-            // store the result in DB Configuration
-            ConfigurationVO cvo = _configDao.findByName(LDAPParams.hostname.toString());
-            if (cvo == null) {
-                cvo = new ConfigurationVO("Hidden", "DEFAULT", "management-server", LDAPParams.hostname.toString(),
-                        null, "Hostname or ip address of the ldap server eg: my.ldap.com");
-            }
-            cvo.setValue(DBEncryptionUtil.encrypt(hostname));
-            _configDao.persist(cvo);
-
-            cvo = _configDao.findByName(LDAPParams.port.toString());
-            if (cvo == null) {
-                cvo = new ConfigurationVO("Hidden", "DEFAULT", "management-server", LDAPParams.port.toString(), null,
-                        "Specify the LDAP port if required, default is 389");
-            }
-            cvo.setValue(DBEncryptionUtil.encrypt(port.toString()));
-            _configDao.persist(cvo);
-
-            cvo = _configDao.findByName(LDAPParams.queryfilter.toString());
-            if (cvo == null) {
-                cvo = new ConfigurationVO("Hidden", "DEFAULT", "management-server", LDAPParams.queryfilter.toString(),
-                        null,
-                        "You specify a query filter here, which narrows down the users, who can be part of this domain");
-            }
-            cvo.setValue(DBEncryptionUtil.encrypt(queryFilter));
-            _configDao.persist(cvo);
-
-            cvo = _configDao.findByName(LDAPParams.searchbase.toString());
-            if (cvo == null) {
-                cvo = new ConfigurationVO("Hidden", "DEFAULT", "management-server", LDAPParams.searchbase.toString(),
-                        null,
-                        "The search base defines the starting point for the search in the directory tree Example:  dc=cloud,dc=com.");
-            }
-            cvo.setValue(DBEncryptionUtil.encrypt(searchBase));
-            _configDao.persist(cvo);
-
-            cvo = _configDao.findByName(LDAPParams.usessl.toString());
-            if (cvo == null) {
-                cvo = new ConfigurationVO("Hidden", "DEFAULT", "management-server", LDAPParams.usessl.toString(), null,
-                        "Check Use SSL if the external LDAP server is configured for LDAP over SSL.");
-            }
-            cvo.setValue(DBEncryptionUtil.encrypt(useSSL.toString()));
-            _configDao.persist(cvo);
-
-            cvo = _configDao.findByName(LDAPParams.dn.toString());
-            if (cvo == null) {
-                cvo = new ConfigurationVO("Hidden", "DEFAULT", "management-server", LDAPParams.dn.toString(), null,
-                        "Specify the distinguished name of a user with the search permission on the directory");
-            }
-            cvo.setValue(DBEncryptionUtil.encrypt(bindDN));
-            _configDao.persist(cvo);
-
-            cvo = _configDao.findByName(LDAPParams.passwd.toString());
-            if (cvo == null) {
-                cvo = new ConfigurationVO("Hidden", "DEFAULT", "management-server", LDAPParams.passwd.toString(), null,
-                        "Enter the password");
-            }
-            cvo.setValue(DBEncryptionUtil.encrypt(bindPasswd));
-            _configDao.persist(cvo);
-
-            cvo = _configDao.findByName(LDAPParams.truststore.toString());
-            if (cvo == null) {
-                cvo = new ConfigurationVO("Hidden", "DEFAULT", "management-server", LDAPParams.truststore.toString(),
-                        null, "Enter the path to trusted keystore");
-            }
-            cvo.setValue(DBEncryptionUtil.encrypt(trustStore));
-            _configDao.persist(cvo);
-
-            cvo = _configDao.findByName(LDAPParams.truststorepass.toString());
-            if (cvo == null) {
-                cvo = new ConfigurationVO("Hidden", "DEFAULT", "management-server",
-                        LDAPParams.truststorepass.toString(), null, "Enter the password for trusted keystore");
-            }
-            cvo.setValue(DBEncryptionUtil.encrypt(trustStorePassword));
-            _configDao.persist(cvo);
-
-            s_logger.debug("The ldap server is configured: " + hostname);
-        } catch (NamingException ne) {
-            throw new InvalidParameterValueException("Naming Exception, check you ldap data ! " + ne.getMessage()
-                    + (ne.getCause() != null ? ("; Caused by:" + ne.getCause().getMessage()) : ""));
-        }
-        return true;
-    }
-
-    @Override
-    @DB
     @ActionEvent(eventType = EventTypes.EVENT_ZONE_EDIT, eventDescription = "editing zone", async = false)
     public DataCenter editZone(UpdateZoneCmd cmd) {
         // Parameter validation as from execute() method in V1

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/server/src/com/cloud/server/ManagementServerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java
index 7ac1254..2a203b4 100755
--- a/server/src/com/cloud/server/ManagementServerImpl.java
+++ b/server/src/com/cloud/server/ManagementServerImpl.java
@@ -87,8 +87,6 @@ import org.apache.cloudstack.api.command.admin.internallb.ListInternalLBVMsCmd;
 import org.apache.cloudstack.api.command.admin.internallb.ListInternalLoadBalancerElementsCmd;
 import org.apache.cloudstack.api.command.admin.internallb.StartInternalLBVMCmd;
 import org.apache.cloudstack.api.command.admin.internallb.StopInternalLBVMCmd;
-import org.apache.cloudstack.api.command.admin.ldap.LDAPConfigCmd;
-import org.apache.cloudstack.api.command.admin.ldap.LDAPRemoveCmd;
 import org.apache.cloudstack.api.command.admin.network.AddNetworkDeviceCmd;
 import org.apache.cloudstack.api.command.admin.network.AddNetworkServiceProviderCmd;
 import org.apache.cloudstack.api.command.admin.network.CreateNetworkOfferingCmd;
@@ -2504,8 +2502,6 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
         cmdList.add(ReconnectHostCmd.class);
         cmdList.add(UpdateHostCmd.class);
         cmdList.add(UpdateHostPasswordCmd.class);
-        cmdList.add(LDAPConfigCmd.class);
-        cmdList.add(LDAPRemoveCmd.class);
         cmdList.add(AddNetworkDeviceCmd.class);
         cmdList.add(AddNetworkServiceProviderCmd.class);
         cmdList.add(CreateNetworkOfferingCmd.class);

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java b/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java
index 80b9e23..29b899c 100755
--- a/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java
+++ b/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java
@@ -26,8 +26,6 @@ import javax.naming.ConfigurationException;
 import javax.naming.NamingException;
 
 import org.apache.cloudstack.api.command.admin.config.UpdateCfgCmd;
-import org.apache.cloudstack.api.command.admin.ldap.LDAPConfigCmd;
-import org.apache.cloudstack.api.command.admin.ldap.LDAPRemoveCmd;
 import org.apache.cloudstack.api.command.admin.network.CreateNetworkOfferingCmd;
 import org.apache.cloudstack.api.command.admin.network.DeleteNetworkOfferingCmd;
 import org.apache.cloudstack.api.command.admin.network.UpdateNetworkOfferingCmd;
@@ -87,8 +85,6 @@ import com.cloud.user.Account;
 import com.cloud.utils.component.ManagerBase;
 import com.cloud.vm.VirtualMachine.Type;
 import org.apache.cloudstack.api.command.admin.config.UpdateCfgCmd;
-import org.apache.cloudstack.api.command.admin.ldap.LDAPConfigCmd;
-import org.apache.cloudstack.api.command.admin.ldap.LDAPRemoveCmd;
 import org.apache.cloudstack.api.command.admin.network.CreateNetworkOfferingCmd;
 import org.apache.cloudstack.api.command.admin.network.DeleteNetworkOfferingCmd;
 import org.apache.cloudstack.api.command.admin.network.UpdateNetworkOfferingCmd;
@@ -384,33 +380,6 @@ public class MockConfigurationManagerImpl extends ManagerBase implements Configu
     }
 
     /* (non-Javadoc)
-     * @see com.cloud.configuration.ConfigurationService#updateLDAP(org.apache.cloudstack.api.commands.LDAPConfigCmd)
-     */
-    @Override
-    public boolean updateLDAP(LDAPConfigCmd cmd) throws NamingException {
-        // TODO Auto-generated method stub
-        return false;
-    }
-
-    /* (non-Javadoc)
-     * @see com.cloud.configuration.ConfigurationService#removeLDAP(org.apache.cloudstack.api.commands.LDAPRemoveCmd)
-     */
-    @Override
-    public boolean removeLDAP(LDAPRemoveCmd cmd) {
-        // TODO Auto-generated method stub
-        return false;
-    }
-
-    /* (non-Javadoc)
-     * @see com.cloud.configuration.ConfigurationService#listLDAPConfig(org.apache.cloudstack.api.commands.LDAPConfigCmd)
-     */
-    @Override
-    public LDAPConfigCmd listLDAPConfig(LDAPConfigCmd cmd) {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    /* (non-Javadoc)
      * @see com.cloud.configuration.ConfigurationService#isOfferingForVpc(com.cloud.offering.NetworkOffering)
      */
     @Override

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/setup/db/db/schema-410to420.sql
----------------------------------------------------------------------
diff --git a/setup/db/db/schema-410to420.sql b/setup/db/db/schema-410to420.sql
index 82ca403..f4cd1b1 100644
--- a/setup/db/db/schema-410to420.sql
+++ b/setup/db/db/schema-410to420.sql
@@ -1462,7 +1462,7 @@ CREATE VIEW `cloud`.`disk_offering_view` AS
         disk_offering.iops_write_rate,
         disk_offering.sort_key,
         disk_offering.type,
-	disk_offering.display_offering,
+  disk_offering.display_offering,
         domain.id domain_id,
         domain.uuid domain_uuid,
         domain.name domain_name,
@@ -1516,7 +1516,7 @@ CREATE VIEW `cloud`.`user_vm_view` AS
         data_center.uuid data_center_uuid,
         data_center.name data_center_name,
         data_center.is_security_group_enabled security_group_enabled,
-		data_center.networktype data_center_type,
+    data_center.networktype data_center_type,
         host.id host_id,
         host.uuid host_uuid,
         host.name host_name,
@@ -1672,7 +1672,7 @@ CREATE VIEW `cloud`.`volume_view` AS
         volumes.attached,
         volumes.removed,
         volumes.pod_id,
-	volumes.display_volume,
+  volumes.display_volume,
         volumes.format,
         account.id account_id,
         account.uuid account_uuid,
@@ -1688,7 +1688,7 @@ CREATE VIEW `cloud`.`volume_view` AS
         data_center.id data_center_id,
         data_center.uuid data_center_uuid,
         data_center.name data_center_name,
-	data_center.networktype data_center_type,
+  data_center.networktype data_center_type,
         vm_instance.id vm_id,
         vm_instance.uuid vm_uuid,
         vm_instance.name vm_name,
@@ -1820,7 +1820,7 @@ CREATE VIEW `cloud`.`template_view` AS
         data_center.name data_center_name,
         launch_permission.account_id lp_account_id,
         template_store_ref.store_id,
-		image_store.scope as store_scope,
+    image_store.scope as store_scope,
         template_store_ref.state,
         template_store_ref.download_state,
         template_store_ref.download_pct,
@@ -1840,7 +1840,7 @@ CREATE VIEW `cloud`.`template_view` AS
         resource_tags.resource_uuid tag_resource_uuid,
         resource_tags.resource_type tag_resource_type,
         resource_tags.customer tag_customer,
-		CONCAT(vm_template.id, '_', IFNULL(data_center.id, 0)) as temp_zone_pair
+    CONCAT(vm_template.id, '_', IFNULL(data_center.id, 0)) as temp_zone_pair
     from
         `cloud`.`vm_template`
             inner join
@@ -1859,7 +1859,7 @@ CREATE VIEW `cloud`.`template_view` AS
         `cloud`.`template_store_ref` ON template_store_ref.template_id = vm_template.id and template_store_ref.store_role = 'Image'
             left join
         `cloud`.`image_store` ON image_store.removed is NULL AND template_store_ref.store_id is not NULL AND image_store.id = template_store_ref.store_id 
-        	left join
+          left join
         `cloud`.`template_zone_ref` ON template_zone_ref.template_id = vm_template.id AND template_store_ref.store_id is NULL AND template_zone_ref.removed is null    
             left join
         `cloud`.`data_center` ON (image_store.data_center_id = data_center.id OR template_zone_ref.zone_id = data_center.id)
@@ -2048,7 +2048,7 @@ INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'manag
 INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'vm.disk.throttling.bytes_write_rate', 0, 'Default disk I/O write rate in bytes per second allowed in User vm\'s disk. ');
 
 -- Re-enable foreign key checking, at the end of the upgrade path
-SET foreign_key_checks = 1;			
+SET foreign_key_checks = 1;
 
 UPDATE `cloud`.`snapshot_policy` set uuid=id WHERE uuid is NULL;
 #update shared sg enabled network with not null name in Advance Security Group enabled network
@@ -2142,10 +2142,25 @@ CREATE VIEW `cloud`.`project_view` AS
             left join
         `cloud`.`project_account` pacct ON projects.id = pacct.project_id;
 
+INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'ldap.bind.principal', NULL, 'Specifies the bind principal to use for bind to LDAP');
+INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'ldap.bind.password', NULL, 'Specifies the password to use for binding to LDAP');
+INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'ldap.username.attribute', 'uid', 'Sets the username attribute used within LDAP');
+INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'ldap.email.attribute', 'mail', 'Sets the email attribute used within LDAP');
+INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'ldap.firstname.attribute', 'givenname', 'Sets the firstname attribute used within LDAP');
+INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'ldap.lastname.attribute', 'sn', 'Sets the lastname attribute used within LDAP');
+INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'ldap.user.object', 'inetOrgPerson', 'Sets the object type of users within LDAP');
+INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'ldap.basedn', NULL, 'Sets the basedn for LDAP');
+
+CREATE TABLE `cloud`.`ldap_configuration` (
+  `id` bigint unsigned NOT NULL auto_increment COMMENT 'id',
+  `hostname` varchar(255) NOT NULL COMMENT 'the hostname of the ldap server',
+  `port` int(10) COMMENT 'port that the ldap server is listening on',
+  PRIMARY KEY  (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
 INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Network', 'DEFAULT', 'management-server', 'network.loadbalancer.haproxy.max.conn', '4096', 'Load Balancer(haproxy) maximum number of concurrent connections(global max)');
 
 ALTER TABLE `cloud`.`network_offerings` ADD COLUMN `concurrent_connections` int(10) unsigned COMMENT 'Load Balancer(haproxy) maximum number of concurrent connections(global max)';
-
         
 ALTER TABLE `cloud`.`sync_queue` MODIFY `queue_size` smallint(6) NOT NULL DEFAULT '0' COMMENT 'number of items being processed by the queue';
 ALTER TABLE `cloud`.`sync_queue` MODIFY `queue_size_limit` smallint(6) NOT NULL DEFAULT '1' COMMENT 'max number of items the queue can process concurrently';

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/test/integration/component/test_ldap.py
----------------------------------------------------------------------
diff --git a/test/integration/component/test_ldap.py b/test/integration/component/test_ldap.py
index fc3bd48..83f970b 100644
--- a/test/integration/component/test_ldap.py
+++ b/test/integration/component/test_ldap.py
@@ -44,78 +44,30 @@ class Services:
     def __init__(self):
         self.services = {
             "account": {
-                "email": "test@test.com",
-                "firstname": "test",
-                "lastname": "t",
-                "username": "test",
-                "password": "password",
+                "email": "rmurphy@cloudstack.org",
+                "firstname": "Ryan",
+                "lastname": "Murphy",
+                "username": "rmurphy",
+                "password": "internalcloudstackpassword",
                 },
-            "ldapCon_1":#valid values&Query filter as email.
+            "ldapConfiguration_1":
                 {
-                    "ldapHostname": "10.147.38.163",
-                    "port": "389",
-                    "binddn": "CN=test,CN=Users,DC=hyd-qa,DC=com",
-                    "bindpass": "aaaa_1111",
-                    "queryfilter": "(&(mail=%e))",
-                    "searchbase": "CN=Users,DC=hyd-qa,DC=com",
-                    "ldapusername": "test",
-                    "ldappasswd": "aaaa_1111"
-                },
-            "ldapCon_2": ##valid values&Query filter as displayName.
-                {
-                    "ldapHostname": "10.147.38.163",
-                    "port": "389",
-                    "binddn": "CN=test,CN=Users,DC=hyd-qa,DC=com",
-                    "bindpass": "aaaa_1111",
-                    "queryfilter": "(&(displayName=%u))",
-                    "searchbase": "CN=Users,DC=hyd-qa,DC=com",
-                    "ldapusername": "test",
-                    "ldappasswd": "aaaa_1111"
-                },
-            "ldapCon_3": #Configuration with missing parameters value(queryfilter)
-                {
-                    "ldapHostname": "10.147.38.163",
-                    "port": "389",
-                    "binddn": "CN=test,CN=Users,DC=hyd-qa,DC=com",
-                    "bindpass": "aaaa_1111",
-                    "queryfilter": "",
-                    "searchbase": "CN=Users,DC=hyd-qa,DC=com",
-                    "ldapusername": "test",
-                    "ldappasswd": "aaaa_1111"
-                },
-
-            "ldapCon_4": #invalid configuration-wrong query filter
-                {
-                    "ldapHostname": "10.147.38.163",
-                    "port": "389",
-                    "binddn": "CN=test,CN=Users,DC=hyd-qa,DC=com",
-                    "bindpass": "aaaa_1111",
-                    "queryfilter": "(&(displayName=%p))",
-                    "searchbase":"CN=Users,DC=hyd-qa,DC=com",
-                    "ldapusername": "test",
-                    "ldappasswd": "aaaa_1111"
-                },
-            "ldapCon_5": #Configuration with invalid ldap credentials
-                {
-                    "ldapHostname": "10.147.38.163",
-                    "port": "389",
-                    "binddn": "CN=test,CN=Users,DC=hyd-qa,DC=com",
-                    "bindpass": "aaaa_1111",
-                    "queryfilter": "(&(displayName=%u))",
-                    "searchbase": "CN=Users,DC=hyd-qa,DC=com",
-                    "ldapusername": "test",
-                    "ldappasswd": "aaaa"
+                "basedn": "dc=cloudstack,dc=org",
+                "emailAttribute": "mail",
+                "realnameAttribute": "cn",
+                "userObject": "inetOrgPerson",
+                "usernameAttribute": "uid",
+                "hostname": "localhost",
+                "port": "10389",
+                "ldapUsername": "rmurphy",
+                "ldapPassword": "password"
                 }
-
-
-
         }
 
 
 class TestLdap(cloudstackTestCase):
     """
-    This test perform registering ldap configuration details in CS and create a user[ldap user] in CS
-     and  validate user credentials against LDAP server:AD
+    This tests attempts to register a LDAP server and authenticate as an LDAP user.
     """
 
     @classmethod
@@ -134,8 +86,6 @@ class TestLdap(cloudstackTestCase):
     @classmethod
     def tearDownClass(cls):
         try:
-            #Cleanup resources used
-            #print "tear down class"
             cleanup_resources(cls.api_client, cls._cleanup)
 
         except Exception as tde:
@@ -144,10 +94,10 @@ class TestLdap(cloudstackTestCase):
 
     def setUp(self):
 
-        self.apiclient = self.testClient.getApiClient()
+        self.apiClient = self.testClient.getApiClient()
 
         self.acct = createAccount.createAccountCmd()
-        self.acct.accounttype = 0 #We need a regular user. admins have accounttype=1
+        self.acct.accounttype = 0
         self.acct.firstname = self.services["account"]["firstname"]
         self.acct.lastname = self.services["account"]["lastname"]
         self.acct.password = self.services["account"]["password"]
@@ -155,208 +105,153 @@ class TestLdap(cloudstackTestCase):
         self.acct.email = self.services["account"]["email"]
         self.acct.account = self.services["account"]["username"]
         self.acct.domainid = 1
-        # mapping ldap user  by creating same user in  cloudstack
-
-        self.acctRes = self.apiclient.createAccount(self.acct)
 
+        self.acctRes = self.apiClient.createAccount(self.acct)
 
         return
 
     def tearDown(self):
 
         try:
-            #Clean up, terminate the created accounts, domains etc
-
             deleteAcct = deleteAccount.deleteAccountCmd()
             deleteAcct.id = self.acctRes.id
 
             acct_name=self.acctRes.name
 
-            self.apiclient.deleteAccount(deleteAcct)
+            self.apiClient.deleteAccount(deleteAcct)
 
             self.debug("Deleted the the following account name %s:" %acct_name)
-            #delete only if ldapconfig registered  in CS
-            if(self.ldapconfRes):
-                deleteldapconfg=ldapRemove.ldapRemoveCmd()
-                res=self.apiclient.ldapRemove(deleteldapconfg)
 
+            if(self.ldapconfRes==1):
+                self._deleteLdapConfiguration(self.services["ldapConfiguration_1"])
 
         except Exception as e:
             raise Exception("Warning: Exception during cleanup : %s" % e)
         return
 
     @attr(tags=["advanced", "basic"])
-    def test_01_configLDAP(self):
-        '''
-        This test is to verify ldapConfig API  with valid  values.(i.e query fileter as email)
-        '''
-        # 1. This test covers ldapConfig  & login API with valid ldap credentials..
-        # require ldap configuration:ldapCon_1
+    def test_01_addLdapConfiguration(self):
+        """
+        This test configures LDAP and attempts to authenticate as a user.
+        """
+
 
         self.debug("start test")
 
-        self.ldapconfRes=self._testldapConfig(self.services["ldapCon_1"])
+        self.ldapconfRes=self._addLdapConfiguration(self.services["ldapConfiguration_1"])
 
         if(self.ldapconfRes==1):
 
+            self.debug("Ldap Configuration was succcessful")
 
-            self.debug("configure ldap successful")
-
-            #validating the user credentials with ldap Server
-            loginRes = self.chkLogin(self.services["ldapCon_1"]["ldapusername"], self.services["ldapCon_1"]["ldappasswd"])
-            self.assertEquals(loginRes,1,"ldap Authentication failed")
+            loginRes = self._checkLogin(self.services["ldapConfiguration_1"]["ldapUsername"],self.services["ldapConfiguration_1"]["ldapPassword"])
+            self.debug(loginRes)
+            self.assertEquals(loginRes,1,"Ldap Authentication")
 
         else:
 
             self.debug("LDAP Configuration failed with exception")
 
-            self.assertEquals(self.ldapconfRes,1,"ldapConfig API failed")
+            self.assertEquals(self.ldapconfRes,1,"addLdapConfiguration failed")
 
 
         self.debug("end test")
 
-    @attr(tags=["advanced", "basic"])
-    def test_02_configLDAP(self):
-        '''
-        This test is to verify ldapConfig API  with valid  values.(i.e query fileter as displayName)
-        '''
-
-        # 1. This test covers ldapConfig  & login API with valid ldap credentials.
-        # 2. require ldap configuration:ldapCon_2
+    def _addLdapConfiguration(self,ldapConfiguration):
 
-        self.debug("start test")
-        self.ldapconfRes=self._testldapConfig(self.services["ldapCon_2"])
-        self.assertEquals(self.ldapconfRes,1,"ldapConfig API failed")
-        if(self.ldapconfRes==1):
-            self.debug("configure ldap successful")
-            #validating the user credentials with ldap Server
-            loginRes = self.chkLogin(self.services["ldapCon_2"]["ldapusername"], self.services["ldapCon_2"]["ldappasswd"])
-            self.assertEquals(loginRes,1,"ldap Authentication failed")
-        else:
-            self.debug("LDAP Configuration failed with exception")
-        self.debug("end test")
+        """
 
-    @attr(tags=["advanced", "basic"])
-    def test_03_configLDAP(self):
+        :param ldapConfiguration
 
-        '''
-        This test is to verify ldapConfig API  with missing config parameters value(i.queryfilter)
-        '''
+        """
 
-        # 1. Issue ldapConfig API with no ldap config parameter value and check behavior
-        # 2. require ldap configuration:ldapCon_3
+        # Setup Global settings
 
-        self.debug("start test...")
-        self.ldapconfRes=self._testldapConfig(self.services["ldapCon_3"])
-        self.assertEquals(self.ldapconfRes,0,"LDAP configuration successful with invalid value.API failed")
-        self.debug("end test")
-    @attr(tags=["advanced", "basic"])
-    def test_04_configLDAP(self):
-        '''
-        This test is to verify ldapConfig API with invalid configuration values(by passing wrong query filter)
-        '''
-        # 1. calling ldapConfig API with invalid query filter value and check behavior
-        # 2. require ldap configuration:ldapCon_4
+        updateConfigurationCmd = updateConfiguration.updateConfigurationCmd()
+        updateConfigurationCmd.name = "ldap.basedn"
+        updateConfigurationCmd.value = ldapConfiguration['basedn']
+        updateConfigurationResponse = self.apiClient.updateConfiguration(updateConfigurationCmd)
+        self.debug("updated the parameter %s with value %s"%(updateConfigurationResponse.name, updateConfigurationResponse.value))
 
-        self.debug("start test...")
-        self.ldapconfRes=self._testldapConfig(self.services["ldapCon_4"])
-        self.assertEquals(self.ldapconfRes,0,"API failed")
+        updateConfigurationCmd = updateConfiguration.updateConfigurationCmd()
+        updateConfigurationCmd.name = "ldap.email.attribute"
+        updateConfigurationCmd.value = ldapConfiguration['emailAttribute']
+        updateConfigurationResponse = self.apiClient.updateConfiguration(updateConfigurationCmd)
+        self.debug("updated the parameter %s with value %s"%(updateConfigurationResponse.name, updateConfigurationResponse.value))
 
 
-    @attr(tags=["advanced", "basic"])
-    def test_05_configLDAP(self):
+        updateConfigurationCmd = updateConfiguration.updateConfigurationCmd()
+        updateConfigurationCmd.name = "ldap.realname.attribute"
+        updateConfigurationCmd.value = ldapConfiguration['realnameAttribute']
+        updateConfigurationResponse = self.apiClient.updateConfiguration(updateConfigurationCmd)
+        self.debug("updated the parameter %s with value %s"%(updateConfigurationResponse.name, updateConfigurationResponse.value))
 
-        '''
-        This test is to verify login API functionality by passing wrong ldap credentials
-        '''
-        # 1.This script first  configure the ldap and validates the user credentials using login API
-        # 2. require ldap configuration:ldapCon_5
 
+        updateConfigurationCmd = updateConfiguration.updateConfigurationCmd()
+        updateConfigurationCmd.name = "ldap.user.object"
+        updateConfigurationCmd.value = ldapConfiguration['userObject']
+        updateConfigurationResponse = self.apiClient.updateConfiguration(updateConfigurationCmd)
+        self.debug("updated the parameter %s with value %s"%(updateConfigurationResponse.name, updateConfigurationResponse.value))
 
-        self.debug("start test")
-        self.ldapconfRes=self._testldapConfig(self.services["ldapCon_5"])
-        self.assertEquals(self.ldapconfRes,1,"API failed")
-        #validating the cloudstack user credentials with ldap Server
-        loginRes = self.chkLogin(self.services["ldapCon_5"]["ldapusername"], self.services["ldapCon_5"]["ldappasswd"])
-        self.assertNotEqual(loginRes,1,"login API failed")
-        self.debug("end test")
 
-    @attr(tags=["advanced", "basic"])
-    def test_06_removeLDAP(self):
-        '''
-        This test is to verify ldapRemove API functionality
-        '''
-        # 1. This script fist configures ldap and removes the configured ldap values
-        # 2. require ldap configuration:ldapCon_1
+        updateConfigurationCmd = updateConfiguration.updateConfigurationCmd()
+        updateConfigurationCmd.name = "ldap.username.attribute"
+        updateConfigurationCmd.value = ldapConfiguration['usernameAttribute']
+        updateConfigurationResponse = self.apiClient.updateConfiguration(updateConfigurationCmd)
+        self.debug("updated the parameter %s with value %s"%(updateConfigurationResponse.name, updateConfigurationResponse.value))
 
+        self.debug("start addLdapConfiguration test")
 
-        self.debug("start test")
-        self.ldapconfRes=self._testldapConfig(self.services["ldapCon_1"])
-        if(self.ldapconfRes==1):
-            self.debug("ldap configured successfully")
-            deleteldapconfg=ldapRemove.ldapRemoveCmd()
-            res=self.apiclient.ldapRemove(deleteldapconfg)
-            self.debug("ldap removed successfully")
-            self.ldapconfRes=0
-        else:
+        ldapServer = addLdapConfiguration.addLdapConfigurationCmd()
+        ldapServer.hostname = ldapConfiguration['hostname']
+        ldapServer.port = ldapConfiguration['port']
 
-            self.debug("LDAP Configuration failed with exception")
-            self.assertEquals(self.ldapconfRes,0,"ldapconfig API failed")
-        self.debug("end test")
+        self.debug("calling addLdapConfiguration API command")
+        try:
+            self.apiClient.addLdapConfiguration(ldapServer)
+            self.debug("addLdapConfiguration was successful")
+            return 1
+        except Exception, e:
+            self.debug("addLdapConfiguration failed %s" %e)
+            return 0
 
-    def _testldapConfig(self,ldapSrvD):
+    def _deleteLdapConfiguration(self,ldapConfiguration):
 
         """
 
-        :param ldapSrvD
-
+        :param ldapConfiguration
 
         """
-        #This Method takes dictionary as parameter,
-        # reads the ldap configuration values from the passed dictionary and
-        # register the ldapconfig detail in cloudstack
-        # & return true or false based on ldapconfig API response
-
-        self.debug("start ldapconfig  test")
-        #creating the  ldapconfig cmd object
-        lpconfig = ldapConfig.ldapConfigCmd()
-        #Config the ldap server by assigning the ldapconfig dict variable values to ldapConfig object
-        lpconfig.hostname = ldapSrvD["ldapHostname"]
-        lpconfig.port = ldapSrvD["port"]
-        lpconfig.binddn = ldapSrvD["binddn"]
-        lpconfig.bindpass = ldapSrvD["bindpass"]
-        lpconfig.searchbase = ldapSrvD["searchbase"]
-        lpconfig.queryfilter = ldapSrvD["queryfilter"]
-
-        #end of assigning the variables
-
-        #calling the ldapconfig Api
-        self.debug("calling ldapconfig API")
+
+        ldapServer = deleteLdapConfiguration.deleteLdapConfigurationCmd()
+        ldapServer.hostname = ldapConfiguration["hostname"]
+
         try:
-            lpconfig1 = self.apiclient.ldapConfig(lpconfig)
-            self.debug("ldapconfig API succesfful")
+            self.apiClient.deleteLdapConfiguration(ldapServer)
+            self.debug("deleteLdapConfiguration was successful")
             return 1
         except Exception, e:
-            self.debug("ldapconfig API failed %s" %e)
+            self.debug("deleteLdapConfiguration failed %s" %e)
             return 0
 
-    def chkLogin(self, username, password):
+    def _checkLogin(self, username, password):
         """
 
         :param username:
         :param password:
 
         """
-        self.debug("login test")
+        self.debug("Attempting to login.")
 
         try:
-            login1 = login.loginCmd()
-            login1.username = username
-            login1.password = password
-            loginRes = self.apiclient.login(login1)
+            loginParams = login.loginCmd()
+            loginParams.username = username
+            loginParams.password = password
+            loginRes = self.apiClient.login(loginParams)
             self.debug("login response %s" % loginRes)
             if loginRes is None:
                 self.debug("login not successful")
+                return 0
             else:
                 self.debug("login successful")
                 return 1

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/tools/apidoc/gen_toc.py
----------------------------------------------------------------------
diff --git a/tools/apidoc/gen_toc.py b/tools/apidoc/gen_toc.py
index 33a7e75..f582340 100644
--- a/tools/apidoc/gen_toc.py
+++ b/tools/apidoc/gen_toc.py
@@ -118,7 +118,7 @@ known_categories = {
     'TrafficType': 'Usage',
     'Product': 'Product',
     'LB': 'Load Balancer',
-    'ldap': 'LDAP',
+    'Ldap': 'LDAP',
     'Swift': 'Swift',
     'S3' : 'S3',
     'SecondaryStorage': 'Host',

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/ui/css/cloudstack3.css
----------------------------------------------------------------------
diff --git a/ui/css/cloudstack3.css b/ui/css/cloudstack3.css
index 50cfbd4..93658db 100644
--- a/ui/css/cloudstack3.css
+++ b/ui/css/cloudstack3.css
@@ -5834,7 +5834,7 @@ label.error {
 .multi-wizard .buttons {
   width: 100%;
   position: absolute;
-  top: 519px;
+  bottom: 10px;
   left: 0px;
 }
 
@@ -12267,3 +12267,85 @@ div.ui-dialog div.autoscaler div.field-group div.form-container form div.form-it
   color: #0000FF !important;
 }
 
+.accounts-wizard table {
+  margin: 0;
+  width: 100%;
+  table-layout: fixed;
+}
+
+.accounts-wizard td:last-child {
+  border: none;
+}
+
+.accounts-wizard tbody tr:nth-child(even) {
+  background: #DFE1E3;
+}
+
+.accounts-wizard tbody tr:nth-child(odd) {
+  background: #F2F0F0;
+}
+
+.accounts-wizard .content {
+  display: inline-block;
+}
+
+.accounts-wizard .content:last-child {
+  margin-left: 14px;
+}
+
+.accounts-wizard .select-container {
+  overflow: auto;
+}
+
+.accounts-wizard .input-area{
+  width: 320px;
+  font-size: 15px;
+  color: #485867;
+  text-shadow: 0px 2px 1px #FFFFFF;
+}
+
+.ldap-account-choice {
+  border: none !important;
+  border-radius: 0 0 0 0 !important;
+}
+
+.manual-account-details .name {
+  margin-top: 2px;
+  width: 100px;
+  float: left;
+  padding-bottom:10px;
+}
+
+.manual-account-details .value {
+  float: left;
+}
+
+.manual-account-details .form-item:after {
+  content: ".";
+  display: block;
+  clear: both;
+  visibility: hidden;
+  line-height: 0;
+  height: 0;
+}
+
+.manual-account-details .form-item {
+  padding: 10px;
+  width: 278px;
+}
+
+.manual-account-details select, .manual-account-details input {
+  width: 150px;
+}
+
+.manual-account-details > *:nth-child(even) {
+  background: #DFE1E3;
+}
+
+.manual-account-details > *:nth-child(odd) {
+  background: #F2F0F0;
+}
+
+.manual-account-details .value {
+  display: inline-block;
+}
\ No newline at end of file


[4/4] git commit: updated refs/heads/ldapplugin to eaa4143

Posted by ap...@apache.org.
Merge LDAPPlugin

Signed-off-by: Abhinandan Prateek <ap...@apache.org>


Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/eaa41433
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/eaa41433
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/eaa41433

Branch: refs/heads/ldapplugin
Commit: eaa41433715ba1a1137ad9f346ea8569e31e4560
Parents: 1f64354
Author: Ian Duffy <ia...@ianduffy.ie>
Authored: Thu Jul 25 10:24:13 2013 +0100
Committer: Abhinandan Prateek <ap...@apache.org>
Committed: Thu Jul 25 17:54:52 2013 +0530

----------------------------------------------------------------------
 .../configuration/ConfigurationService.java     |   8 -
 .../org/apache/cloudstack/api/ApiConstants.java |  11 -
 .../cloudstack/api/ResponseGenerator.java       |   3 -
 .../api/command/admin/ldap/LDAPConfigCmd.java   | 206 ------------
 .../api/command/admin/ldap/LDAPRemoveCmd.java   |  71 -----
 .../api/response/LDAPConfigResponse.java        | 105 ------
 .../api/response/LDAPRemoveResponse.java        |  26 --
 client/tomcatconf/applicationContext.xml.in     |   7 +-
 client/tomcatconf/commands.properties.in        |  18 +-
 client/tomcatconf/componentContext.xml.in       |   4 +-
 client/tomcatconf/nonossComponentContext.xml.in |   4 +-
 .../tomcatconf/simulatorComponentContext.xml.in |   4 +-
 .../ratelimit/ApiRateLimitServiceImpl.java      |   1 -
 plugins/user-authenticators/ldap/pom.xml        | 119 +++++--
 .../server/auth/LDAPUserAuthenticator.java      | 173 ----------
 .../api/command/LdapAddConfigurationCmd.java    |  80 +++++
 .../api/command/LdapCreateAccount.java          | 141 ++++++++
 .../api/command/LdapDeleteConfigurationCmd.java |  76 +++++
 .../api/command/LdapListAllUsersCmd.java        |  88 +++++
 .../api/command/LdapListConfigurationCmd.java   | 106 ++++++
 .../api/command/LdapUserSearchCmd.java          |  96 ++++++
 .../api/response/LdapConfigurationResponse.java |  63 ++++
 .../api/response/LdapUserResponse.java          |  98 ++++++
 .../cloudstack/ldap/LdapAuthenticator.java      |  73 +++++
 .../cloudstack/ldap/LdapConfiguration.java      | 116 +++++++
 .../cloudstack/ldap/LdapConfigurationVO.java    |  66 ++++
 .../cloudstack/ldap/LdapContextFactory.java     | 103 ++++++
 .../org/apache/cloudstack/ldap/LdapManager.java |  50 +++
 .../apache/cloudstack/ldap/LdapManagerImpl.java | 205 ++++++++++++
 .../org/apache/cloudstack/ldap/LdapUser.java    |  75 +++++
 .../apache/cloudstack/ldap/LdapUserManager.java |  98 ++++++
 .../org/apache/cloudstack/ldap/LdapUtils.java   |  61 ++++
 .../ldap/NoLdapUserMatchingQueryException.java  |  32 ++
 .../ldap/NoSuchLdapUserException.java           |  31 ++
 .../ldap/dao/LdapConfigurationDao.java          |  30 ++
 .../ldap/dao/LdapConfigurationDaoImpl.java      |  66 ++++
 .../ldap/BasicNamingEnumerationImpl.groovy      |  56 ++++
 .../ldap/LdapAddConfigurationCmdSpec.groovy     |  89 ++++++
 .../ldap/LdapAuthenticatorSpec.groovy           |  90 ++++++
 .../ldap/LdapConfigurationDaoImplSpec.groovy    |  29 ++
 .../ldap/LdapConfigurationResponseSpec.groovy   |  49 +++
 .../ldap/LdapConfigurationSpec.groovy           | 181 +++++++++++
 .../cloudstack/ldap/LdapConfigurationVO.groovy  |  36 +++
 .../ldap/LdapContextFactorySpec.groovy          | 134 ++++++++
 .../ldap/LdapDeleteConfigurationCmdSpec.groovy  |  68 ++++
 .../ldap/LdapListAllUsersCmdSpec.groovy         |  72 +++++
 .../ldap/LdapListConfigurationCmdSpec.groovy    | 100 ++++++
 .../cloudstack/ldap/LdapManagerImplSpec.groovy  | 319 +++++++++++++++++++
 .../ldap/LdapSearchUserCmdSpec.groovy           |  72 +++++
 .../cloudstack/ldap/LdapUserManagerSpec.groovy  | 207 ++++++++++++
 .../cloudstack/ldap/LdapUserResponseSpec.groovy |  67 ++++
 .../apache/cloudstack/ldap/LdapUserSpec.groovy  |  79 +++++
 .../apache/cloudstack/ldap/LdapUtilsSpec.groovy |  68 ++++
 .../NoLdapUserMatchingQueryExceptionSpec.groovy |  30 ++
 .../ldap/NoSuchLdapUserExceptionSpec.groovy     |  30 ++
 .../ldap/test/resources/cloudstack.org.ldif     |  19 ++
 server/src/com/cloud/api/ApiResponseHelper.java |  15 -
 server/src/com/cloud/configuration/Config.java  |  12 +-
 .../configuration/ConfigurationManagerImpl.java | 172 ----------
 .../com/cloud/server/ManagementServerImpl.java  |   4 -
 .../cloud/vpc/MockConfigurationManagerImpl.java |  31 --
 setup/db/db/schema-410to420.sql                 |  33 +-
 test/integration/component/test_ldap.py         | 287 ++++++-----------
 tools/apidoc/gen_toc.py                         |   2 +-
 ui/css/cloudstack3.css                          |  84 ++++-
 ui/index.jsp                                    |  61 +++-
 ui/scripts/accounts.js                          |  13 +-
 ui/scripts/accountsWizard.js                    | 158 +++++++++
 ui/scripts/globalSettings.js                    | 143 +--------
 ui/scripts/ui-custom/accountsWizard.js          | 116 +++++++
 70 files changed, 4230 insertions(+), 1210 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/api/src/com/cloud/configuration/ConfigurationService.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/configuration/ConfigurationService.java b/api/src/com/cloud/configuration/ConfigurationService.java
index 381fcad..cc6e47f 100644
--- a/api/src/com/cloud/configuration/ConfigurationService.java
+++ b/api/src/com/cloud/configuration/ConfigurationService.java
@@ -26,8 +26,6 @@ import com.cloud.exception.ConcurrentOperationException;
 import com.cloud.exception.ResourceUnavailableException;
 import com.cloud.exception.ResourceAllocationException;
 import org.apache.cloudstack.api.command.admin.config.UpdateCfgCmd;
-import org.apache.cloudstack.api.command.admin.ldap.LDAPConfigCmd;
-import org.apache.cloudstack.api.command.admin.ldap.LDAPRemoveCmd;
 import org.apache.cloudstack.api.command.admin.network.CreateNetworkOfferingCmd;
 import org.apache.cloudstack.api.command.admin.network.DeleteNetworkOfferingCmd;
 import org.apache.cloudstack.api.command.admin.network.UpdateNetworkOfferingCmd;
@@ -272,12 +270,6 @@ public interface ConfigurationService {
 
     DiskOffering getDiskOffering(long diskOfferingId);
 
-    boolean updateLDAP(LDAPConfigCmd cmd) throws NamingException;
-
-	boolean removeLDAP(LDAPRemoveCmd cmd);
-
-    LDAPConfigCmd listLDAPConfig(LDAPConfigCmd cmd);
-
     /**
      * @param offering
      * @return

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/api/src/org/apache/cloudstack/api/ApiConstants.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/api/ApiConstants.java b/api/src/org/apache/cloudstack/api/ApiConstants.java
index 121403f..4fbbf52 100755
--- a/api/src/org/apache/cloudstack/api/ApiConstants.java
+++ b/api/src/org/apache/cloudstack/api/ApiConstants.java
@@ -525,15 +525,4 @@ public class ApiConstants {
     public enum VMDetails {
         all, group, nics, stats, secgrp, tmpl, servoff, iso, volume, min, affgrp;
     }
-
-    public enum LDAPParams {
-        hostname, port, usessl, queryfilter, searchbase, dn, passwd, truststore, truststorepass;
-
-        @Override
-        public String toString() {
-            return "ldap." + name();
-        }
-    }
-
-
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/api/src/org/apache/cloudstack/api/ResponseGenerator.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/api/ResponseGenerator.java b/api/src/org/apache/cloudstack/api/ResponseGenerator.java
index d8d07cb..372fc9d 100644
--- a/api/src/org/apache/cloudstack/api/ResponseGenerator.java
+++ b/api/src/org/apache/cloudstack/api/ResponseGenerator.java
@@ -129,7 +129,6 @@ import org.apache.cloudstack.api.response.IpForwardingRuleResponse;
 import org.apache.cloudstack.api.response.IsolationMethodResponse;
 import org.apache.cloudstack.api.response.LBHealthCheckResponse;
 import org.apache.cloudstack.api.response.LBStickinessResponse;
-import org.apache.cloudstack.api.response.LDAPConfigResponse;
 import org.apache.cloudstack.api.response.LoadBalancerResponse;
 import org.apache.cloudstack.api.response.NetworkACLItemResponse;
 import org.apache.cloudstack.api.response.NetworkACLResponse;
@@ -363,8 +362,6 @@ public interface ResponseGenerator {
 
     VirtualRouterProviderResponse createVirtualRouterProviderResponse(VirtualRouterProvider result);
 
-    LDAPConfigResponse createLDAPConfigResponse(String hostname, Integer port, Boolean useSSL, String queryFilter, String baseSearch, String dn);
-
     StorageNetworkIpRangeResponse createStorageNetworkIpRangeResponse(StorageNetworkIpRange result);
 
     RegionResponse createRegionResponse(Region region);

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/api/src/org/apache/cloudstack/api/command/admin/ldap/LDAPConfigCmd.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/api/command/admin/ldap/LDAPConfigCmd.java b/api/src/org/apache/cloudstack/api/command/admin/ldap/LDAPConfigCmd.java
deleted file mode 100644
index 6f8b7b1..0000000
--- a/api/src/org/apache/cloudstack/api/command/admin/ldap/LDAPConfigCmd.java
+++ /dev/null
@@ -1,206 +0,0 @@
-// 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.
-package org.apache.cloudstack.api.command.admin.ldap;
-
-
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.naming.NamingException;
-
-import org.apache.cloudstack.api.APICommand;
-import org.apache.cloudstack.api.ApiConstants;
-import org.apache.cloudstack.api.BaseCmd;
-import org.apache.cloudstack.api.Parameter;
-import org.apache.cloudstack.api.ServerApiException;
-import org.apache.cloudstack.api.response.LDAPConfigResponse;
-import org.apache.cloudstack.api.response.ListResponse;
-import org.apache.commons.lang.StringEscapeUtils;
-import org.apache.log4j.Logger;
-
-import com.cloud.exception.ConcurrentOperationException;
-import com.cloud.exception.InsufficientCapacityException;
-import com.cloud.exception.ResourceAllocationException;
-import com.cloud.exception.ResourceUnavailableException;
-import com.cloud.exception.InvalidParameterValueException;
-import com.cloud.user.Account;
-
-@APICommand(name = "ldapConfig", description="Configure the LDAP context for this site.", responseObject=LDAPConfigResponse.class, since="3.0.0")
-public class LDAPConfigCmd extends BaseCmd  {
-    public static final Logger s_logger = Logger.getLogger(LDAPConfigCmd.class.getName());
-
-    private static final String s_name = "ldapconfigresponse";
-
-    /////////////////////////////////////////////////////
-    //////////////// API parameters /////////////////////
-    /////////////////////////////////////////////////////
-    @Parameter(name=ApiConstants.LIST_ALL, type=CommandType.BOOLEAN,  description="If true return current LDAP configuration")
-    private Boolean listAll;
-    
-    @Parameter(name=ApiConstants.HOST_NAME, type=CommandType.STRING,  description="Hostname or ip address of the ldap server eg: my.ldap.com")
-    private String hostname;
-
-    @Parameter(name=ApiConstants.PORT, type=CommandType.INTEGER, description="Specify the LDAP port if required, default is 389.")
-    private Integer port=0;
-
-    @Parameter(name=ApiConstants.USE_SSL, type=CommandType.BOOLEAN, description="Check Use SSL if the external LDAP server is configured for LDAP over SSL.")
-    private Boolean useSSL;
-
-    @Parameter(name=ApiConstants.SEARCH_BASE, type=CommandType.STRING,  description="The search base defines the starting point for the search in the directory tree Example:  dc=cloud,dc=com.")
-    private String searchBase;
-
-    @Parameter(name=ApiConstants.QUERY_FILTER, type=CommandType.STRING,  description="You specify a query filter here, which narrows down the users, who can be part of this domain.")
-    private String queryFilter;
-
-    @Parameter(name=ApiConstants.BIND_DN, type=CommandType.STRING, description="Specify the distinguished name of a user with the search permission on the directory.")
-    private String bindDN;
-
-    @Parameter(name=ApiConstants.BIND_PASSWORD, type=CommandType.STRING, description="Enter the password.")
-    private String bindPassword;
-
-    @Parameter(name=ApiConstants.TRUST_STORE, type=CommandType.STRING, description="Enter the path to trust certificates store.")
-    private String trustStore;
-
-    @Parameter(name=ApiConstants.TRUST_STORE_PASSWORD, type=CommandType.STRING, description="Enter the password for trust store.")
-    private String trustStorePassword;
-
-    /////////////////////////////////////////////////////
-    /////////////////// Accessors ///////////////////////
-    /////////////////////////////////////////////////////
-
-    public Boolean getListAll() {
-    	return listAll == null ? Boolean.FALSE : listAll;
-    }
-    
-    public String getBindPassword() {
-        return bindPassword;
-    }
-
-    public String getBindDN() {
-        return bindDN;
-    }
-
-    public void setBindDN(String bdn) {
-        this.bindDN=bdn;
-    }
-
-    public String getQueryFilter() {
-        return queryFilter;
-    }
-
-    public void setQueryFilter(String queryFilter) {
-        this.queryFilter=StringEscapeUtils.unescapeHtml(queryFilter);
-    }
-    public String getSearchBase() {
-        return searchBase;
-    }
-
-    public void setSearchBase(String searchBase) {
-        this.searchBase=searchBase;
-    }
-
-    public Boolean getUseSSL() {
-        return useSSL == null ? Boolean.FALSE : useSSL;
-    }
-
-    public void setUseSSL(Boolean useSSL) {
-        this.useSSL=useSSL;
-    }
-
-    public String getHostname() {
-        return hostname;
-    }
-
-    public void setHostname(String hostname) {
-        this.hostname=hostname;
-    }
-
-    public Integer getPort() {
-        return port <= 0 ? 389 : port;
-    }
-
-    public void setPort(Integer port) {
-        this.port=port;
-    }
-
-    public String getTrustStore() {
-        return trustStore;
-    }
-
-    public void setTrustStore(String trustStore) {
-        this.trustStore=trustStore;
-    }
-
-    public String getTrustStorePassword() {
-        return trustStorePassword;
-    }
-
-
-    /////////////////////////////////////////////////////
-    /////////////// API Implementation///////////////////
-    /////////////////////////////////////////////////////
-
-
-    @Override
-    public void execute() throws ResourceUnavailableException,
-            InsufficientCapacityException, ServerApiException,
-            ConcurrentOperationException, ResourceAllocationException {
-          try {
-              if (getListAll()){
-                  // return the existing conf
-                  LDAPConfigCmd cmd = _configService.listLDAPConfig(this);
-                  ListResponse<LDAPConfigResponse> response = new ListResponse<LDAPConfigResponse>();
-                  List<LDAPConfigResponse> responses = new ArrayList<LDAPConfigResponse>();
-
-                  if(!cmd.getHostname().equals("")) {
-                  	responses.add(_responseGenerator.createLDAPConfigResponse(cmd.getHostname(), cmd.getPort(), cmd.getUseSSL(), cmd.getQueryFilter(), cmd.getSearchBase(), cmd.getBindDN()));
-                  }
-                  
-                  response.setResponses(responses);
-                  response.setResponseName(getCommandName());
-                  this.setResponseObject(response);
-              }
-              else if (getHostname()==null || getSearchBase() == null || getQueryFilter() == null) {
-                  throw new InvalidParameterValueException("You need to provide hostname, searchbase and queryfilter to configure your LDAP server");
-              }
-              else {
-                  boolean result = _configService.updateLDAP(this);
-                  if (result){
-                      LDAPConfigResponse lr = _responseGenerator.createLDAPConfigResponse(getHostname(), getPort(), getUseSSL(), getQueryFilter(), getSearchBase(), getBindDN());
-                      lr.setResponseName(getCommandName());
-                      this.setResponseObject(lr);
-                  }
-              }
-          }
-          catch (NamingException ne){
-              ne.printStackTrace();
-          }
-
-    }
-
-    @Override
-    public String getCommandName() {
-        return s_name;
-    }
-
-    @Override
-    public long getEntityOwnerId() {
-        return Account.ACCOUNT_ID_SYSTEM;
-    }
-
-
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/api/src/org/apache/cloudstack/api/command/admin/ldap/LDAPRemoveCmd.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/api/command/admin/ldap/LDAPRemoveCmd.java b/api/src/org/apache/cloudstack/api/command/admin/ldap/LDAPRemoveCmd.java
deleted file mode 100644
index 5159fba..0000000
--- a/api/src/org/apache/cloudstack/api/command/admin/ldap/LDAPRemoveCmd.java
+++ /dev/null
@@ -1,71 +0,0 @@
-// 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.
-package org.apache.cloudstack.api.command.admin.ldap;
-
-
-import org.apache.cloudstack.api.APICommand;
-import org.apache.cloudstack.api.BaseCmd;
-import org.apache.cloudstack.api.response.LDAPConfigResponse;
-import org.apache.cloudstack.api.response.LDAPRemoveResponse;
-import org.apache.log4j.Logger;
-
-import com.cloud.user.Account;
-
-@APICommand(name = "ldapRemove", description="Remove the LDAP context for this site.", responseObject=LDAPConfigResponse.class, since="3.0.1")
-public class LDAPRemoveCmd extends BaseCmd  {
-    public static final Logger s_logger = Logger.getLogger(LDAPRemoveCmd.class.getName());
-
-    private static final String s_name = "ldapremoveresponse";
-
-    /////////////////////////////////////////////////////
-    //////////////// API parameters /////////////////////
-    /////////////////////////////////////////////////////
-
-
-    /////////////////////////////////////////////////////
-    /////////////////// Accessors ///////////////////////
-    /////////////////////////////////////////////////////
-
-
-    /////////////////////////////////////////////////////
-    /////////////// API Implementation///////////////////
-    /////////////////////////////////////////////////////
-
-
-    @Override
-    public void execute(){
-          boolean result = _configService.removeLDAP(this);
-          if (result){
-              LDAPRemoveResponse lr = new LDAPRemoveResponse();
-              lr.setObjectName("ldapremove");
-              lr.setResponseName(getCommandName());
-              this.setResponseObject(lr);
-          }
-    }
-
-    @Override
-    public String getCommandName() {
-        return s_name;
-    }
-
-    @Override
-    public long getEntityOwnerId() {
-        return Account.ACCOUNT_ID_SYSTEM;
-    }
-
-
-}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/api/src/org/apache/cloudstack/api/response/LDAPConfigResponse.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/api/response/LDAPConfigResponse.java b/api/src/org/apache/cloudstack/api/response/LDAPConfigResponse.java
deleted file mode 100644
index bbeec63..0000000
--- a/api/src/org/apache/cloudstack/api/response/LDAPConfigResponse.java
+++ /dev/null
@@ -1,105 +0,0 @@
-// 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.
-package org.apache.cloudstack.api.response;
-
-import org.apache.cloudstack.api.ApiConstants;
-import org.apache.cloudstack.api.BaseResponse;
-
-import com.cloud.serializer.Param;
-import com.google.gson.annotations.SerializedName;
-
-public class LDAPConfigResponse  extends BaseResponse {
-
-    @SerializedName(ApiConstants.HOST_NAME) @Param(description="Hostname or ip address of the ldap server eg: my.ldap.com")
-    private String hostname;
-
-    @SerializedName(ApiConstants.PORT) @Param(description="Specify the LDAP port if required, default is 389")
-    private String port;
-
-    @SerializedName(ApiConstants.USE_SSL) @Param(description="Check Use SSL if the external LDAP server is configured for LDAP over SSL")
-    private String useSSL;
-
-    @SerializedName(ApiConstants.SEARCH_BASE) @Param(description="The search base defines the starting point for the search in the directory tree Example:  dc=cloud,dc=com")
-    private String searchBase;
-
-    @SerializedName(ApiConstants.QUERY_FILTER) @Param(description="You specify a query filter here, which narrows down the users, who can be part of this domain")
-    private String queryFilter;
-
-    @SerializedName(ApiConstants.BIND_DN) @Param(description="Specify the distinguished name of a user with the search permission on the directory")
-    private String bindDN;
-
-    @SerializedName(ApiConstants.BIND_PASSWORD) @Param(description="DN password")
-    private String bindPassword;
-
-    public String getHostname() {
-        return hostname;
-    }
-
-    public void setHostname(String hostname) {
-        this.hostname = hostname;
-    }
-
-    public String getPort() {
-        return port;
-    }
-
-    public void setPort(String port) {
-        this.port = port;
-    }
-
-    public String getUseSSL() {
-        return useSSL;
-    }
-
-    public void setUseSSL(String useSSL) {
-        this.useSSL = useSSL;
-    }
-
-    public String getSearchBase() {
-        return searchBase;
-    }
-
-    public void setSearchBase(String searchBase) {
-        this.searchBase = searchBase;
-    }
-
-    public String getQueryFilter() {
-        return queryFilter;
-    }
-
-    public void setQueryFilter(String queryFilter) {
-        this.queryFilter = queryFilter;
-    }
-
-    public String getBindDN() {
-        return bindDN;
-    }
-
-    public void setBindDN(String bindDN) {
-        this.bindDN = bindDN;
-    }
-
-    public String getBindPassword() {
-        return bindPassword;
-    }
-
-    public void setBindPassword(String bindPassword) {
-        this.bindPassword = bindPassword;
-    }
-
-
-}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/api/src/org/apache/cloudstack/api/response/LDAPRemoveResponse.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/api/response/LDAPRemoveResponse.java b/api/src/org/apache/cloudstack/api/response/LDAPRemoveResponse.java
deleted file mode 100644
index 0feec5b..0000000
--- a/api/src/org/apache/cloudstack/api/response/LDAPRemoveResponse.java
+++ /dev/null
@@ -1,26 +0,0 @@
-// 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.
-package org.apache.cloudstack.api.response;
-
-import org.apache.cloudstack.api.BaseResponse;
-
-public class LDAPRemoveResponse extends BaseResponse {
-
-    public LDAPRemoveResponse(){
-        super();
-    }
-}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/client/tomcatconf/applicationContext.xml.in
----------------------------------------------------------------------
diff --git a/client/tomcatconf/applicationContext.xml.in b/client/tomcatconf/applicationContext.xml.in
index 343a8a1..f29d72f 100644
--- a/client/tomcatconf/applicationContext.xml.in
+++ b/client/tomcatconf/applicationContext.xml.in
@@ -391,7 +391,7 @@
   <bean id="MD5UserAuthenticator" class="com.cloud.server.auth.MD5UserAuthenticator">
     <property name="name" value="MD5"/>
   </bean>
-  <bean id="LDAPUserAuthenticator" class="com.cloud.server.auth.LDAPUserAuthenticator">
+  <bean id="LdapAuthenticator" class="org.apache.cloudstack.ldap.LdapAuthenticator">
     <property name="name" value="LDAP"/>
   </bean>
   <bean id="SHA256SaltedUserAuthenticator" class="com.cloud.server.auth.SHA256SaltedUserAuthenticator">
@@ -400,6 +400,11 @@
   <bean id="PlainTextUserAuthenticator" class="com.cloud.server.auth.PlainTextUserAuthenticator">
     <property name="name" value="PLAINTEXT"/>
   </bean>
+  <bean id="LdapManager" class="org.apache.cloudstack.ldap.LdapManagerImpl" />
+  <bean id="LdapUserManager" class="org.apache.cloudstack.ldap.LdapUserManager" />
+  <bean id="LdapContextFactory" class="org.apache.cloudstack.ldap.LdapContextFactory" />
+  <bean id="LdapConfigurationDao" class="org.apache.cloudstack.ldap.dao.LdapConfigurationDaoImpl" />
+  <bean id="LdapConfiguration" class="org.apache.cloudstack.ldap.LdapConfiguration" />
 
   <!--
       Network Elements

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/client/tomcatconf/commands.properties.in
----------------------------------------------------------------------
diff --git a/client/tomcatconf/commands.properties.in b/client/tomcatconf/commands.properties.in
index d8d176f..b275017 100644
--- a/client/tomcatconf/commands.properties.in
+++ b/client/tomcatconf/commands.properties.in
@@ -5,9 +5,9 @@
 # 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
@@ -210,8 +210,6 @@ scaleSystemVm=1
 #### configuration commands
 updateConfiguration=1
 listConfigurations=1
-ldapConfig=1
-ldapRemove=1
 listCapabilities=15
 listDeploymentPlanners=1
 cleanVMReservations=1
@@ -381,12 +379,12 @@ listProjectInvitations=15
 updateProjectInvitation=15
 deleteProjectInvitation=15
 
-#### 
+####
 createFirewallRule=15
 deleteFirewallRule=15
 listFirewallRules=15
 
-#### 
+####
 createEgressFirewallRule=15
 deleteEgressFirewallRule=15
 listEgressFirewallRules=15
@@ -666,3 +664,11 @@ listDedicatedZones=1
 listDedicatedPods=1
 listDedicatedClusters=1
 listDedicatedHosts=1
+
+### LDAP
+searchLdap=3
+listLdapConfigurations=3
+addLdapConfiguration=3
+deleteLdapConfiguration=3
+listAllLdapUsers=3
+ldapCreateAccount=3

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/client/tomcatconf/componentContext.xml.in
----------------------------------------------------------------------
diff --git a/client/tomcatconf/componentContext.xml.in b/client/tomcatconf/componentContext.xml.in
index 1fbec61..39eab5e 100644
--- a/client/tomcatconf/componentContext.xml.in
+++ b/client/tomcatconf/componentContext.xml.in
@@ -127,7 +127,7 @@
       <list>
           <ref bean="SHA256SaltedUserAuthenticator"/>
           <ref bean="MD5UserAuthenticator"/>
-          <ref bean="LDAPUserAuthenticator"/>
+          <ref bean="LdapAuthenticator"/>
           <ref bean="PlainTextUserAuthenticator"/>
       </list>
     </property>
@@ -137,7 +137,7 @@
       <list>
           <ref bean="SHA256SaltedUserAuthenticator"/>
           <ref bean="MD5UserAuthenticator"/>
-          <ref bean="LDAPUserAuthenticator"/>
+          <ref bean="LdapAuthenticator"/>
           <ref bean="PlainTextUserAuthenticator"/>
       </list>
     </property>

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/client/tomcatconf/nonossComponentContext.xml.in
----------------------------------------------------------------------
diff --git a/client/tomcatconf/nonossComponentContext.xml.in b/client/tomcatconf/nonossComponentContext.xml.in
index 6fd51ef..c8a71eb 100644
--- a/client/tomcatconf/nonossComponentContext.xml.in
+++ b/client/tomcatconf/nonossComponentContext.xml.in
@@ -224,7 +224,7 @@
       <list>
           <ref bean="SHA256SaltedUserAuthenticator"/>
           <ref bean="MD5UserAuthenticator"/>
-          <ref bean="LDAPUserAuthenticator"/>
+          <ref bean="LdapAuthenticator"/>
           <ref bean="PlainTextUserAuthenticator"/>
       </list>
     </property>
@@ -234,7 +234,7 @@
       <list>
           <ref bean="SHA256SaltedUserAuthenticator"/>
           <ref bean="MD5UserAuthenticator"/>
-          <ref bean="LDAPUserAuthenticator"/>
+          <ref bean="LdapAuthenticator"/>
           <ref bean="PlainTextUserAuthenticator"/>
       </list>
     </property>

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/client/tomcatconf/simulatorComponentContext.xml.in
----------------------------------------------------------------------
diff --git a/client/tomcatconf/simulatorComponentContext.xml.in b/client/tomcatconf/simulatorComponentContext.xml.in
index 7225e84..099bdb6 100644
--- a/client/tomcatconf/simulatorComponentContext.xml.in
+++ b/client/tomcatconf/simulatorComponentContext.xml.in
@@ -73,7 +73,7 @@
       <list>
         <ref bean="SHA256SaltedUserAuthenticator"/>
         <ref bean="MD5UserAuthenticator"/>
-        <ref bean="LDAPUserAuthenticator"/>
+        <ref bean="LdapAuthenticator"/>
         <ref bean="PlainTextUserAuthenticator"/>
       </list>
     </property>
@@ -83,7 +83,7 @@
       <list>
         <ref bean="SHA256SaltedUserAuthenticator"/>
         <ref bean="MD5UserAuthenticator"/>
-        <ref bean="LDAPUserAuthenticator"/>
+        <ref bean="LdapAuthenticator"/>
         <ref bean="PlainTextUserAuthenticator"/>
       </list>
     </property>

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/plugins/api/rate-limit/src/org/apache/cloudstack/ratelimit/ApiRateLimitServiceImpl.java
----------------------------------------------------------------------
diff --git a/plugins/api/rate-limit/src/org/apache/cloudstack/ratelimit/ApiRateLimitServiceImpl.java b/plugins/api/rate-limit/src/org/apache/cloudstack/ratelimit/ApiRateLimitServiceImpl.java
index 7d1b43a..5566511 100644
--- a/plugins/api/rate-limit/src/org/apache/cloudstack/ratelimit/ApiRateLimitServiceImpl.java
+++ b/plugins/api/rate-limit/src/org/apache/cloudstack/ratelimit/ApiRateLimitServiceImpl.java
@@ -29,7 +29,6 @@ import net.sf.ehcache.CacheManager;
 import org.apache.log4j.Logger;
 
 import org.apache.cloudstack.acl.APIChecker;
-import org.apache.cloudstack.api.ApiConstants.LDAPParams;
 import org.apache.cloudstack.api.command.admin.ratelimit.ResetApiLimitCmd;
 import org.apache.cloudstack.api.command.user.ratelimit.GetApiLimitCmd;
 import org.apache.cloudstack.api.response.ApiLimitResponse;

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/plugins/user-authenticators/ldap/pom.xml
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/ldap/pom.xml b/plugins/user-authenticators/ldap/pom.xml
index 5c45f11..a00d189 100644
--- a/plugins/user-authenticators/ldap/pom.xml
+++ b/plugins/user-authenticators/ldap/pom.xml
@@ -1,22 +1,14 @@
-<!--
-  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.
--->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+<!-- 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. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <artifactId>cloud-plugin-user-authenticator-ldap</artifactId>
   <name>Apache CloudStack Plugin - User Authenticator LDAP</name>
@@ -26,4 +18,93 @@
     <version>4.2.0-SNAPSHOT</version>
     <relativePath>../../pom.xml</relativePath>
   </parent>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.codehaus.gmaven</groupId>
+        <artifactId>gmaven-plugin</artifactId>
+        <version>1.3</version>
+        <configuration>
+          <providerSelection>1.7</providerSelection>
+        </configuration>
+        <executions>
+          <execution>
+            <goals>
+              <goal>compile</goal>
+              <goal>testCompile</goal>
+            </goals>
+            <configuration>
+              <sources>
+                <fileset>
+                  <directory>test/groovy</directory>
+                  <includes>
+                    <include>**/*.groovy</include>
+                  </includes>
+                </fileset>
+              </sources>
+            </configuration>
+          </execution>
+        </executions>
+        <dependencies>
+          <dependency>
+            <groupId>org.codehaus.gmaven.runtime</groupId>
+            <artifactId>gmaven-runtime-1.7</artifactId>
+            <version>1.3</version>
+            <exclusions>
+              <exclusion>
+                <groupId>org.codehaus.groovy</groupId>
+                <artifactId>groovy-all</artifactId>
+              </exclusion>
+            </exclusions>
+          </dependency>
+          <dependency>
+            <groupId>org.codehaus.groovy</groupId>
+            <artifactId>groovy-all</artifactId>
+            <version>2.0.5</version>
+          </dependency>
+        </dependencies>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <includes>
+            <include>**/*Spec*</include>
+          </includes>
+        </configuration>
+      </plugin>
+
+      <plugin>
+        <groupId>com.btmatthews.maven.plugins</groupId>
+        <artifactId>ldap-maven-plugin</artifactId>
+        <version>1.1.0</version>
+        <configuration>
+          <monitorPort>11389</monitorPort>
+            <monitorKey>ldap</monitorKey>
+            <daemon>false</daemon>
+            <rootDn>dc=cloudstack,dc=org</rootDn>
+            <ldapPort>10389</ldapPort>
+            <ldifFile>test/resources/cloudstack.org.ldif</ldifFile>
+        </configuration>
+      </plugin>
+
+    </plugins>
+  </build>
+
+  <dependencies>
+    <!-- Mandatory dependencies for using Spock -->
+    <dependency>
+      <groupId>org.spockframework</groupId>
+      <artifactId>spock-core</artifactId>
+      <version>0.7-groovy-2.0</version>
+    </dependency>
+
+    <!-- Optional dependencies for using Spock -->
+    <dependency> <!-- enables mocking of classes (in addition to interfaces) -->
+      <groupId>cglib</groupId>
+      <artifactId>cglib-nodep</artifactId>
+      <version>2.2</version>
+    </dependency>
+  </dependencies>
 </project>

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/plugins/user-authenticators/ldap/src/com/cloud/server/auth/LDAPUserAuthenticator.java
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/ldap/src/com/cloud/server/auth/LDAPUserAuthenticator.java b/plugins/user-authenticators/ldap/src/com/cloud/server/auth/LDAPUserAuthenticator.java
deleted file mode 100644
index d928a5b..0000000
--- a/plugins/user-authenticators/ldap/src/com/cloud/server/auth/LDAPUserAuthenticator.java
+++ /dev/null
@@ -1,173 +0,0 @@
-// 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.
-//
-package com.cloud.server.auth;
-
-import java.security.NoSuchAlgorithmException;
-import java.security.SecureRandom;
-import java.util.Hashtable;
-import java.util.Map;
-
-import javax.ejb.Local;
-import javax.inject.Inject;
-import javax.naming.ConfigurationException;
-import javax.naming.Context;
-import javax.naming.NamingEnumeration;
-import javax.naming.NamingException;
-import javax.naming.directory.DirContext;
-import javax.naming.directory.InitialDirContext;
-import javax.naming.directory.SearchControls;
-import javax.naming.directory.SearchResult;
-
-import org.apache.cloudstack.api.ApiConstants.LDAPParams;
-import org.apache.log4j.Logger;
-import org.bouncycastle.util.encoders.Base64;
-
-import com.cloud.configuration.dao.ConfigurationDao;
-import com.cloud.user.UserAccount;
-import com.cloud.user.dao.UserAccountDao;
-import com.cloud.utils.exception.CloudRuntimeException;
-
-@Local(value={UserAuthenticator.class})
-public class LDAPUserAuthenticator extends DefaultUserAuthenticator {
-    public static final Logger s_logger = Logger.getLogger(LDAPUserAuthenticator.class);
-
-    @Inject private ConfigurationDao _configDao;
-    @Inject private UserAccountDao _userAccountDao;
-
-    @Override
-    public boolean authenticate(String username, String password, Long domainId, Map<String, Object[]> requestParameters ) {
-        if (s_logger.isDebugEnabled()) {
-            s_logger.debug("Retrieving user: " + username);
-        }
-        UserAccount user = _userAccountDao.getUserAccount(username, domainId);
-        if (user == null) {
-            s_logger.debug("Unable to find user with " + username + " in domain " + domainId);
-            return false;
-        }
-
-        String url = _configDao.getValue(LDAPParams.hostname.toString());
-        if (url==null){
-            s_logger.debug("LDAP authenticator is not configured.");
-            return false;
-        }
-        String port = _configDao.getValue(LDAPParams.port.toString());
-        String queryFilter = _configDao.getValue(LDAPParams.queryfilter.toString());
-        String searchBase = _configDao.getValue(LDAPParams.searchbase.toString());
-        Boolean useSSL = Boolean.valueOf(_configDao.getValue(LDAPParams.usessl.toString()));
-        String bindDN = _configDao.getValue(LDAPParams.dn.toString());
-        String bindPasswd = _configDao.getValue(LDAPParams.passwd.toString());
-        String trustStore = _configDao.getValue(LDAPParams.truststore.toString());
-        String trustStorePassword = _configDao.getValue(LDAPParams.truststorepass.toString());
-
-        try {
-            // get all params
-            Hashtable<String, String> env = new Hashtable<String, String>(11);
-            env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
-            String protocol = "ldap://" ;
-            if (useSSL){
-                env.put(Context.SECURITY_PROTOCOL, "ssl");
-                protocol="ldaps://" ;
-                System.setProperty("javax.net.ssl.trustStore", trustStore);
-                System.setProperty("javax.net.ssl.trustStorePassword", trustStorePassword);
-            }
-            env.put(Context.PROVIDER_URL, protocol + url  + ":" + port);
-
-            if (bindDN != null && bindPasswd != null){
-                env.put(Context.SECURITY_PRINCIPAL, bindDN);
-                env.put(Context.SECURITY_CREDENTIALS, bindPasswd);
-            }
-            else {
-                // Use anonymous authentication
-                env.put(Context.SECURITY_AUTHENTICATION, "none");
-            }
-            // Create the initial context
-            DirContext ctx = new InitialDirContext(env);
-            // use this context to search
-
-            // substitute the queryFilter with this user info
-            queryFilter = queryFilter.replaceAll("\\%u", username);
-            queryFilter = queryFilter.replaceAll("\\%n", user.getFirstname() + " " + user.getLastname());
-            queryFilter = queryFilter.replaceAll("\\%e", user.getEmail());
-
-
-            SearchControls sc = new SearchControls();
-            String[] searchFilter = { "dn" };
-            sc.setReturningAttributes(new String[0]); //return no attributes
-            sc.setReturningAttributes(searchFilter);
-            sc.setSearchScope(SearchControls.SUBTREE_SCOPE);
-            sc.setCountLimit(1);
-
-            // Search for objects with those matching attributes
-            NamingEnumeration<SearchResult> answer = ctx.search(searchBase, queryFilter,  sc);
-            SearchResult sr = answer.next();
-            String cn = sr.getName();
-            answer.close();
-            ctx.close();
-
-            s_logger.info("DN from LDAP =" + cn);
-
-            // check the password
-            env = new Hashtable<String, String>(11);
-            env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
-            protocol = "ldap://" ;
-            if (useSSL){
-                env.put(Context.SECURITY_PROTOCOL, "ssl");
-                protocol="ldaps://" ;
-            }
-            env.put(Context.PROVIDER_URL, protocol + url  + ":" + port);
-            env.put(Context.SECURITY_PRINCIPAL, cn + "," + searchBase);
-            env.put(Context.SECURITY_CREDENTIALS, password);
-            // Create the initial context
-            ctx = new InitialDirContext(env);
-            ctx.close();
-
-        } catch (NamingException ne) {
-            s_logger.warn("Authentication Failed ! " + ne.getMessage() + (ne.getCause() != null ? ("; Caused by:" + ne.getCause().getMessage()) : ""));
-            return false;
-        }
-        catch (Exception e){
-            e.printStackTrace();
-            s_logger.warn("Unknown error encountered " + e.getMessage());
-            return false;
-        }
-
-        // authenticate
-        return true;
-    }
-
-    @Override
-    public boolean configure(String name, Map<String, Object> params)
-            throws ConfigurationException {
-        if (name == null) {
-            name = "LDAP";
-        }
-	super.configure(name, params);
-        return true;
-    }
-
-    @Override
-    public String encode(String password) {
-        // Password is not used, so set to a random string
-        try {
-            SecureRandom randomGen = SecureRandom.getInstance("SHA1PRNG");
-            byte bytes[] = new byte[20];
-            randomGen.nextBytes(bytes);
-            return Base64.encode(bytes).toString();
-        } catch (NoSuchAlgorithmException e) {
-            throw new CloudRuntimeException("Failed to generate random password",e);
-        }	
-    }
-}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapAddConfigurationCmd.java
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapAddConfigurationCmd.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapAddConfigurationCmd.java
new file mode 100644
index 0000000..b8e08e5
--- /dev/null
+++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapAddConfigurationCmd.java
@@ -0,0 +1,80 @@
+package org.apache.cloudstack.api.command;
+
+import javax.inject.Inject;
+
+import org.apache.log4j.Logger;
+
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.response.LdapConfigurationResponse;
+import org.apache.cloudstack.ldap.LdapManager;
+
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.user.Account;
+
+@APICommand(name = "addLdapConfiguration", description = "Add a new Ldap Configuration", responseObject = LdapConfigurationResponse.class, since = "4.2.0")
+public class LdapAddConfigurationCmd extends BaseCmd {
+    public static final Logger s_logger = Logger.getLogger(LdapAddConfigurationCmd.class.getName());
+    private static final String s_name = "ldapconfigurationresponse";
+
+    @Inject
+    private LdapManager _ldapManager;
+
+    @Parameter(name = "hostname", type = CommandType.STRING, required = true, description = "Hostname")
+    private String hostname;
+
+    @Parameter(name = "port", type = CommandType.INTEGER, required = true, description = "Port")
+    private int port;
+
+    public LdapAddConfigurationCmd() {
+        super();
+    }
+
+    public LdapAddConfigurationCmd(final LdapManager ldapManager) {
+        super();
+        _ldapManager = ldapManager;
+    }
+
+    @Override
+    public void execute() throws ServerApiException {
+        try {
+            final LdapConfigurationResponse response = _ldapManager.addConfiguration(hostname, port);
+            response.setObjectName("LdapAddConfiguration");
+            response.setResponseName(getCommandName());
+            setResponseObject(response);
+        } catch (final InvalidParameterValueException e) {
+            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.toString());
+        }
+
+    }
+
+    @Override
+    public String getCommandName() {
+        return s_name;
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        return Account.ACCOUNT_ID_SYSTEM;
+    }
+
+    public String getHostname() {
+        return hostname;
+    }
+
+    public int getPort() {
+        return port;
+    }
+
+    public void setHostname(final String hostname) {
+        this.hostname = hostname;
+    }
+
+    public void setPort(final int port) {
+        this.port = port;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapCreateAccount.java
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapCreateAccount.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapCreateAccount.java
new file mode 100644
index 0000000..01aebce
--- /dev/null
+++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapCreateAccount.java
@@ -0,0 +1,141 @@
+// 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.
+package org.apache.cloudstack.api.command;
+
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.Map;
+
+import javax.inject.Inject;
+import javax.naming.NamingException;
+
+import org.apache.log4j.Logger;
+import org.bouncycastle.util.encoders.Base64;
+
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.response.AccountResponse;
+import org.apache.cloudstack.api.response.DomainResponse;
+import org.apache.cloudstack.context.CallContext;
+import org.apache.cloudstack.ldap.LdapManager;
+import org.apache.cloudstack.ldap.LdapUser;
+
+import com.cloud.user.Account;
+import com.cloud.user.UserAccount;
+
+@APICommand(name = "ldapCreateAccount", description = "Creates an account from an LDAP user", responseObject = AccountResponse.class, since = "4.2.0")
+public class LdapCreateAccount extends BaseCmd {
+    public static final Logger s_logger = Logger.getLogger(LdapCreateAccount.class.getName());
+    private static final String s_name = "createaccountresponse";
+
+    @Inject
+    private LdapManager _ldapManager;
+
+    @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "Creates the user under the specified account. If no account is specified, the username will be used as the account name.")
+    private String accountName;
+
+    @Parameter(name = ApiConstants.ACCOUNT_TYPE, type = CommandType.SHORT, required = true, description = "Type of the account.  Specify 0 for user, 1 for root admin, and 2 for domain admin")
+    private Short accountType;
+
+    @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class,
+            description = "Creates the user under the specified domain.")
+    private Long domainId;
+
+    @Parameter(name = ApiConstants.TIMEZONE, type = CommandType.STRING, description = "Specifies a timezone for this command. For more information on the timezone parameter, see Time Zone Format.")
+    private String timeZone;
+
+    @Parameter(name = ApiConstants.USERNAME, type = CommandType.STRING, required = true, description = "Unique username.")
+    private String userName;
+
+    @Parameter(name = ApiConstants.NETWORK_DOMAIN, type = CommandType.STRING, description = "Network domain for the account's networks")
+    private String networkDomain;
+
+    @Parameter(name = ApiConstants.ACCOUNT_DETAILS, type = CommandType.MAP, description = "details for account used to store specific parameters")
+    private Map<String, String> details;
+
+    @Parameter(name = ApiConstants.ACCOUNT_ID, type = CommandType.STRING, description = "Account UUID, required for adding account from external provisioning system")
+    private String accountUUID;
+
+    @Parameter(name = ApiConstants.USER_ID, type = CommandType.STRING, description = "User UUID, required for adding account from external provisioning system")
+    private String userUUID;
+
+    public LdapCreateAccount() {
+        super();
+    }
+
+    public LdapCreateAccount(final LdapManager ldapManager) {
+        super();
+        _ldapManager = ldapManager;
+    }
+
+    @Override
+    public void execute() throws ServerApiException {
+        CallContext.current().setEventDetails("Account Name: " + accountName + ", Domain Id:" + domainId);
+        try {
+            LdapUser user = _ldapManager.getUser(userName);
+            validateUser(user);
+            UserAccount userAccount = _accountService.createUserAccount(userName, generatePassword(), user.getFirstname(), user.getLastname(), user.getEmail(), timeZone,
+                    accountName, accountType, domainId, networkDomain, details, accountUUID, userUUID);
+            if (userAccount != null) {
+                AccountResponse response = _responseGenerator.createUserAccountResponse(userAccount);
+                response.setResponseName(getCommandName());
+                setResponseObject(response);
+            } else {
+                throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create a user account");
+            }
+        } catch (NamingException e) {
+            throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, "No LDAP user exists with the username of " + userName);
+        }
+    }
+
+    private String generatePassword() throws ServerApiException {
+        try {
+            SecureRandom randomGen = SecureRandom.getInstance("SHA1PRNG");
+            byte bytes[] = new byte[20];
+            randomGen.nextBytes(bytes);
+            return Base64.encode(bytes).toString();
+        } catch (NoSuchAlgorithmException e) {
+            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to generate random password");
+        }
+    }
+
+    @Override
+    public String getCommandName() {
+        return s_name;
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        return Account.ACCOUNT_ID_SYSTEM;
+    }
+
+    private void validateUser(LdapUser user) throws ServerApiException {
+        if (user.getEmail() == null) {
+            throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, userName + " has no email address set within LDAP");
+        }
+        if (user.getFirstname() == null) {
+            throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, userName + " has no firstname set within LDAP");
+        }
+        if (user.getLastname() == null) {
+            throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, userName + " has no lastname set within LDAP");
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapDeleteConfigurationCmd.java
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapDeleteConfigurationCmd.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapDeleteConfigurationCmd.java
new file mode 100644
index 0000000..b45bce5
--- /dev/null
+++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapDeleteConfigurationCmd.java
@@ -0,0 +1,76 @@
+// 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.
+package org.apache.cloudstack.api.command;
+
+import javax.inject.Inject;
+
+import org.apache.log4j.Logger;
+
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.response.LdapConfigurationResponse;
+import org.apache.cloudstack.ldap.LdapManager;
+
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.user.Account;
+
+@APICommand(name = "deleteLdapConfiguration", description = "Remove an Ldap Configuration", responseObject = LdapConfigurationResponse.class, since = "4.2.0")
+public class LdapDeleteConfigurationCmd extends BaseCmd {
+    public static final Logger s_logger = Logger.getLogger(LdapDeleteConfigurationCmd.class.getName());
+    private static final String s_name = "ldapconfigurationresponse";
+
+    @Inject
+    private LdapManager _ldapManager;
+
+    @Parameter(name = "hostname", type = CommandType.STRING, required = true, description = "Hostname")
+    private String hostname;
+
+    public LdapDeleteConfigurationCmd() {
+        super();
+    }
+
+    public LdapDeleteConfigurationCmd(final LdapManager ldapManager) {
+        super();
+        _ldapManager = ldapManager;
+    }
+
+    @Override
+    public void execute() throws ServerApiException {
+        try {
+            final LdapConfigurationResponse response = _ldapManager.deleteConfiguration(hostname);
+            response.setObjectName("LdapDeleteConfiguration");
+            response.setResponseName(getCommandName());
+            setResponseObject(response);
+        } catch (final InvalidParameterValueException e) {
+            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.toString());
+        }
+
+    }
+
+    @Override
+    public String getCommandName() {
+        return s_name;
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        return Account.ACCOUNT_ID_SYSTEM;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapListAllUsersCmd.java
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapListAllUsersCmd.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapListAllUsersCmd.java
new file mode 100644
index 0000000..98c9fcf
--- /dev/null
+++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapListAllUsersCmd.java
@@ -0,0 +1,88 @@
+// 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.
+package org.apache.cloudstack.api.command;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.inject.Inject;
+
+import org.apache.log4j.Logger;
+
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.BaseListCmd;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.response.LdapUserResponse;
+import org.apache.cloudstack.api.response.ListResponse;
+import org.apache.cloudstack.ldap.LdapManager;
+import org.apache.cloudstack.ldap.LdapUser;
+import org.apache.cloudstack.ldap.NoLdapUserMatchingQueryException;
+
+import com.cloud.user.Account;
+
+@APICommand(name = "listAllLdapUsers", responseObject = LdapUserResponse.class, description = "Lists all LDAP Users", since = "4.2.0")
+public class LdapListAllUsersCmd extends BaseListCmd {
+
+    public static final Logger s_logger = Logger.getLogger(LdapListAllUsersCmd.class.getName());
+    private static final String s_name = "ldapuserresponse";
+    @Inject
+    private LdapManager _ldapManager;
+
+    public LdapListAllUsersCmd() {
+        super();
+    }
+
+    public LdapListAllUsersCmd(final LdapManager ldapManager) {
+        super();
+        _ldapManager = ldapManager;
+    }
+
+    private List<LdapUserResponse> createLdapUserResponse(List<LdapUser> users) {
+        final List<LdapUserResponse> ldapResponses = new ArrayList<LdapUserResponse>();
+        for (final LdapUser user : users) {
+            final LdapUserResponse ldapResponse = _ldapManager.createLdapUserResponse(user);
+            ldapResponse.setObjectName("LdapUser");
+            ldapResponses.add(ldapResponse);
+        }
+        return ldapResponses;
+    }
+
+    @Override
+    public void execute() throws ServerApiException {
+        try {
+            final List<LdapUser> users = _ldapManager.getUsers();
+            final ListResponse<LdapUserResponse> response = new ListResponse<LdapUserResponse>();
+            final List<LdapUserResponse> ldapResponses = createLdapUserResponse(users);
+            response.setResponses(ldapResponses);
+            response.setResponseName(getCommandName());
+            setResponseObject(response);
+        } catch (final NoLdapUserMatchingQueryException ex) {
+            throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, ex.getMessage());
+        }
+    }
+
+    @Override
+    public String getCommandName() {
+        return s_name;
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        return Account.ACCOUNT_ID_SYSTEM;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapListConfigurationCmd.java
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapListConfigurationCmd.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapListConfigurationCmd.java
new file mode 100644
index 0000000..fc9bd35
--- /dev/null
+++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapListConfigurationCmd.java
@@ -0,0 +1,106 @@
+// 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.
+package org.apache.cloudstack.api.command;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.inject.Inject;
+
+import org.apache.log4j.Logger;
+
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.BaseListCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.response.LdapConfigurationResponse;
+import org.apache.cloudstack.api.response.ListResponse;
+import org.apache.cloudstack.ldap.LdapConfigurationVO;
+import org.apache.cloudstack.ldap.LdapManager;
+
+import com.cloud.user.Account;
+import com.cloud.utils.Pair;
+
+@APICommand(name = "listLdapConfigurations", responseObject = LdapConfigurationResponse.class, description = "Lists all LDAP configurations", since = "4.2.0")
+public class LdapListConfigurationCmd extends BaseListCmd {
+    public static final Logger s_logger = Logger.getLogger(LdapListConfigurationCmd.class.getName());
+
+    private static final String s_name = "ldapconfigurationresponse";
+
+    @Inject
+    private LdapManager _ldapManager;
+
+    @Parameter(name = "hostname", type = CommandType.STRING, required = false, description = "Hostname")
+    private String hostname;
+
+    @Parameter(name = "port", type = CommandType.INTEGER, required = false, description = "Port")
+    private int port;
+
+    public LdapListConfigurationCmd() {
+        super();
+    }
+
+    public LdapListConfigurationCmd(final LdapManager ldapManager) {
+        super();
+        _ldapManager = ldapManager;
+    }
+
+    private List<LdapConfigurationResponse> createLdapConfigurationResponses(List<? extends LdapConfigurationVO> configurations) {
+        final List<LdapConfigurationResponse> responses = new ArrayList<LdapConfigurationResponse>();
+        for (final LdapConfigurationVO resource : configurations) {
+            final LdapConfigurationResponse configurationResponse = _ldapManager.createLdapConfigurationResponse(resource);
+            configurationResponse.setObjectName("LdapConfiguration");
+            responses.add(configurationResponse);
+        }
+        return responses;
+    }
+
+    @Override
+    public void execute() {
+        final Pair<List<? extends LdapConfigurationVO>, Integer> result = _ldapManager.listConfigurations(this);
+        final List<LdapConfigurationResponse> responses = createLdapConfigurationResponses(result.first());
+        final ListResponse<LdapConfigurationResponse> response = new ListResponse<LdapConfigurationResponse>();
+        response.setResponses(responses, result.second());
+        response.setResponseName(getCommandName());
+        setResponseObject(response);
+    }
+
+    @Override
+    public String getCommandName() {
+        return s_name;
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        return Account.ACCOUNT_ID_SYSTEM;
+    }
+
+    public String getHostname() {
+        return hostname;
+    }
+
+    public int getPort() {
+        return port;
+    }
+
+    public void setHostname(final String hostname) {
+        this.hostname = hostname;
+    }
+
+    public void setPort(final int port) {
+        this.port = port;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapUserSearchCmd.java
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapUserSearchCmd.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapUserSearchCmd.java
new file mode 100644
index 0000000..314581b
--- /dev/null
+++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapUserSearchCmd.java
@@ -0,0 +1,96 @@
+// 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.
+package org.apache.cloudstack.api.command;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.inject.Inject;
+
+import org.apache.log4j.Logger;
+
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.BaseListCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.response.LdapUserResponse;
+import org.apache.cloudstack.api.response.ListResponse;
+import org.apache.cloudstack.ldap.LdapManager;
+import org.apache.cloudstack.ldap.LdapUser;
+import org.apache.cloudstack.ldap.NoLdapUserMatchingQueryException;
+
+import com.cloud.user.Account;
+
+@APICommand(name = "searchLdap", responseObject = LdapUserResponse.class, description = "Searches LDAP based on the username attribute", since = "4.2.0")
+public class LdapUserSearchCmd extends BaseListCmd {
+
+    public static final Logger s_logger = Logger.getLogger(LdapUserSearchCmd.class.getName());
+    private static final String s_name = "ldapuserresponse";
+    @Inject
+    private LdapManager _ldapManager;
+
+    @Parameter(name = "query", type = CommandType.STRING, entityType = LdapUserResponse.class, required = true, description = "query to search using")
+    private String query;
+
+    public LdapUserSearchCmd() {
+        super();
+    }
+
+    public LdapUserSearchCmd(final LdapManager ldapManager) {
+        super();
+        _ldapManager = ldapManager;
+    }
+
+    private List<LdapUserResponse> createLdapUserResponse(List<LdapUser> users) {
+        final List<LdapUserResponse> ldapUserResponses = new ArrayList<LdapUserResponse>();
+        if (users != null) {
+            for (final LdapUser user : users) {
+                final LdapUserResponse ldapUserResponse = _ldapManager.createLdapUserResponse(user);
+                ldapUserResponse.setObjectName("LdapUser");
+                ldapUserResponses.add(ldapUserResponse);
+            }
+        }
+        return ldapUserResponses;
+    }
+
+    @Override
+    public void execute() {
+        final ListResponse<LdapUserResponse> response = new ListResponse<LdapUserResponse>();
+        List<LdapUser> users = null;
+
+        try {
+            users = _ldapManager.searchUsers(query);
+        } catch (final NoLdapUserMatchingQueryException e) {
+            s_logger.debug(e.getMessage());
+        }
+
+        final List<LdapUserResponse> ldapUserResponses = createLdapUserResponse(users);
+
+        response.setResponses(ldapUserResponses);
+        response.setResponseName(getCommandName());
+        setResponseObject(response);
+    }
+
+    @Override
+    public String getCommandName() {
+        return s_name;
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        return Account.ACCOUNT_ID_SYSTEM;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/response/LdapConfigurationResponse.java
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/response/LdapConfigurationResponse.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/response/LdapConfigurationResponse.java
new file mode 100644
index 0000000..a4e4782
--- /dev/null
+++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/response/LdapConfigurationResponse.java
@@ -0,0 +1,63 @@
+// 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.
+package org.apache.cloudstack.api.response;
+
+import com.google.gson.annotations.SerializedName;
+
+import org.apache.cloudstack.api.BaseResponse;
+
+import com.cloud.serializer.Param;
+
+public class LdapConfigurationResponse extends BaseResponse {
+    @SerializedName("hostname")
+    @Param(description = "hostname")
+    private String hostname;
+
+    @SerializedName("port")
+    @Param(description = "port")
+    private int port;
+
+    public LdapConfigurationResponse() {
+        super();
+    }
+
+    public LdapConfigurationResponse(final String hostname) {
+        super();
+        this.hostname = hostname;
+    }
+
+    public LdapConfigurationResponse(final String hostname, final int port) {
+        this.hostname = hostname;
+        this.port = port;
+    }
+
+    public String getHostname() {
+        return hostname;
+    }
+
+    public int getPort() {
+        return port;
+    }
+
+    public void setHostname(final String hostname) {
+        this.hostname = hostname;
+    }
+
+    public void setPort(final int port) {
+        this.port = port;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/response/LdapUserResponse.java
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/response/LdapUserResponse.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/response/LdapUserResponse.java
new file mode 100644
index 0000000..eb1c14c
--- /dev/null
+++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/response/LdapUserResponse.java
@@ -0,0 +1,98 @@
+// 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.
+package org.apache.cloudstack.api.response;
+
+import com.google.gson.annotations.SerializedName;
+
+import org.apache.cloudstack.api.BaseResponse;
+
+import com.cloud.serializer.Param;
+
+public class LdapUserResponse extends BaseResponse {
+    @SerializedName("email")
+    @Param(description = "The user's email")
+    private String email;
+
+    @SerializedName("principal")
+    @Param(description = "The user's principle")
+    private String principal;
+
+    @SerializedName("firstname")
+    @Param(description = "The user's firstname")
+    private String firstname;
+
+    @SerializedName("lastname")
+    @Param(description = "The user's lastname")
+    private String lastname;
+
+    @SerializedName("username")
+    @Param(description = "The user's username")
+    private String username;
+
+    public LdapUserResponse() {
+        super();
+    }
+
+    public LdapUserResponse(final String username, final String email, final String firstname, final String lastname, final String principal) {
+        super();
+        this.username = username;
+        this.email = email;
+        this.firstname = firstname;
+        this.lastname = lastname;
+        this.principal = principal;
+    }
+
+    public String getEmail() {
+        return email;
+    }
+
+    public String getFirstname() {
+        return firstname;
+    }
+
+    public String getLastname() {
+        return lastname;
+    }
+
+    public String getPrincipal() {
+        return principal;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public void setEmail(final String email) {
+        this.email = email;
+    }
+
+    public void setFirstname(final String firstname) {
+        this.firstname = firstname;
+    }
+
+    public void setLastname(final String lastname) {
+        this.lastname = lastname;
+    }
+
+    public void setPrincipal(final String principal) {
+        this.principal = principal;
+    }
+
+    public void setUsername(final String username) {
+        this.username = username;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapAuthenticator.java
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapAuthenticator.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapAuthenticator.java
new file mode 100644
index 0000000..54b6a8b
--- /dev/null
+++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapAuthenticator.java
@@ -0,0 +1,73 @@
+// 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.
+package org.apache.cloudstack.ldap;
+
+import java.util.Map;
+
+import javax.inject.Inject;
+
+import org.apache.log4j.Logger;
+
+import org.apache.cloudstack.api.command.LdapListConfigurationCmd;
+
+import com.cloud.server.auth.DefaultUserAuthenticator;
+import com.cloud.user.UserAccount;
+import com.cloud.user.dao.UserAccountDao;
+
+public class LdapAuthenticator extends DefaultUserAuthenticator {
+    private static final Logger s_logger = Logger.getLogger(LdapAuthenticator.class.getName());
+
+    @Inject
+    private LdapManager _ldapManager;
+    @Inject
+    private UserAccountDao _userAccountDao;
+
+    public LdapAuthenticator() {
+        super();
+    }
+
+    public LdapAuthenticator(final LdapManager ldapManager, final UserAccountDao userAccountDao) {
+        super();
+        _ldapManager = ldapManager;
+        _userAccountDao = userAccountDao;
+    }
+
+    @Override
+    public boolean authenticate(final String username, final String password, final Long domainId, final Map<String, Object[]> requestParameters) {
+
+        final UserAccount user = _userAccountDao.getUserAccount(username, domainId);
+
+        if (user == null) {
+            s_logger.debug("Unable to find user with " + username + " in domain " + domainId);
+            return false;
+        } else if (isLdapConfigurationPresent()) {
+            return _ldapManager.canAuthenticate(username, password);
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public String encode(final String password) {
+        return password;
+    }
+
+    private boolean isLdapConfigurationPresent() {
+        return _ldapManager.listConfigurations(new LdapListConfigurationCmd(_ldapManager)).second() > 0;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapConfiguration.java
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapConfiguration.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapConfiguration.java
new file mode 100644
index 0000000..d0ae4d2
--- /dev/null
+++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapConfiguration.java
@@ -0,0 +1,116 @@
+// 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.
+package org.apache.cloudstack.ldap;
+
+import java.util.List;
+
+import javax.inject.Inject;
+import javax.naming.directory.SearchControls;
+
+import org.apache.cloudstack.api.command.LdapListConfigurationCmd;
+
+import com.cloud.configuration.dao.ConfigurationDao;
+import com.cloud.utils.Pair;
+
+public class LdapConfiguration {
+    private final static String factory = "com.sun.jndi.ldap.LdapCtxFactory";
+
+    private final static int scope = SearchControls.SUBTREE_SCOPE;
+
+    @Inject
+    private ConfigurationDao _configDao;
+
+    @Inject
+    private LdapManager _ldapManager;
+
+    public LdapConfiguration() {
+    }
+
+    public LdapConfiguration(final ConfigurationDao configDao, final LdapManager ldapManager) {
+        _configDao = configDao;
+        _ldapManager = ldapManager;
+    }
+
+    public String getAuthentication() {
+        if ((getBindPrincipal() == null) && (getBindPassword() == null)) {
+            return "none";
+        } else {
+            return "simple";
+        }
+    }
+
+    public String getBaseDn() {
+        return _configDao.getValue("ldap.basedn");
+    }
+
+    public String getBindPassword() {
+        return _configDao.getValue("ldap.bind.password");
+    }
+
+    public String getBindPrincipal() {
+        return _configDao.getValue("ldap.bind.principal");
+    }
+
+    public String getEmailAttribute() {
+        final String emailAttribute = _configDao.getValue("ldap.email.attribute");
+        return emailAttribute == null ? "mail" : emailAttribute;
+    }
+
+    public String getFactory() {
+        return factory;
+    }
+
+    public String getFirstnameAttribute() {
+        final String firstnameAttribute = _configDao.getValue("ldap.firstname.attribute");
+        return firstnameAttribute == null ? "givenname" : firstnameAttribute;
+    }
+
+    public String getLastnameAttribute() {
+        final String lastnameAttribute = _configDao.getValue("ldap.lastname.attribute");
+        return lastnameAttribute == null ? "sn" : lastnameAttribute;
+    }
+
+    public String getProviderUrl() {
+        final Pair<List<? extends LdapConfigurationVO>, Integer> result = _ldapManager.listConfigurations(new LdapListConfigurationCmd(_ldapManager));
+        final StringBuilder providerUrls = new StringBuilder();
+        String delim = "";
+        for (final LdapConfigurationVO resource : result.first()) {
+            final String providerUrl = "ldap://" + resource.getHostname() + ":" + resource.getPort();
+            providerUrls.append(delim).append(providerUrl);
+            delim = " ";
+        }
+        return providerUrls.toString();
+    }
+
+    public String[] getReturnAttributes() {
+        return new String[] {getUsernameAttribute(), getEmailAttribute(), getFirstnameAttribute(), getLastnameAttribute()};
+    }
+
+    public int getScope() {
+        return scope;
+    }
+
+    public String getUsernameAttribute() {
+        final String usernameAttribute = _configDao.getValue("ldap.username.attribute");
+        return usernameAttribute == null ? "uid" : usernameAttribute;
+    }
+
+    public String getUserObject() {
+        final String userObject = _configDao.getValue("ldap.user.object");
+        return userObject == null ? "inetOrgPerson" : userObject;
+    }
+}
\ No newline at end of file


[3/4] Merge LDAPPlugin

Posted by ap...@apache.org.
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapConfigurationVO.java
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapConfigurationVO.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapConfigurationVO.java
new file mode 100644
index 0000000..5a243f2
--- /dev/null
+++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapConfigurationVO.java
@@ -0,0 +1,66 @@
+// 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.
+package org.apache.cloudstack.ldap;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+import org.apache.cloudstack.api.InternalIdentity;
+
+@Entity
+@Table(name = "ldap_configuration")
+public class LdapConfigurationVO implements InternalIdentity {
+    @Column(name = "hostname")
+    private String hostname;
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    @Column(name = "id")
+    private Long id;
+
+    @Column(name = "port")
+    private int port;
+
+    public LdapConfigurationVO() {
+    }
+
+    public LdapConfigurationVO(final String hostname, final int port) {
+        this.hostname = hostname;
+        this.port = port;
+    }
+
+    public String getHostname() {
+        return hostname;
+    }
+
+    @Override
+    public long getId() {
+        return id;
+    }
+
+    public int getPort() {
+        return port;
+    }
+
+    public void setId(long id) {
+        this.id = id;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapContextFactory.java
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapContextFactory.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapContextFactory.java
new file mode 100644
index 0000000..fd33e88
--- /dev/null
+++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapContextFactory.java
@@ -0,0 +1,103 @@
+// 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.
+package org.apache.cloudstack.ldap;
+
+import java.util.Hashtable;
+
+import javax.inject.Inject;
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InitialDirContext;
+
+import org.apache.log4j.Logger;
+
+public class LdapContextFactory {
+    private static final Logger s_logger = Logger.getLogger(LdapContextFactory.class.getName());
+
+    @Inject
+    private LdapConfiguration _ldapConfiguration;
+
+    public LdapContextFactory() {
+    }
+
+    public LdapContextFactory(final LdapConfiguration ldapConfiguration) {
+        _ldapConfiguration = ldapConfiguration;
+    }
+
+    public DirContext createBindContext() throws NamingException {
+        return createBindContext(null);
+    }
+
+    public DirContext createBindContext(final String providerUrl) throws NamingException {
+        final String bindPrincipal = _ldapConfiguration.getBindPrincipal();
+        final String bindPassword = _ldapConfiguration.getBindPassword();
+        return createInitialDirContext(bindPrincipal, bindPassword, providerUrl, true);
+    }
+
+    private DirContext createInitialDirContext(final String principal, final String password, final boolean isSystemContext) throws NamingException {
+        return createInitialDirContext(principal, password, null, isSystemContext);
+    }
+
+    private DirContext createInitialDirContext(final String principal, final String password, final String providerUrl, final boolean isSystemContext) throws NamingException {
+        return new InitialDirContext(getEnvironment(principal, password, providerUrl, isSystemContext));
+    }
+
+    public DirContext createUserContext(final String principal, final String password) throws NamingException {
+        return createInitialDirContext(principal, password, false);
+    }
+
+    private Hashtable<String, String> getEnvironment(final String principal, final String password, final String providerUrl, final boolean isSystemContext) {
+        final String factory = _ldapConfiguration.getFactory();
+        final String url = providerUrl == null ? _ldapConfiguration.getProviderUrl() : providerUrl;
+        final String authentication = _ldapConfiguration.getAuthentication();
+
+        final Hashtable<String, String> environment = new Hashtable<String, String>();
+
+        environment.put(Context.INITIAL_CONTEXT_FACTORY, factory);
+        environment.put(Context.PROVIDER_URL, url);
+        environment.put("com.sun.jndi.ldap.read.timeout", "500");
+        environment.put("com.sun.jndi.ldap.connect.pool", "true");
+
+        if ("none".equals(authentication) && !isSystemContext) {
+            environment.put(Context.SECURITY_AUTHENTICATION, "simple");
+        } else {
+            environment.put(Context.SECURITY_AUTHENTICATION, authentication);
+        }
+
+        if (principal != null) {
+            environment.put(Context.SECURITY_PRINCIPAL, principal);
+        }
+
+        if (password != null) {
+            environment.put(Context.SECURITY_CREDENTIALS, password);
+        }
+
+        return environment;
+    }
+
+    public void testConnection(final String providerUrl) throws NamingException {
+        try {
+            createBindContext(providerUrl);
+            s_logger.info("LDAP Connection was successful");
+        } catch (final NamingException e) {
+            s_logger.warn("LDAP Connection failed");
+            s_logger.error(e.getMessage(), e);
+            throw e;
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapManager.java
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapManager.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapManager.java
new file mode 100644
index 0000000..aa0b751
--- /dev/null
+++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapManager.java
@@ -0,0 +1,50 @@
+// 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.
+package org.apache.cloudstack.ldap;
+
+import java.util.List;
+
+import javax.naming.NamingException;
+
+import org.apache.cloudstack.api.command.LdapListConfigurationCmd;
+import org.apache.cloudstack.api.response.LdapConfigurationResponse;
+import org.apache.cloudstack.api.response.LdapUserResponse;
+
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.utils.Pair;
+import com.cloud.utils.component.PluggableService;
+
+public interface LdapManager extends PluggableService {
+
+    LdapConfigurationResponse addConfiguration(String hostname, int port) throws InvalidParameterValueException;
+
+    boolean canAuthenticate(String username, String password);
+
+    LdapConfigurationResponse createLdapConfigurationResponse(LdapConfigurationVO configuration);
+
+    LdapUserResponse createLdapUserResponse(LdapUser user);
+
+    LdapConfigurationResponse deleteConfiguration(String hostname) throws InvalidParameterValueException;
+
+    LdapUser getUser(final String username) throws NamingException;
+
+    List<LdapUser> getUsers() throws NoLdapUserMatchingQueryException;
+
+    Pair<List<? extends LdapConfigurationVO>, Integer> listConfigurations(LdapListConfigurationCmd cmd);
+
+    List<LdapUser> searchUsers(String query) throws NoLdapUserMatchingQueryException;
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapManagerImpl.java
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapManagerImpl.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapManagerImpl.java
new file mode 100644
index 0000000..b6fb3e8
--- /dev/null
+++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapManagerImpl.java
@@ -0,0 +1,205 @@
+// 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.
+package org.apache.cloudstack.ldap;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.ejb.Local;
+import javax.inject.Inject;
+import javax.naming.NamingException;
+import javax.naming.directory.DirContext;
+
+import org.apache.log4j.Logger;
+import org.springframework.stereotype.Component;
+
+import org.apache.cloudstack.api.command.LdapAddConfigurationCmd;
+import org.apache.cloudstack.api.command.LdapCreateAccount;
+import org.apache.cloudstack.api.command.LdapDeleteConfigurationCmd;
+import org.apache.cloudstack.api.command.LdapListAllUsersCmd;
+import org.apache.cloudstack.api.command.LdapListConfigurationCmd;
+import org.apache.cloudstack.api.command.LdapUserSearchCmd;
+import org.apache.cloudstack.api.response.LdapConfigurationResponse;
+import org.apache.cloudstack.api.response.LdapUserResponse;
+import org.apache.cloudstack.ldap.dao.LdapConfigurationDao;
+
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.utils.Pair;
+
+@Component
+@Local(value = LdapManager.class)
+public class LdapManagerImpl implements LdapManager {
+    private static final Logger s_logger = Logger.getLogger(LdapManagerImpl.class.getName());
+
+    @Inject
+    private LdapConfigurationDao _ldapConfigurationDao;
+
+    @Inject
+    private LdapContextFactory _ldapContextFactory;
+
+    @Inject
+    private LdapUserManager _ldapUserManager;
+
+    public LdapManagerImpl() {
+        super();
+    }
+
+    public LdapManagerImpl(final LdapConfigurationDao ldapConfigurationDao, final LdapContextFactory ldapContextFactory, final LdapUserManager ldapUserManager) {
+        super();
+        _ldapConfigurationDao = ldapConfigurationDao;
+        _ldapContextFactory = ldapContextFactory;
+        _ldapUserManager = ldapUserManager;
+    }
+
+    @Override
+    public LdapConfigurationResponse addConfiguration(final String hostname, final int port) throws InvalidParameterValueException {
+        LdapConfigurationVO configuration = _ldapConfigurationDao.findByHostname(hostname);
+        if (configuration == null) {
+            try {
+                final String providerUrl = "ldap://" + hostname + ":" + port;
+                _ldapContextFactory.createBindContext(providerUrl);
+                configuration = new LdapConfigurationVO(hostname, port);
+                _ldapConfigurationDao.persist(configuration);
+                s_logger.info("Added new ldap server with hostname: " + hostname);
+                return new LdapConfigurationResponse(hostname, port);
+            } catch (final NamingException e) {
+                throw new InvalidParameterValueException("Unable to bind to the given LDAP server");
+            }
+        } else {
+            throw new InvalidParameterValueException("Duplicate configuration");
+        }
+    }
+
+    @Override
+    public boolean canAuthenticate(final String username, final String password) {
+        final String escapedUsername = LdapUtils.escapeLDAPSearchFilter(username);
+        try {
+            final LdapUser user = getUser(escapedUsername);
+            final String principal = user.getPrincipal();
+            final DirContext context = _ldapContextFactory.createUserContext(principal, password);
+            closeContext(context);
+            return true;
+        } catch (final NamingException e) {
+            s_logger.info("Failed to authenticate user: " + username + ". incorrect password.");
+            return false;
+        }
+    }
+
+    private void closeContext(final DirContext context) {
+        try {
+            if (context != null) {
+                context.close();
+            }
+        } catch (final NamingException e) {
+            s_logger.warn(e.getMessage());
+        }
+    }
+
+    @Override
+    public LdapConfigurationResponse createLdapConfigurationResponse(final LdapConfigurationVO configuration) {
+        final LdapConfigurationResponse response = new LdapConfigurationResponse();
+        response.setHostname(configuration.getHostname());
+        response.setPort(configuration.getPort());
+        return response;
+    }
+
+    @Override
+    public LdapUserResponse createLdapUserResponse(final LdapUser user) {
+        final LdapUserResponse response = new LdapUserResponse();
+        response.setUsername(user.getUsername());
+        response.setFirstname(user.getFirstname());
+        response.setLastname(user.getLastname());
+        response.setEmail(user.getEmail());
+        response.setPrincipal(user.getPrincipal());
+        return response;
+    }
+
+    @Override
+    public LdapConfigurationResponse deleteConfiguration(final String hostname) throws InvalidParameterValueException {
+        final LdapConfigurationVO configuration = _ldapConfigurationDao.findByHostname(hostname);
+        if (configuration == null) {
+            throw new InvalidParameterValueException("Cannot find configuration with hostname " + hostname);
+        } else {
+            _ldapConfigurationDao.remove(configuration.getId());
+            s_logger.info("Removed ldap server with hostname: " + hostname);
+            return new LdapConfigurationResponse(configuration.getHostname(), configuration.getPort());
+        }
+    }
+
+    @Override
+    public List<Class<?>> getCommands() {
+        final List<Class<?>> cmdList = new ArrayList<Class<?>>();
+        cmdList.add(LdapUserSearchCmd.class);
+        cmdList.add(LdapListAllUsersCmd.class);
+        cmdList.add(LdapAddConfigurationCmd.class);
+        cmdList.add(LdapDeleteConfigurationCmd.class);
+        cmdList.add(LdapListConfigurationCmd.class);
+        cmdList.add(LdapCreateAccount.class);
+        return cmdList;
+    }
+
+    @Override
+    public LdapUser getUser(final String username) throws NamingException {
+        DirContext context = null;
+        try {
+            context = _ldapContextFactory.createBindContext();
+
+            final String escapedUsername = LdapUtils.escapeLDAPSearchFilter(username);
+            return _ldapUserManager.getUser(escapedUsername, context);
+
+        } catch (final NamingException e) {
+            throw e;
+        } finally {
+            closeContext(context);
+        }
+    }
+
+    @Override
+    public List<LdapUser> getUsers() throws NoLdapUserMatchingQueryException {
+        DirContext context = null;
+        try {
+            context = _ldapContextFactory.createBindContext();
+            return _ldapUserManager.getUsers(context);
+        } catch (final NamingException e) {
+            throw new NoLdapUserMatchingQueryException("*");
+        } finally {
+            closeContext(context);
+        }
+    }
+
+    @Override
+    public Pair<List<? extends LdapConfigurationVO>, Integer> listConfigurations(final LdapListConfigurationCmd cmd) {
+        final String hostname = cmd.getHostname();
+        final int port = cmd.getPort();
+        final Pair<List<LdapConfigurationVO>, Integer> result = _ldapConfigurationDao.searchConfigurations(hostname, port);
+        return new Pair<List<? extends LdapConfigurationVO>, Integer>(result.first(), result.second());
+    }
+
+    @Override
+    public List<LdapUser> searchUsers(final String username) throws NoLdapUserMatchingQueryException {
+        DirContext context = null;
+        try {
+            context = _ldapContextFactory.createBindContext();
+            final String escapedUsername = LdapUtils.escapeLDAPSearchFilter(username);
+            return _ldapUserManager.getUsers("*" + escapedUsername + "*", context);
+        } catch (final NamingException e) {
+            throw new NoLdapUserMatchingQueryException(username);
+        } finally {
+            closeContext(context);
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapUser.java
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapUser.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapUser.java
new file mode 100644
index 0000000..6bc1a78
--- /dev/null
+++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapUser.java
@@ -0,0 +1,75 @@
+// 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.
+package org.apache.cloudstack.ldap;
+
+public class LdapUser implements Comparable<LdapUser> {
+    private final String email;
+    private final String principal;
+    private final String firstname;
+    private final String lastname;
+    private final String username;
+
+    public LdapUser(final String username, final String email, final String firstname, final String lastname, final String principal) {
+        this.username = username;
+        this.email = email;
+        this.firstname = firstname;
+        this.lastname = lastname;
+        this.principal = principal;
+    }
+
+    @Override
+    public int compareTo(final LdapUser other) {
+        return getUsername().compareTo(other.getUsername());
+    }
+
+    @Override
+    public boolean equals(final Object other) {
+        if (this == other) {
+            return true;
+        }
+        if (other instanceof LdapUser) {
+            final LdapUser otherLdapUser = (LdapUser)other;
+            return getUsername().equals(otherLdapUser.getUsername());
+        }
+        return false;
+    }
+
+    public String getEmail() {
+        return email;
+    }
+
+    public String getFirstname() {
+        return firstname;
+    }
+
+    public String getLastname() {
+        return lastname;
+    }
+
+    public String getPrincipal() {
+        return principal;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    @Override
+    public int hashCode() {
+        return getUsername().hashCode();
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapUserManager.java
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapUserManager.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapUserManager.java
new file mode 100644
index 0000000..f255752
--- /dev/null
+++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapUserManager.java
@@ -0,0 +1,98 @@
+// 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.
+package org.apache.cloudstack.ldap;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.inject.Inject;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+
+public class LdapUserManager {
+
+    @Inject
+    private LdapConfiguration _ldapConfiguration;
+
+    public LdapUserManager() {
+    }
+
+    public LdapUserManager(final LdapConfiguration ldapConfiguration) {
+        _ldapConfiguration = ldapConfiguration;
+    }
+
+    private LdapUser createUser(final SearchResult result) throws NamingException {
+        final Attributes attributes = result.getAttributes();
+
+        final String username = LdapUtils.getAttributeValue(attributes, _ldapConfiguration.getUsernameAttribute());
+        final String email = LdapUtils.getAttributeValue(attributes, _ldapConfiguration.getEmailAttribute());
+        final String firstname = LdapUtils.getAttributeValue(attributes, _ldapConfiguration.getFirstnameAttribute());
+        final String lastname = LdapUtils.getAttributeValue(attributes, _ldapConfiguration.getLastnameAttribute());
+        final String principal = result.getName() + "," + _ldapConfiguration.getBaseDn();
+
+        return new LdapUser(username, email, firstname, lastname, principal);
+    }
+
+    public LdapUser getUser(final String username, final DirContext context) throws NamingException {
+        final NamingEnumeration<SearchResult> result = searchUsers(username, context);
+        if (result.hasMoreElements()) {
+            return createUser(result.nextElement());
+        } else {
+            throw new NamingException("No user found for username " + username);
+        }
+    }
+
+    public List<LdapUser> getUsers(final DirContext context) throws NamingException {
+        return getUsers(null, context);
+    }
+
+    public List<LdapUser> getUsers(final String username, final DirContext context) throws NamingException {
+        final NamingEnumeration<SearchResult> results = searchUsers(username, context);
+
+        final List<LdapUser> users = new ArrayList<LdapUser>();
+
+        while (results.hasMoreElements()) {
+            final SearchResult result = results.nextElement();
+            users.add(createUser(result));
+        }
+
+        Collections.sort(users);
+
+        return users;
+    }
+
+    public NamingEnumeration<SearchResult> searchUsers(final DirContext context) throws NamingException {
+        return searchUsers(null, context);
+    }
+
+    public NamingEnumeration<SearchResult> searchUsers(final String username, final DirContext context) throws NamingException {
+        final SearchControls controls = new SearchControls();
+
+        controls.setSearchScope(_ldapConfiguration.getScope());
+        controls.setReturningAttributes(_ldapConfiguration.getReturnAttributes());
+
+        final String filter = "(&(objectClass=" + _ldapConfiguration.getUserObject() + ")" + "("
+                + _ldapConfiguration.getUsernameAttribute() + "=" + (username == null ? "*" : username) + "))";
+
+        return context.search(_ldapConfiguration.getBaseDn(), filter, controls);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapUtils.java
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapUtils.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapUtils.java
new file mode 100644
index 0000000..8e7e93e
--- /dev/null
+++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapUtils.java
@@ -0,0 +1,61 @@
+// 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.
+package org.apache.cloudstack.ldap;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+
+public final class LdapUtils {
+    public static String escapeLDAPSearchFilter(final String filter) {
+        final StringBuilder sb = new StringBuilder();
+        for (char character : filter.toCharArray()) {
+            switch (character) {
+            case '\\':
+                sb.append("\\5c");
+                break;
+            case '*':
+                sb.append("\\2a");
+                break;
+            case '(':
+                sb.append("\\28");
+                break;
+            case ')':
+                sb.append("\\29");
+                break;
+            case '\u0000':
+                sb.append("\\00");
+                break;
+            default:
+                sb.append(character);
+            }
+        }
+        return sb.toString();
+    }
+
+    public static String getAttributeValue(final Attributes attributes, final String attributeName) throws NamingException {
+        final Attribute attribute = attributes.get(attributeName);
+        if (attribute != null) {
+            final Object value = attribute.get();
+            return String.valueOf(value);
+        }
+        return null;
+    }
+
+    private LdapUtils() {
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/NoLdapUserMatchingQueryException.java
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/NoLdapUserMatchingQueryException.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/NoLdapUserMatchingQueryException.java
new file mode 100644
index 0000000..0f510c3
--- /dev/null
+++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/NoLdapUserMatchingQueryException.java
@@ -0,0 +1,32 @@
+// 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.
+package org.apache.cloudstack.ldap;
+
+public class NoLdapUserMatchingQueryException extends Exception {
+    private static final long serialVersionUID = 7124360347208388174L;
+
+    private final String query;
+
+    public NoLdapUserMatchingQueryException(final String query) {
+        super("No users matching: " + query);
+        this.query = query;
+    }
+
+    public String getQuery() {
+        return query;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/NoSuchLdapUserException.java
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/NoSuchLdapUserException.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/NoSuchLdapUserException.java
new file mode 100644
index 0000000..d9bf13f
--- /dev/null
+++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/NoSuchLdapUserException.java
@@ -0,0 +1,31 @@
+// 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.
+package org.apache.cloudstack.ldap;
+
+public class NoSuchLdapUserException extends Exception {
+    private static final long serialVersionUID = 6782938919658010900L;
+    private final String username;
+
+    public NoSuchLdapUserException(final String username) {
+        super("No such user: " + username);
+        this.username = username;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/dao/LdapConfigurationDao.java
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/dao/LdapConfigurationDao.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/dao/LdapConfigurationDao.java
new file mode 100644
index 0000000..a2d5e65
--- /dev/null
+++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/dao/LdapConfigurationDao.java
@@ -0,0 +1,30 @@
+// 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.
+package org.apache.cloudstack.ldap.dao;
+
+import java.util.List;
+
+import org.apache.cloudstack.ldap.LdapConfigurationVO;
+
+import com.cloud.utils.Pair;
+import com.cloud.utils.db.GenericDao;
+
+public interface LdapConfigurationDao extends GenericDao<LdapConfigurationVO, Long> {
+    LdapConfigurationVO findByHostname(String hostname);
+
+    Pair<List<LdapConfigurationVO>, Integer> searchConfigurations(String hostname, int port);
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/dao/LdapConfigurationDaoImpl.java
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/dao/LdapConfigurationDaoImpl.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/dao/LdapConfigurationDaoImpl.java
new file mode 100644
index 0000000..0f2a015
--- /dev/null
+++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/dao/LdapConfigurationDaoImpl.java
@@ -0,0 +1,66 @@
+// 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.
+package org.apache.cloudstack.ldap.dao;
+
+import java.util.List;
+
+import javax.ejb.Local;
+
+import org.springframework.stereotype.Component;
+
+import org.apache.cloudstack.ldap.LdapConfigurationVO;
+
+import com.cloud.utils.Pair;
+import com.cloud.utils.db.GenericDaoBase;
+import com.cloud.utils.db.SearchBuilder;
+import com.cloud.utils.db.SearchCriteria;
+import com.cloud.utils.db.SearchCriteria.Op;
+
+@Component
+@Local(value = {LdapConfigurationDao.class})
+public class LdapConfigurationDaoImpl extends GenericDaoBase<LdapConfigurationVO, Long> implements LdapConfigurationDao {
+    private final SearchBuilder<LdapConfigurationVO> hostnameSearch;
+    private final SearchBuilder<LdapConfigurationVO> listAllConfigurationsSearch;
+
+    public LdapConfigurationDaoImpl() {
+        super();
+        hostnameSearch = createSearchBuilder();
+        hostnameSearch.and("hostname", hostnameSearch.entity().getHostname(), SearchCriteria.Op.EQ);
+        hostnameSearch.done();
+
+        listAllConfigurationsSearch = createSearchBuilder();
+        listAllConfigurationsSearch.and("hostname", listAllConfigurationsSearch.entity().getHostname(), Op.EQ);
+        listAllConfigurationsSearch.and("port", listAllConfigurationsSearch.entity().getPort(), Op.EQ);
+        listAllConfigurationsSearch.done();
+    }
+
+    @Override
+    public LdapConfigurationVO findByHostname(final String hostname) {
+        final SearchCriteria<LdapConfigurationVO> sc = hostnameSearch.create();
+        sc.setParameters("hostname", hostname);
+        return findOneBy(sc);
+    }
+
+    @Override
+    public Pair<List<LdapConfigurationVO>, Integer> searchConfigurations(final String hostname, final int port) {
+        final SearchCriteria<LdapConfigurationVO> sc = listAllConfigurationsSearch.create();
+        if (hostname != null) {
+            sc.setParameters("hostname", hostname);
+        }
+        return searchAndCount(sc, null);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/BasicNamingEnumerationImpl.groovy
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/BasicNamingEnumerationImpl.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/BasicNamingEnumerationImpl.groovy
new file mode 100644
index 0000000..ab7e22a
--- /dev/null
+++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/BasicNamingEnumerationImpl.groovy
@@ -0,0 +1,56 @@
+// 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.
+package groovy.org.apache.cloudstack.ldap
+
+import javax.naming.NamingEnumeration
+import javax.naming.NamingException
+import javax.naming.directory.SearchResult
+
+class BasicNamingEnumerationImpl implements NamingEnumeration {
+
+    private LinkedList<String> items = new LinkedList<SearchResult>();
+
+    @Override
+    public boolean hasMoreElements() {
+        return items.size != 0;
+    }
+
+    @Override
+    public Object nextElement() {
+        SearchResult result = items.getFirst();
+        items.removeFirst();
+        return result;
+    }
+
+    @Override
+    public void close() throws NamingException {
+    }
+
+    @Override
+    public boolean hasMore() throws NamingException {
+        return hasMoreElements();
+    }
+
+    @Override
+    public Object next() throws NamingException {
+        return nextElement();
+    }
+
+    public void add(SearchResult item) {
+        items.add(item)
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapAddConfigurationCmdSpec.groovy
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapAddConfigurationCmdSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapAddConfigurationCmdSpec.groovy
new file mode 100644
index 0000000..3dcb23f
--- /dev/null
+++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapAddConfigurationCmdSpec.groovy
@@ -0,0 +1,89 @@
+// 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.
+package groovy.org.apache.cloudstack.ldap
+
+import com.cloud.exception.InvalidParameterValueException
+import org.apache.cloudstack.api.ServerApiException
+import org.apache.cloudstack.api.command.LdapAddConfigurationCmd
+import org.apache.cloudstack.api.response.LdapConfigurationResponse
+import org.apache.cloudstack.ldap.LdapManager
+
+class LdapAddConfigurationCmdSpec extends spock.lang.Specification {
+
+    def "Test successful response from execute"() {
+        given:
+        def ldapManager = Mock(LdapManager)
+        ldapManager.addConfiguration(_, _) >> new LdapConfigurationResponse("localhost", 389)
+        def ldapAddConfigurationCmd = new LdapAddConfigurationCmd(ldapManager)
+        when:
+        ldapAddConfigurationCmd.execute()
+        then:
+        ldapAddConfigurationCmd.responseObject.hostname == "localhost"
+        ldapAddConfigurationCmd.responseObject.port == 389
+    }
+
+    def "Test failed response from execute"() {
+        given:
+        def ldapManager = Mock(LdapManager)
+        ldapManager.addConfiguration(_, _) >> { throw new InvalidParameterValueException() }
+        def ldapAddConfigurationCmd = new LdapAddConfigurationCmd(ldapManager)
+        when:
+        ldapAddConfigurationCmd.execute()
+        then:
+        thrown ServerApiException
+    }
+
+    def "Test successful setting of hostname"() {
+        given:
+        def ldapManager = Mock(LdapManager)
+        def ldapAddConfigurationCmd = new LdapAddConfigurationCmd(ldapManager)
+        when:
+        ldapAddConfigurationCmd.setHostname("localhost")
+        then:
+        ldapAddConfigurationCmd.getHostname() == "localhost"
+    }
+
+    def "Test successful setting of port"() {
+        given:
+        def ldapManager = Mock(LdapManager)
+        def ldapAddConfigurationCmd = new LdapAddConfigurationCmd(ldapManager)
+        when:
+        ldapAddConfigurationCmd.setPort(389)
+        then:
+        ldapAddConfigurationCmd.getPort() == 389
+    }
+
+    def "Test getEntityOwnerId is 0"() {
+        given:
+        def ldapManager = Mock(LdapManager)
+        def ldapAddConfigurationCmd = new LdapAddConfigurationCmd(ldapManager)
+        when:
+        long ownerId = ldapAddConfigurationCmd.getEntityOwnerId()
+        then:
+        ownerId == 1
+    }
+
+    def "Test successful return of getCommandName"() {
+        given:
+        def ldapManager = Mock(LdapManager)
+        def ldapAddConfigurationCmd = new LdapAddConfigurationCmd(ldapManager)
+        when:
+        String commandName = ldapAddConfigurationCmd.getCommandName()
+        then:
+        commandName == "ldapconfigurationresponse"
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapAuthenticatorSpec.groovy
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapAuthenticatorSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapAuthenticatorSpec.groovy
new file mode 100644
index 0000000..573f88c
--- /dev/null
+++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapAuthenticatorSpec.groovy
@@ -0,0 +1,90 @@
+// 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.
+package groovy.org.apache.cloudstack.ldap
+
+import com.cloud.user.UserAccountVO
+import com.cloud.user.dao.UserAccountDao
+import com.cloud.utils.Pair
+import org.apache.cloudstack.ldap.LdapAuthenticator
+import org.apache.cloudstack.ldap.LdapConfigurationVO
+import org.apache.cloudstack.ldap.LdapManager
+
+class LdapAuthenticatorSpec extends spock.lang.Specification {
+
+    def "Test a failed authentication due to user not being found within cloudstack"() {
+        given:
+        LdapManager ldapManager = Mock(LdapManager)
+        UserAccountDao userAccountDao = Mock(UserAccountDao)
+        userAccountDao.getUserAccount(_, _) >> null
+        def ldapAuthenticator = new LdapAuthenticator(ldapManager, userAccountDao)
+        when:
+        def result = ldapAuthenticator.authenticate("rmurphy", "password", 0, null)
+        then:
+        result == false
+    }
+
+    def "Test failed authentication due to ldap not being configured"() {
+        given:
+        def ldapManager = Mock(LdapManager)
+        List<LdapConfigurationVO> ldapConfigurationList = new ArrayList()
+        Pair<List<LdapConfigurationVO>, Integer> ldapConfigurations = new Pair<List<LdapConfigurationVO>, Integer>();
+        ldapConfigurations.set(ldapConfigurationList, ldapConfigurationList.size())
+        ldapManager.listConfigurations(_) >> ldapConfigurations
+
+        UserAccountDao userAccountDao = Mock(UserAccountDao)
+        userAccountDao.getUserAccount(_, _) >> new UserAccountVO()
+
+        def ldapAuthenticator = new LdapAuthenticator(ldapManager, userAccountDao)
+        when:
+        def result = ldapAuthenticator.authenticate("rmurphy", "password", 0, null)
+        then:
+        result == false
+    }
+
+    def "Test failed authentication due to ldap bind being unsuccessful"() {
+        given:
+
+        def ldapManager = Mock(LdapManager)
+        List<LdapConfigurationVO> ldapConfigurationList = new ArrayList()
+        ldapConfigurationList.add(new LdapConfigurationVO("localhost", 389))
+        Pair<List<LdapConfigurationVO>, Integer> ldapConfigurations = new Pair<List<LdapConfigurationVO>, Integer>();
+        ldapConfigurations.set(ldapConfigurationList, ldapConfigurationList.size())
+        ldapManager.listConfigurations(_) >> ldapConfigurations
+        ldapManager.canAuthenticate(_, _) >> false
+
+        UserAccountDao userAccountDao = Mock(UserAccountDao)
+        userAccountDao.getUserAccount(_, _) >> new UserAccountVO()
+        def ldapAuthenticator = new LdapAuthenticator(ldapManager, userAccountDao)
+
+        when:
+        def result = ldapAuthenticator.authenticate("rmurphy", "password", 0, null)
+
+        then:
+        result == false
+    }
+
+    def "Test that encode doesn't change the input"() {
+        given:
+        LdapManager ldapManager = Mock(LdapManager)
+        UserAccountDao userAccountDao = Mock(UserAccountDao)
+        def ldapAuthenticator = new LdapAuthenticator(ldapManager, userAccountDao)
+        when:
+        def result = ldapAuthenticator.encode("password")
+        then:
+        result == "password"
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapConfigurationDaoImplSpec.groovy
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapConfigurationDaoImplSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapConfigurationDaoImplSpec.groovy
new file mode 100644
index 0000000..191b609
--- /dev/null
+++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapConfigurationDaoImplSpec.groovy
@@ -0,0 +1,29 @@
+// 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.
+package groovy.org.apache.cloudstack.ldap
+
+import org.apache.cloudstack.ldap.dao.LdapConfigurationDaoImpl
+
+class LdapConfigurationDaoImplSpec extends spock.lang.Specification {
+    def "Test setting up of a LdapConfigurationDao"() {
+        given:
+        def ldapConfigurationDaoImpl = new LdapConfigurationDaoImpl();
+        expect:
+        ldapConfigurationDaoImpl.hostnameSearch != null;
+        ldapConfigurationDaoImpl.listAllConfigurationsSearch != null
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapConfigurationResponseSpec.groovy
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapConfigurationResponseSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapConfigurationResponseSpec.groovy
new file mode 100644
index 0000000..4e6bebb
--- /dev/null
+++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapConfigurationResponseSpec.groovy
@@ -0,0 +1,49 @@
+// 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.
+package groovy.org.apache.cloudstack.ldap
+
+import org.apache.cloudstack.api.response.LdapConfigurationResponse
+
+class LdapConfigurationResponseSpec extends spock.lang.Specification {
+    def "Testing succcessful setting of LdapConfigurationResponse hostname"() {
+        given:
+        LdapConfigurationResponse response = new LdapConfigurationResponse();
+        when:
+        response.setHostname("localhost");
+        then:
+        response.getHostname() == "localhost";
+    }
+
+    def "Testing successful setting of LdapConfigurationResponse port"() {
+        given:
+        LdapConfigurationResponse response = new LdapConfigurationResponse()
+        when:
+        response.setPort(389)
+        then:
+        response.getPort() == 389
+    }
+
+    def "Testing successful setting of LdapConfigurationResponse hostname and port via constructor"() {
+        given:
+        LdapConfigurationResponse response
+        when:
+        response = new LdapConfigurationResponse("localhost", 389)
+        then:
+        response.getHostname() == "localhost"
+        response.getPort() == 389
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapConfigurationSpec.groovy
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapConfigurationSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapConfigurationSpec.groovy
new file mode 100644
index 0000000..a867fd6
--- /dev/null
+++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapConfigurationSpec.groovy
@@ -0,0 +1,181 @@
+// 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.
+package groovy.org.apache.cloudstack.ldap
+
+import com.cloud.configuration.dao.ConfigurationDao
+import com.cloud.utils.Pair
+import org.apache.cloudstack.api.ServerApiException
+import org.apache.cloudstack.ldap.LdapConfiguration
+import org.apache.cloudstack.ldap.LdapConfigurationVO
+import org.apache.cloudstack.ldap.LdapManager
+
+import javax.naming.directory.SearchControls
+
+class LdapConfigurationSpec extends spock.lang.Specification {
+    def "Test that providerUrl successfully returns a URL when a configuration is available"() {
+        given:
+        def configDao = Mock(ConfigurationDao)
+
+        def ldapManager = Mock(LdapManager)
+        List<LdapConfigurationVO> ldapConfigurationList = new ArrayList()
+        ldapConfigurationList.add(new LdapConfigurationVO("localhost", 389))
+        Pair<List<LdapConfigurationVO>, Integer> result = new Pair<List<LdapConfigurationVO>, Integer>();
+        result.set(ldapConfigurationList, ldapConfigurationList.size())
+        ldapManager.listConfigurations(_) >> result
+
+        LdapConfiguration ldapConfiguration = new LdapConfiguration(configDao, ldapManager)
+
+        when:
+        String providerUrl = ldapConfiguration.getProviderUrl()
+
+        then:
+        providerUrl == "ldap://localhost:389"
+    }
+
+    def "Test that getAuthentication returns simple"() {
+        given:
+        def configDao = Mock(ConfigurationDao)
+        def ldapManager = Mock(LdapManager)
+        def ldapConfiguration = new LdapConfiguration(configDao, ldapManager)
+        configDao.getValue("ldap.bind.password") >> "password"
+        configDao.getValue("ldap.bind.principal") >> "cn=rmurphy,dc=cloudstack,dc=org"
+        when:
+        String authentication = ldapConfiguration.getAuthentication()
+        then:
+        authentication == "simple"
+    }
+
+    def "Test that getAuthentication returns none"() {
+        given:
+        def configDao = Mock(ConfigurationDao)
+        def ldapManager = Mock(LdapManager)
+        def ldapConfiguration = new LdapConfiguration(configDao, ldapManager)
+        when:
+        String authentication = ldapConfiguration.getAuthentication()
+        then:
+        authentication == "none"
+    }
+
+    def "Test that getEmailAttribute returns mail"() {
+        given:
+        def configDao = Mock(ConfigurationDao)
+        configDao.getValue("ldap.email.attribute") >> "mail"
+        def ldapManager = Mock(LdapManager)
+        def ldapConfiguration = new LdapConfiguration(configDao, ldapManager)
+        when:
+        String emailAttribute = ldapConfiguration.getEmailAttribute()
+        then:
+        emailAttribute == "mail"
+    }
+
+    def "Test that getUsernameAttribute returns uid"() {
+        given:
+        def configDao = Mock(ConfigurationDao)
+        configDao.getValue("ldap.username.attribute") >> "uid"
+        def ldapManager = Mock(LdapManager)
+        def ldapConfiguration = new LdapConfiguration(configDao, ldapManager)
+        when:
+        String usernameAttribute = ldapConfiguration.getUsernameAttribute()
+        then:
+        usernameAttribute == "uid"
+    }
+
+    def "Test that getFirstnameAttribute returns givenname"() {
+        given:
+        def configDao = Mock(ConfigurationDao)
+        configDao.getValue("ldap.firstname.attribute") >> "givenname"
+        def ldapManager = Mock(LdapManager)
+        def ldapConfiguration = new LdapConfiguration(configDao, ldapManager)
+        when:
+        String firstname = ldapConfiguration.getFirstnameAttribute()
+        then:
+        firstname == "givenname"
+    }
+
+    def "Test that getLastnameAttribute returns givenname"() {
+        given:
+        def configDao = Mock(ConfigurationDao)
+        configDao.getValue("ldap.lastname.attribute") >> "sn"
+        def ldapManager = Mock(LdapManager)
+        def ldapConfiguration = new LdapConfiguration(configDao, ldapManager)
+        when:
+        String lastname = ldapConfiguration.getLastnameAttribute()
+        then:
+        lastname == "sn"
+    }
+
+    def "Test that getUserObject returns inetOrgPerson"() {
+        given:
+        def configDao = Mock(ConfigurationDao)
+        configDao.getValue("ldap.user.object") >> "inetOrgPerson"
+        def ldapManager = Mock(LdapManager)
+        def ldapConfiguration = new LdapConfiguration(configDao, ldapManager)
+        when:
+        String userObject = ldapConfiguration.getUserObject()
+        then:
+        userObject == "inetOrgPerson"
+    }
+
+    def "Test that getReturnAttributes returns the correct data"() {
+        given:
+        def configDao = Mock(ConfigurationDao)
+        configDao.getValue("ldap.firstname.attribute") >> "givenname"
+        configDao.getValue("ldap.lastname.attribute") >> "sn"
+        configDao.getValue("ldap.username.attribute") >> "uid"
+        configDao.getValue("ldap.email.attribute") >> "mail"
+        def ldapManager = Mock(LdapManager)
+        def ldapConfiguration = new LdapConfiguration(configDao, ldapManager)
+        when:
+        String[] returnAttributes = ldapConfiguration.getReturnAttributes()
+        then:
+        returnAttributes == ["uid", "mail", "givenname", "sn"]
+    }
+
+    def "Test that getScope returns SearchControls.SUBTREE_SCOPE"() {
+        given:
+        def configDao = Mock(ConfigurationDao)
+        def ldapManager = Mock(LdapManager)
+        def ldapConfiguration = new LdapConfiguration(configDao, ldapManager)
+        when:
+        int scope = ldapConfiguration.getScope()
+        then:
+        scope == SearchControls.SUBTREE_SCOPE;
+    }
+
+    def "Test that getBaseDn returns dc=cloudstack,dc=org"() {
+        given:
+        def configDao = Mock(ConfigurationDao)
+        configDao.getValue("ldap.basedn") >> "dc=cloudstack,dc=org"
+        def ldapManager = Mock(LdapManager)
+        def ldapConfiguration = new LdapConfiguration(configDao, ldapManager)
+        when:
+        String baseDn = ldapConfiguration.getBaseDn();
+        then:
+        baseDn == "dc=cloudstack,dc=org"
+    }
+
+    def "Test that getFactory returns com.sun.jndi.ldap.LdapCtxFactory"() {
+        given:
+        def configDao = Mock(ConfigurationDao)
+        def ldapManager = Mock(LdapManager)
+        def ldapConfiguration = new LdapConfiguration(configDao, ldapManager)
+        when:
+        String factory = ldapConfiguration.getFactory();
+        then:
+        factory == "com.sun.jndi.ldap.LdapCtxFactory"
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapConfigurationVO.groovy
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapConfigurationVO.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapConfigurationVO.groovy
new file mode 100644
index 0000000..27f3dfc
--- /dev/null
+++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapConfigurationVO.groovy
@@ -0,0 +1,36 @@
+// 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.
+package groovy.org.apache.cloudstack.ldap
+
+import org.apache.cloudstack.ldap.LdapConfigurationVO
+
+
+class LdapConfigurationVOSpec extends spock.lang.Specification {
+    def "Testing that the ID hostname and port is correctly set within the LDAP configuration VO"() {
+        given: "You have created an LDAP Configuration VO"
+        def configuration = new LdapConfigurationVO(hostname, port)
+        configuration.setId(id)
+        expect: "The id hostname and port is equal to the given data source"
+        configuration.getId() == id
+        configuration.getHostname() == hostname
+        configuration.getPort() == port
+        where: "The id, hostname and port is set to "
+        hostname << ["", null, "localhost"]
+        id << [0, 1000, -1000]
+        port << [0, 1000, -1000]
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapContextFactorySpec.groovy
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapContextFactorySpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapContextFactorySpec.groovy
new file mode 100644
index 0000000..e9b3b6e
--- /dev/null
+++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapContextFactorySpec.groovy
@@ -0,0 +1,134 @@
+// 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.
+package groovy.org.apache.cloudstack.ldap
+
+import org.apache.cloudstack.ldap.LdapConfiguration
+import org.apache.cloudstack.ldap.LdapContextFactory
+import spock.lang.Shared
+
+import javax.naming.NamingException
+import javax.naming.directory.SearchControls
+import javax.naming.ldap.LdapContext
+
+class LdapContextFactorySpec extends spock.lang.Specification {
+    @Shared
+    private def ldapConfiguration
+
+    @Shared
+    private def username
+
+    @Shared
+    private def principal
+
+    @Shared
+    private def password
+
+    def setupSpec() {
+        ldapConfiguration = Mock(LdapConfiguration)
+
+        ldapConfiguration.getFactory() >> "com.sun.jndi.ldap.LdapCtxFactory"
+        ldapConfiguration.getProviderUrl() >> "ldap://localhost:389"
+        ldapConfiguration.getAuthentication() >> "none"
+        ldapConfiguration.getScope() >> SearchControls.SUBTREE_SCOPE
+        ldapConfiguration.getReturnAttributes() >> ["uid", "mail", "cn"]
+        ldapConfiguration.getUsernameAttribute() >> "uid"
+        ldapConfiguration.getEmailAttribute() >> "mail"
+        ldapConfiguration.getFirstnameAttribute() >> "givenname"
+        ldapConfiguration.getLastnameAttribute() >> "sn"
+        ldapConfiguration.getBaseDn() >> "dc=cloudstack,dc=org"
+
+        username = "rmurphy"
+        principal = "cn=" + username + "," + ldapConfiguration.getBaseDn()
+        password = "password"
+    }
+
+    def "Test successfully creating a system environment with anon bind"() {
+        given:
+        def ldapContextFactory = new LdapContextFactory(ldapConfiguration)
+
+        when:
+        def result = ldapContextFactory.getEnvironment(principal, password, null, false)
+
+        then:
+        result['java.naming.provider.url'] == ldapConfiguration.getProviderUrl()
+        result['java.naming.factory.initial'] == ldapConfiguration.getFactory()
+        result['java.naming.security.principal'] == principal
+        result['java.naming.security.authentication'] == "simple"
+        result['java.naming.security.credentials'] == password
+    }
+
+    def "Test successfully creating a environment with username and password"() {
+        given:
+        def ldapContextFactory = new LdapContextFactory(ldapConfiguration)
+
+        when:
+        def result = ldapContextFactory.getEnvironment(null, null, null, true)
+
+        then:
+        result['java.naming.provider.url'] == ldapConfiguration.getProviderUrl()
+        result['java.naming.factory.initial'] == ldapConfiguration.getFactory()
+        result['java.naming.security.principal'] == null
+        result['java.naming.security.authentication'] == ldapConfiguration.getAuthentication()
+        result['java.naming.security.credentials'] == null
+    }
+
+    def "Test successfully binding as a user"() {
+        given:
+        def ldapContextFactory = new LdapContextFactory(ldapConfiguration)
+        when:
+        ldapContextFactory.createUserContext(principal, password)
+        then:
+        thrown NamingException
+    }
+
+    def "Test successully binding as system"() {
+        given:
+        def ldapContextFactory = new LdapContextFactory(ldapConfiguration)
+        when:
+        ldapContextFactory.createBindContext()
+        then:
+        thrown NamingException
+    }
+
+    def "Test succcessfully creating a initial context"() {
+        given:
+        def ldapContextFactory = new LdapContextFactory(ldapConfiguration)
+        when:
+        ldapContextFactory.createInitialDirContext(null, null, true)
+        then:
+        thrown NamingException
+    }
+
+    def "Test successful failed connection"() {
+        given:
+        def ldapContextFactory = Spy(LdapContextFactory, constructorArgs: [ldapConfiguration])
+        when:
+        ldapContextFactory.testConnection(ldapConfiguration.getProviderUrl())
+        then:
+        thrown NamingException
+    }
+
+    def "Test successful connection"() {
+        given:
+        def ldapContextFactory = Spy(LdapContextFactory, constructorArgs: [ldapConfiguration])
+        ldapContextFactory.createBindContext(_) >> Mock(LdapContext)
+        when:
+        ldapContextFactory.testConnection(ldapConfiguration.getProviderUrl())
+        then:
+        notThrown NamingException
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapDeleteConfigurationCmdSpec.groovy
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapDeleteConfigurationCmdSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapDeleteConfigurationCmdSpec.groovy
new file mode 100644
index 0000000..f4d185b
--- /dev/null
+++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapDeleteConfigurationCmdSpec.groovy
@@ -0,0 +1,68 @@
+// 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.
+package groovy.org.apache.cloudstack.ldap
+
+import com.cloud.exception.InvalidParameterValueException
+import org.apache.cloudstack.api.ServerApiException
+import org.apache.cloudstack.api.command.LdapDeleteConfigurationCmd
+import org.apache.cloudstack.api.response.LdapConfigurationResponse
+import org.apache.cloudstack.ldap.LdapManager
+
+class LdapDeleteConfigurationCmdSpec extends spock.lang.Specification {
+
+    def "Test successful response from execute"() {
+        given:
+        def ldapManager = Mock(LdapManager)
+        ldapManager.deleteConfiguration(_) >> new LdapConfigurationResponse("localhost")
+        def ldapDeleteConfigurationCmd = new LdapDeleteConfigurationCmd(ldapManager)
+        when:
+        ldapDeleteConfigurationCmd.execute()
+        then:
+        ldapDeleteConfigurationCmd.responseObject.hostname == "localhost"
+    }
+
+    def "Test failed response from execute"() {
+        given:
+        def ldapManager = Mock(LdapManager)
+        ldapManager.deleteConfiguration(_) >> { throw new InvalidParameterValueException() }
+        def ldapDeleteConfigurationCmd = new LdapDeleteConfigurationCmd(ldapManager)
+        when:
+        ldapDeleteConfigurationCmd.execute()
+        then:
+        thrown ServerApiException
+    }
+
+    def "Test getEntityOwnerId is 0"() {
+        given:
+        def ldapManager = Mock(LdapManager)
+        def ldapDeleteConfigurationCmd = new LdapDeleteConfigurationCmd(ldapManager)
+        when:
+        long ownerId = ldapDeleteConfigurationCmd.getEntityOwnerId()
+        then:
+        ownerId == 1
+    }
+
+    def "Test successful return of getCommandName"() {
+        given:
+        def ldapManager = Mock(LdapManager)
+        def ldapDeleteConfigurationCmd = new LdapDeleteConfigurationCmd(ldapManager)
+        when:
+        String commandName = ldapDeleteConfigurationCmd.getCommandName()
+        then:
+        commandName == "ldapconfigurationresponse"
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapListAllUsersCmdSpec.groovy
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapListAllUsersCmdSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapListAllUsersCmdSpec.groovy
new file mode 100644
index 0000000..2756e92
--- /dev/null
+++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapListAllUsersCmdSpec.groovy
@@ -0,0 +1,72 @@
+// 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.
+package groovy.org.apache.cloudstack.ldap
+
+import org.apache.cloudstack.api.ServerApiException
+import org.apache.cloudstack.api.command.LdapListAllUsersCmd
+import org.apache.cloudstack.api.response.LdapUserResponse
+import org.apache.cloudstack.ldap.LdapManager
+import org.apache.cloudstack.ldap.LdapUser
+import org.apache.cloudstack.ldap.NoLdapUserMatchingQueryException
+
+class LdapListAllUsersCmdSpec extends spock.lang.Specification {
+    def "Test successful response from execute"() {
+        given:
+        def ldapManager = Mock(LdapManager)
+        List<LdapUser> users = new ArrayList()
+        users.add(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org"))
+        ldapManager.getUsers() >> users
+        LdapUserResponse response = new LdapUserResponse("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org")
+        ldapManager.createLdapUserResponse(_) >> response
+        def ldapListAllUsersCmd = new LdapListAllUsersCmd(ldapManager)
+        when:
+        ldapListAllUsersCmd.execute()
+        then:
+        ldapListAllUsersCmd.responseObject.getResponses().size() != 0
+    }
+
+    def "Test successful empty response from execute"() {
+        given:
+        def ldapManager = Mock(LdapManager)
+        ldapManager.getUsers() >> {throw new NoLdapUserMatchingQueryException()}
+        def ldapListAllUsersCmd = new LdapListAllUsersCmd(ldapManager)
+        when:
+        ldapListAllUsersCmd.execute()
+        then:
+        thrown ServerApiException
+    }
+
+    def "Test getEntityOwnerId is 1"() {
+        given:
+        def ldapManager = Mock(LdapManager)
+        def ldapListAllUsersCmd = new LdapListAllUsersCmd(ldapManager)
+        when:
+        long ownerId = ldapListAllUsersCmd.getEntityOwnerId()
+        then:
+        ownerId == 1
+    }
+
+    def "Test successful return of getCommandName"() {
+        given:
+        def ldapManager = Mock(LdapManager)
+        def ldapListAllUsersCmd = new LdapListAllUsersCmd(ldapManager)
+        when:
+        String commandName = ldapListAllUsersCmd.getCommandName()
+        then:
+        commandName == "ldapuserresponse"
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/eaa41433/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapListConfigurationCmdSpec.groovy
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapListConfigurationCmdSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapListConfigurationCmdSpec.groovy
new file mode 100644
index 0000000..d83b926
--- /dev/null
+++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapListConfigurationCmdSpec.groovy
@@ -0,0 +1,100 @@
+// 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.
+package groovy.org.apache.cloudstack.ldap
+
+import org.apache.cloudstack.api.ServerApiException
+import org.apache.cloudstack.api.command.LdapListConfigurationCmd
+import org.apache.cloudstack.api.response.LdapConfigurationResponse
+import org.apache.cloudstack.ldap.LdapConfigurationVO
+import org.apache.cloudstack.ldap.LdapManager
+
+import com.cloud.utils.Pair
+
+class LdapListConfigurationCmdSpec extends spock.lang.Specification {
+
+    def "Test successful response from execute"() {
+        given:
+        def ldapManager = Mock(LdapManager)
+        List<LdapConfigurationVO> ldapConfigurationList = new ArrayList()
+        ldapConfigurationList.add(new LdapConfigurationVO("localhost", 389))
+        Pair<List<LdapConfigurationVO>, Integer> ldapConfigurations = new Pair<List<LdapConfigurationVO>, Integer>();
+        ldapConfigurations.set(ldapConfigurationList, ldapConfigurationList.size())
+        ldapManager.listConfigurations(_) >> ldapConfigurations
+        ldapManager.createLdapConfigurationResponse(_) >> new LdapConfigurationResponse("localhost", 389)
+        def ldapListConfigurationCmd = new LdapListConfigurationCmd(ldapManager)
+        when:
+        ldapListConfigurationCmd.execute()
+        then:
+        ldapListConfigurationCmd.getResponseObject().getResponses().size() != 0
+    }
+
+    def "Test failed response from execute"() {
+        given:
+
+        def ldapManager = Mock(LdapManager)
+        List<LdapConfigurationVO> ldapConfigurationList = new ArrayList()
+        Pair<List<LdapConfigurationVO>, Integer> ldapConfigurations = new Pair<List<LdapConfigurationVO>, Integer>();
+        ldapConfigurations.set(ldapConfigurationList, ldapConfigurationList.size())
+        ldapManager.listConfigurations(_) >> ldapConfigurations
+
+        def ldapListConfigurationCmd = new LdapListConfigurationCmd(ldapManager)
+        when:
+        ldapListConfigurationCmd.execute()
+        then:
+        ldapListConfigurationCmd.getResponseObject().getResponses().size() == 0
+    }
+
+    def "Test successful setting of hostname"() {
+        given:
+        def ldapManager = Mock(LdapManager)
+        def ldapListConfigurationCmd = new LdapListConfigurationCmd(ldapManager)
+        when:
+        ldapListConfigurationCmd.setHostname("localhost")
+        then:
+        ldapListConfigurationCmd.getHostname() == "localhost"
+    }
+
+    def "Test successful setting of Port"() {
+        given:
+        def ldapManager = Mock(LdapManager)
+        def ldapListConfigurationCmd = new LdapListConfigurationCmd(ldapManager)
+        when:
+        ldapListConfigurationCmd.setPort(389)
+        then:
+        ldapListConfigurationCmd.getPort() == 389
+    }
+
+    def "Test getEntityOwnerId is 0"() {
+        given:
+        def ldapManager = Mock(LdapManager)
+        def ldapListConfigurationCmd = new LdapListConfigurationCmd(ldapManager)
+        when:
+        long ownerId = ldapListConfigurationCmd.getEntityOwnerId()
+        then:
+        ownerId == 1
+    }
+
+    def "Test successful return of getCommandName"() {
+        given:
+        def ldapManager = Mock(LdapManager)
+        def ldapListConfigurationCmd = new LdapListConfigurationCmd(ldapManager)
+        when:
+        String commandName = ldapListConfigurationCmd.getCommandName()
+        then:
+        commandName == "ldapconfigurationresponse"
+    }
+}