summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristopher Speller <crspeller@gmail.com>2016-07-26 17:39:51 -0400
committerJoram Wilander <jwawilander@gmail.com>2016-07-26 17:39:51 -0400
commitf5375254f90053bd9b688d36f758aca309ec3735 (patch)
tree45ec76d772286305269c5006d67be267e63bbb41
parent528890dba01d6835754c78bf7695621c828b6838 (diff)
downloadchat-f5375254f90053bd9b688d36f758aca309ec3735.tar.gz
chat-f5375254f90053bd9b688d36f758aca309ec3735.tar.bz2
chat-f5375254f90053bd9b688d36f758aca309ec3735.zip
Adding migration support to LDAP from other account types (#3655)
-rw-r--r--Makefile5
-rw-r--r--api/admin.go6
-rw-r--r--einterfaces/account_migration.go20
-rw-r--r--einterfaces/ldap.go2
-rw-r--r--i18n/en.json8
-rw-r--r--mattermost.go53
-rw-r--r--model/job.go6
-rw-r--r--model/job_test.go60
8 files changed, 154 insertions, 6 deletions
diff --git a/Makefile b/Makefile
index 766cb6dd7..3ece21004 100644
--- a/Makefile
+++ b/Makefile
@@ -182,16 +182,19 @@ ifeq ($(BUILD_ENTERPRISE_READY),true)
$(GO) test $(GOFLAGS) -run=$(TESTS) -covermode=count -c ./enterprise/compliance && ./compliance.test -test.v -test.timeout=120s -test.coverprofile=ccompliance.out || exit 1
$(GO) test $(GOFLAGS) -run=$(TESTS) -covermode=count -c ./enterprise/emoji && ./emoji.test -test.v -test.timeout=120s -test.coverprofile=cemoji.out || exit 1
$(GO) test $(GOFLAGS) -run=$(TESTS) -covermode=count -c ./enterprise/saml && ./saml.test -test.v -test.timeout=60s -test.coverprofile=csaml.out || exit 1
+ $(GO) test $(GOFLAGS) -run=$(TESTS) -covermode=count -c ./enterprise/account_migration && ./account_migration.test -test.v -test.timeout=60s -test.coverprofile=caccount_migration.out || exit 1
tail -n +2 cldap.out >> ecover.out
tail -n +2 ccompliance.out >> ecover.out
tail -n +2 cemoji.out >> ecover.out
tail -n +2 csaml.out >> ecover.out
- rm -f cldap.out ccompliance.out cemoji.out csaml.out
+ tail -n +2 caccount_migration.out >> ecover.out
+ rm -f cldap.out ccompliance.out cemoji.out csaml.out caccount_migration.out
rm -r ldap.test
rm -r compliance.test
rm -r emoji.test
rm -r saml.test
+ rm -r account_migration.test
rm -f config/*.crt
rm -f config/*.key
endif
diff --git a/api/admin.go b/api/admin.go
index 2771e5491..bd3955195 100644
--- a/api/admin.go
+++ b/api/admin.go
@@ -572,11 +572,7 @@ func ldapSyncNow(c *Context, w http.ResponseWriter, r *http.Request) {
go func() {
if utils.IsLicensed && *utils.License.Features.LDAP && *utils.Cfg.LdapSettings.Enable {
if ldapI := einterfaces.GetLdapInterface(); ldapI != nil {
- if err := ldapI.Syncronize(); err != nil {
- l4g.Error("%v", err.Error())
- } else {
- l4g.Info(utils.T("ent.ldap.syncdone.info"))
- }
+ ldapI.SyncNow()
} else {
l4g.Error("%v", model.NewLocAppError("saveComplianceReport", "ent.compliance.licence_disable.app_error", nil, "").Error())
}
diff --git a/einterfaces/account_migration.go b/einterfaces/account_migration.go
new file mode 100644
index 000000000..4824de6d5
--- /dev/null
+++ b/einterfaces/account_migration.go
@@ -0,0 +1,20 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package einterfaces
+
+import "github.com/mattermost/platform/model"
+
+type AccountMigrationInterface interface {
+ MigrateToLdap(fromAuthService string, forignUserFieldNameToMatch string) *model.AppError
+}
+
+var theAccountMigrationInterface AccountMigrationInterface
+
+func RegisterAccountMigrationInterface(newInterface AccountMigrationInterface) {
+ theAccountMigrationInterface = newInterface
+}
+
+func GetAccountMigrationInterface() AccountMigrationInterface {
+ return theAccountMigrationInterface
+}
diff --git a/einterfaces/ldap.go b/einterfaces/ldap.go
index 4f1b56119..fb14a8f02 100644
--- a/einterfaces/ldap.go
+++ b/einterfaces/ldap.go
@@ -15,6 +15,8 @@ type LdapInterface interface {
ValidateFilter(filter string) *model.AppError
Syncronize() *model.AppError
StartLdapSyncJob()
+ SyncNow()
+ GetAllLdapUsers() ([]*model.User, *model.AppError)
}
var theLdapInterface LdapInterface
diff --git a/i18n/en.json b/i18n/en.json
index 7ddb99b3d..3937e5514 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -2100,6 +2100,14 @@
"translation": "Feature requires an enterprise license. Please contact your system administrator about upgrading your enterprise license."
},
{
+ "id": "ent.migration.migratetoldap.duplicate_field",
+ "translation": "Unable to migrate LDAP users with specified field. Duplicate entry detected. Please remove all duplcates and try again."
+ },
+ {
+ "id": "ent.migration.migratetoldap.user_not_found",
+ "translation": "Unable to find user on LDAP server: "
+ },
+ {
"id": "ent.brand.save_brand_image.decode.app_error",
"translation": "Unable to decode image."
},
diff --git a/mattermost.go b/mattermost.go
index f2047580b..68f7f26da 100644
--- a/mattermost.go
+++ b/mattermost.go
@@ -61,6 +61,7 @@ var flagCmdPermanentDeleteTeam bool
var flagCmdPermanentDeleteAllUsers bool
var flagCmdResetDatabase bool
var flagCmdRunLdapSync bool
+var flagCmdMigrateAccounts bool
var flagUsername string
var flagCmdUploadLicense bool
var flagConfigFile string
@@ -73,6 +74,9 @@ var flagSiteURL string
var flagConfirmBackup string
var flagRole string
var flagRunCmds bool
+var flagFromAuth string
+var flagToAuth string
+var flagMatchField string
func doLoadConfig(filename string) (err string) {
defer func() {
@@ -288,6 +292,9 @@ func parseCmds() {
flag.StringVar(&flagChannelName, "channel_name", "", "")
flag.StringVar(&flagSiteURL, "site_url", "", "")
flag.StringVar(&flagConfirmBackup, "confirm_backup", "", "")
+ flag.StringVar(&flagFromAuth, "from_auth", "", "")
+ flag.StringVar(&flagToAuth, "to_auth", "", "")
+ flag.StringVar(&flagMatchField, "match_field", "email", "")
flag.StringVar(&flagRole, "role", "", "")
flag.BoolVar(&flagCmdUpdateDb30, "upgrade_db_30", false, "")
@@ -311,6 +318,7 @@ func parseCmds() {
flag.BoolVar(&flagCmdPermanentDeleteAllUsers, "permanent_delete_all_users", false, "")
flag.BoolVar(&flagCmdResetDatabase, "reset_database", false, "")
flag.BoolVar(&flagCmdRunLdapSync, "ldap_sync", false, "")
+ flag.BoolVar(&flagCmdMigrateAccounts, "migrate_accounts", false, "")
flag.BoolVar(&flagCmdUploadLicense, "upload_license", false, "")
flag.Parse()
@@ -335,6 +343,7 @@ func parseCmds() {
flagCmdPermanentDeleteAllUsers ||
flagCmdResetDatabase ||
flagCmdRunLdapSync ||
+ flagCmdMigrateAccounts ||
flagCmdUploadLicense)
}
@@ -359,6 +368,7 @@ func runCmds() {
cmdResetDatabase()
cmdUploadLicense()
cmdRunLdapSync()
+ cmdRunMigrateAccounts()
}
type TeamForUpgrade struct {
@@ -1475,6 +1485,44 @@ func cmdRunLdapSync() {
}
}
+func cmdRunMigrateAccounts() {
+ if flagCmdMigrateAccounts {
+ if len(flagFromAuth) == 0 || (flagFromAuth != "email" && flagFromAuth != "gitlab" && flagFromAuth != "saml") {
+ fmt.Fprintln(os.Stderr, "flag needs an argument: -from_auth")
+ flag.Usage()
+ os.Exit(1)
+ }
+
+ if len(flagToAuth) == 0 || flagToAuth != "ldap" {
+ fmt.Fprintln(os.Stderr, "flag needs an argument: -from_auth")
+ flag.Usage()
+ os.Exit(1)
+ }
+
+ // Email auth in Mattermost system is represented by ""
+ if flagFromAuth == "email" {
+ flagFromAuth = ""
+ }
+
+ if len(flagMatchField) == 0 || (flagMatchField != "email" && flagMatchField != "username") {
+ fmt.Fprintln(os.Stderr, "flag needs an argument: -match_field")
+ flag.Usage()
+ os.Exit(1)
+ }
+
+ if migrate := einterfaces.GetAccountMigrationInterface(); migrate != nil {
+ if err := migrate.MigrateToLdap(flagFromAuth, flagMatchField); err != nil {
+ fmt.Println("ERROR: Account migration failed.")
+ l4g.Error("%v", err.Error())
+ flushLogAndExit(1)
+ } else {
+ fmt.Println("SUCCESS: Account migration complete.")
+ flushLogAndExit(0)
+ }
+ }
+ }
+}
+
func cmdUploadLicense() {
if flagCmdUploadLicense {
if model.BuildEnterpriseReady != "true" {
@@ -1651,6 +1699,11 @@ COMMANDS:
Example:
platform -upload_license -license="/path/to/license/example.mattermost-license"
+
+ -migrate_accounts Migrates accounts from one authentication provider to anouther. Requires -from_auth -to_auth and -match_field flags. Supported options for -from_auth: email, gitlab, saml. Supported options for -to_auth ldap. Supported options for -match_field email, username. Will display any accounts that are not migrated succesfully.
+
+ Example:
+ platform -migrate_accounts -from_auth email -to_auth ldap -match_field username
-upgrade_db_30 Upgrades the database from a version 2.x schema to version 3 see
http://www.mattermost.org/upgrading-to-mattermost-3-0/
diff --git a/model/job.go b/model/job.go
index b6c68dce4..229d5efd3 100644
--- a/model/job.go
+++ b/model/job.go
@@ -84,6 +84,12 @@ func (task *ScheduledTask) Cancel() {
removeTaskByName(task.Name)
}
+// Executes the task immediatly. A recurring task will be run regularally after interval.
+func (task *ScheduledTask) Execute() {
+ task.function()
+ task.timer.Reset(task.Interval)
+}
+
func (task *ScheduledTask) String() string {
return fmt.Sprintf(
"%s\nInterval: %s\nRecurring: %t\n",
diff --git a/model/job_test.go b/model/job_test.go
index 2a307de1e..8908fed58 100644
--- a/model/job_test.go
+++ b/model/job_test.go
@@ -126,3 +126,63 @@ func TestGetAllTasks(t *testing.T) {
}
}
}
+
+func TestExecuteTask(t *testing.T) {
+ TASK_NAME := "Test Task"
+ TASK_TIME := time.Second * 5
+
+ testValue := 0
+ testFunc := func() {
+ testValue += 1
+ }
+
+ task := CreateTask(TASK_NAME, testFunc, TASK_TIME)
+ if testValue != 0 {
+ t.Fatal("Unexpected execuition of task")
+ }
+
+ task.Execute()
+
+ if testValue != 1 {
+ t.Fatal("Task did not execute")
+ }
+
+ time.Sleep(TASK_TIME + time.Second)
+
+ if testValue != 2 {
+ t.Fatal("Task re-executed")
+ }
+}
+
+func TestExecuteTaskRecurring(t *testing.T) {
+ TASK_NAME := "Test Recurring Task"
+ TASK_TIME := time.Second * 5
+
+ testValue := 0
+ testFunc := func() {
+ testValue += 1
+ }
+
+ task := CreateRecurringTask(TASK_NAME, testFunc, TASK_TIME)
+ if testValue != 0 {
+ t.Fatal("Unexpected execuition of task")
+ }
+
+ time.Sleep(time.Second * 3)
+
+ task.Execute()
+ if testValue != 1 {
+ t.Fatal("Task did not execute")
+ }
+
+ time.Sleep(time.Second * 3)
+ if testValue != 1 {
+ t.Fatal("Task should not have executed before 5 seconds")
+ }
+
+ time.Sleep(time.Second * 3)
+
+ if testValue != 2 {
+ t.Fatal("Task did not re-execute after forced execution")
+ }
+}