diff --git a/api/admin.go b/api/admin.go
index 3b324c75fb..d48c8d3798 100644
--- a/api/admin.go
+++ b/api/admin.go
@@ -42,6 +42,7 @@ func InitAdmin() {
BaseRoutes.Admin.Handle("/reset_mfa", ApiAdminSystemRequired(adminResetMfa)).Methods("POST")
BaseRoutes.Admin.Handle("/reset_password", ApiAdminSystemRequired(adminResetPassword)).Methods("POST")
BaseRoutes.Admin.Handle("/ldap_sync_now", ApiAdminSystemRequired(ldapSyncNow)).Methods("POST")
+ BaseRoutes.Admin.Handle("/ldap_test", ApiAdminSystemRequired(ldapTest)).Methods("POST")
BaseRoutes.Admin.Handle("/saml_metadata", ApiAppHandler(samlMetadata)).Methods("GET")
BaseRoutes.Admin.Handle("/add_certificate", ApiAdminSystemRequired(addCertificate)).Methods("POST")
BaseRoutes.Admin.Handle("/remove_certificate", ApiAdminSystemRequired(removeCertificate)).Methods("POST")
@@ -643,7 +644,7 @@ func ldapSyncNow(c *Context, w http.ResponseWriter, r *http.Request) {
if ldapI := einterfaces.GetLdapInterface(); ldapI != nil {
ldapI.SyncNow()
} else {
- l4g.Error("%v", model.NewLocAppError("saveComplianceReport", "ent.compliance.licence_disable.app_error", nil, "").Error())
+ l4g.Error("%v", model.NewLocAppError("ldapSyncNow", "ent.ldap.disabled.app_error", nil, "").Error())
}
}
}()
@@ -653,6 +654,24 @@ func ldapSyncNow(c *Context, w http.ResponseWriter, r *http.Request) {
w.Write([]byte(model.MapToJson(rdata)))
}
+func ldapTest(c *Context, w http.ResponseWriter, r *http.Request) {
+ if ldapI := einterfaces.GetLdapInterface(); ldapI != nil && utils.IsLicensed && *utils.License.Features.LDAP && *utils.Cfg.LdapSettings.Enable {
+ if err := ldapI.RunTest(); err != nil {
+ c.Err = err
+ c.Err.StatusCode = 500
+ }
+ } else {
+ c.Err = model.NewLocAppError("ldapTest", "ent.ldap.disabled.app_error", nil, "")
+ c.Err.StatusCode = http.StatusNotImplemented
+ }
+
+ if c.Err == nil {
+ rdata := map[string]string{}
+ rdata["status"] = "ok"
+ w.Write([]byte(model.MapToJson(rdata)))
+ }
+}
+
func samlMetadata(c *Context, w http.ResponseWriter, r *http.Request) {
samlInterface := einterfaces.GetSamlInterface()
diff --git a/api/admin_test.go b/api/admin_test.go
index 967e3ceb3f..3d8a956769 100644
--- a/api/admin_test.go
+++ b/api/admin_test.go
@@ -161,6 +161,18 @@ func TestEmailTest(t *testing.T) {
}
}
+func TestLdapTest(t *testing.T) {
+ th := Setup().InitBasic().InitSystemAdmin()
+
+ if _, err := th.BasicClient.TestLdap(utils.Cfg); err == nil {
+ t.Fatal("Shouldn't have permissions")
+ }
+
+ if _, err := th.SystemAdminClient.TestLdap(utils.Cfg); err == nil {
+ t.Fatal("should have errored")
+ }
+}
+
func TestGetTeamAnalyticsStandard(t *testing.T) {
th := Setup().InitBasic().InitSystemAdmin()
th.CreatePrivateChannel(th.BasicClient, th.BasicTeam)
diff --git a/einterfaces/ldap.go b/einterfaces/ldap.go
index fb14a8f023..f50ea277e9 100644
--- a/einterfaces/ldap.go
+++ b/einterfaces/ldap.go
@@ -16,6 +16,7 @@ type LdapInterface interface {
Syncronize() *model.AppError
StartLdapSyncJob()
SyncNow()
+ RunTest() *model.AppError
GetAllLdapUsers() ([]*model.User, *model.AppError)
}
diff --git a/i18n/en.json b/i18n/en.json
index cdb05f14bd..50e7525f13 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -2503,6 +2503,10 @@
"id": "ent.ldap.validate_filter.app_error",
"translation": "Invalid LDAP Filter"
},
+ {
+ "id": "ent.ldap.disabled.app_error",
+ "translation": "LDAP disabled or licence does not support LDAP."
+ },
{
"id": "ent.mfa.activate.authenticate.app_error",
"translation": "Error attempting to authenticate MFA token"
diff --git a/model/client.go b/model/client.go
index e2e003fe88..f43e5ad799 100644
--- a/model/client.go
+++ b/model/client.go
@@ -887,6 +887,19 @@ func (c *Client) TestEmail(config *Config) (*Result, *AppError) {
}
}
+// TestLdap will run a connection test on the current LDAP settings.
+// It will return the standard OK response if settings work. Otherwise
+// it will return an appropriate error.
+func (c *Client) TestLdap(config *Config) (*Result, *AppError) {
+ if r, err := c.DoApiPost("/admin/ldap_test", config.ToJson()); err != nil {
+ return nil, err
+ } else {
+ defer closeBody(r)
+ return &Result{r.Header.Get(HEADER_REQUEST_ID),
+ r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
+ }
+}
+
func (c *Client) GetComplianceReports() (*Result, *AppError) {
if r, err := c.DoApiGet("/admin/compliance_reports", "", ""); err != nil {
return nil, err
diff --git a/webapp/client/client.jsx b/webapp/client/client.jsx
index b842d99393..a059bb38ae 100644
--- a/webapp/client/client.jsx
+++ b/webapp/client/client.jsx
@@ -471,6 +471,15 @@ export default class Client {
end(this.handleResponse.bind(this, 'ldapSyncNow', success, error));
}
+ ldapTest(success, error) {
+ request.
+ post(`${this.getAdminRoute()}/ldap_test`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'ldap_test', success, error));
+ }
+
// Team Routes Section
createTeamFromSignup(teamSignup, success, error) {
diff --git a/webapp/components/admin_console/admin_settings.jsx b/webapp/components/admin_console/admin_settings.jsx
index 8601722eb3..9975a3975a 100644
--- a/webapp/components/admin_console/admin_settings.jsx
+++ b/webapp/components/admin_console/admin_settings.jsx
@@ -21,6 +21,7 @@ export default class AdminSettings extends React.Component {
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
+ this.doSubmit = this.doSubmit.bind(this);
this.state = Object.assign(this.getStateFromConfig(props.config), {
saveNeeded: false,
@@ -39,6 +40,10 @@ export default class AdminSettings extends React.Component {
handleSubmit(e) {
e.preventDefault();
+ this.doSubmit();
+ }
+
+ doSubmit(callback) {
this.setState({
saving: true,
serverError: null
@@ -59,12 +64,20 @@ export default class AdminSettings extends React.Component {
saveNeeded: false,
saving: false
});
+
+ if (callback) {
+ callback();
+ }
},
(err) => {
this.setState({
saving: false,
serverError: err.message
});
+
+ if (callback) {
+ callback();
+ }
}
);
}
diff --git a/webapp/components/admin_console/admin_sidebar.jsx b/webapp/components/admin_console/admin_sidebar.jsx
index 4fcfe2731f..0b107e19a4 100644
--- a/webapp/components/admin_console/admin_sidebar.jsx
+++ b/webapp/components/admin_console/admin_sidebar.jsx
@@ -207,7 +207,7 @@ export default class AdminSidebar extends React.Component {
title={