From a602991be560c845abe40f1e74dd7c10e53f3948 Mon Sep 17 00:00:00 2001 From: Gurgy Date: Wed, 21 Mar 2018 22:54:09 +0100 Subject: [PATCH 01/64] First restructure for user support --- action.go | 15 ++++-- admin/scope.go | 2 + admin/service.go | 120 ++++++++++++++++++++++++++++++++++++++++++++++- cmd/main.go | 14 ++++-- json/service.go | 16 +++++++ ldap/service.go | 4 ++ services.go | 16 ++++--- user.go | 14 ++++++ 8 files changed, 185 insertions(+), 16 deletions(-) create mode 100644 user.go diff --git a/action.go b/action.go index a2cdaa1..39b542f 100644 --- a/action.go +++ b/action.go @@ -10,7 +10,12 @@ type GroupUpdate struct { After Group } -type Actions struct { +type UserUpdate struct { + Before User + After User +} + +type GroupActions struct { Updates []GroupUpdate Additions []Group Deletions []Group @@ -37,9 +42,9 @@ func printProgress(done int, total int) { // Commits a set of actions to a service. // Returns all actions performed and a error if not all actions could be performed for some reason. -func (actions Actions) Commit(service GroupUpdateService) (Actions, error) { +func (actions GroupActions) Commit(service UpdateService) (GroupActions, error) { - performedActions := Actions{} + performedActions := GroupActions{} if len(actions.Updates) > 0 { fmt.Println("Performing updates") @@ -89,8 +94,8 @@ func (actions Actions) Commit(service GroupUpdateService) (Actions, error) { // Determines actions required to make the "old" group list look as the "new" group list. // Returns a list with those actions. -func ActionsRequired(old []Group, new []Group) Actions { - requiredActions := Actions{} +func GroupActionsRequired(old []Group, new []Group) GroupActions { + requiredActions := GroupActions{} for _, newGroup := range new { diff --git a/admin/scope.go b/admin/scope.go index 847a4ec..6c4408f 100644 --- a/admin/scope.go +++ b/admin/scope.go @@ -5,5 +5,7 @@ import "google.golang.org/api/admin/directory/v1" func Scopes() []string { return []string{ admin.AdminDirectoryGroupScope, + admin.AdminDirectoryUserScope, } } +// https://www.googleapis.com/auth/admin.directory.group, https://www.googleapis.com/auth/admin.directory.user \ No newline at end of file diff --git a/admin/service.go b/admin/service.go index d1d073a..229e1d7 100644 --- a/admin/service.go +++ b/admin/service.go @@ -12,13 +12,14 @@ import ( "golang.org/x/oauth2/google" "time" "strings" + "google.golang.org/api/googleapi" ) type googleService struct { service *admin.Service } -func NewGoogleService(keyPath string, adminMail string) (goldapps.GroupUpdateService, error) { +func NewGoogleService(keyPath string, adminMail string) (goldapps.UpdateService, error) { jsonKey, err := ioutil.ReadFile(keyPath) if err != nil { @@ -279,3 +280,120 @@ func (s googleService) addAlias(groupEmail string, alias string) error { _, err := s.service.Groups.Aliases.Insert(groupEmail, &admin.Alias{Alias: alias}).Do() return err } + +func (s googleService) AddUser(user goldapps.User) error { + _,err := s.service.Users.Insert(&admin.User{ + Addresses: nil, + AgreedToTerms: false, + Aliases: nil, + ChangePasswordAtNextLogin: false, + CreationTime: "", + CustomSchemas: nil, + CustomerId: "", + DeletionTime: "", + Emails: nil, + Etag: "", + ExternalIds: nil, + Gender: nil, + HashFunction: "", + Id: "", + Ims: nil, + IncludeInGlobalAddressList: false, + IpWhitelisted: false, + IsAdmin: false, + IsDelegatedAdmin: false, + IsEnforcedIn2Sv: false, + IsEnrolledIn2Sv: false, + IsMailboxSetup: false, + Keywords: nil, + Kind: "", + Languages: nil, + LastLoginTime: "", + Locations: nil, + Name: &admin.UserName{ + FamilyName: "", + FullName: "", + GivenName: "", + ForceSendFields: nil, + NullFields: nil, + }, + NonEditableAliases: nil, + Notes: nil, + OrgUnitPath: "", + Organizations: nil, + Password: "", + Phones: nil, + PosixAccounts: nil, + PrimaryEmail: "", + Relations: nil, + SshPublicKeys: nil, + Suspended: false, + SuspensionReason: "", + ThumbnailPhotoEtag: "", + ThumbnailPhotoUrl: "", + Websites: nil, + ServerResponse: googleapi.ServerResponse{ + HTTPStatusCode: 0, + Header: nil, + }, + ForceSendFields: nil, + NullFields: nil, + }).Do() + return err +} + + + +func (s googleService) DeleteUser(goldapps.User) error { + panic("implement me") +} + +func (s googleService) UpdateUser(goldapps.UserUpdate) error { + panic("implement me") +} + +func (s googleService) GetUsers() ([]goldapps.User, error) { + users, err := s.getUsers("my_customer") + if err != nil { + return nil, err + } + + fmt.Println(users[1].Name.GivenName) + fmt.Println(users[1].Name.FamilyName) + fmt.Println(users[1].PrimaryEmail) + fmt.Println(users[1].Password) // does not work + fmt.Println(users[1].HashFunction) // does not work + externalId := users[1].ExternalIds.([]interface{}) + + for index ,id := range externalId { + for key, value := range id.(map[string]interface{}) { + fmt.Printf("(%d) %s: %s\n", index, key, value.(string)) + } + } + + return nil, err +} + +func (s googleService) getUsers(customer string) ([]admin.User, error) { + users, err := s.service.Users.List().Customer(customer).Do() + if err != nil { + return nil, err + } + + for users.NextPageToken != "" { + newUsers, err := s.service.Users.List().Customer(customer).PageToken(users.NextPageToken).Do() + if err != nil { + return nil, err + } + + users.Users = append(users.Users, newUsers.Users...) + users.NextPageToken = newUsers.NextPageToken + } + + result := make([]admin.User, len(users.Users)) + for i, user := range users.Users { + result[i] = *user + } + + return result, nil +} \ No newline at end of file diff --git a/cmd/main.go b/cmd/main.go index 52fee60..4443129 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -22,11 +22,16 @@ func init() { func main() { + /* fmt.Println("Setting up provider") provider := getProvider() + */ fmt.Println("Setting up consumer") consumer := getConsumer() + consumer.GetUsers() + + /* fmt.Println("Collecting groups from the provider...") providerGroups, err := provider.GetGroups() if err != nil { @@ -44,7 +49,7 @@ func main() { fmt.Printf("%d groups collected.\n", len(consumerGroups)) fmt.Println("Colculating difference between the consumer and provider.") - proposedChanges := goldapps.ActionsRequired(consumerGroups, providerGroups) + proposedChanges := goldapps.GroupActionsRequired(consumerGroups, providerGroups) changes := getChanges(proposedChanges) if flags.interactive { @@ -78,9 +83,10 @@ func main() { fmt.Printf("\t Performed %d out of %d Updates\n", len(performed.Updates), len(changes.Updates)) fmt.Printf("Error: %s", err.Error()) } + */ } -func getChanges(proposedChanges goldapps.Actions) goldapps.Actions { +func getChanges(proposedChanges goldapps.GroupActions) goldapps.GroupActions { if !flags.interactive && flags.noInteraction { fmt.Printf( "Automaticly accepting %d addition, %d deletions and %d updates\n", @@ -141,7 +147,7 @@ func getChanges(proposedChanges goldapps.Actions) goldapps.Actions { return proposedChanges } -func getConsumer() goldapps.GroupUpdateService { +func getConsumer() goldapps.UpdateService { var to string if flags.interactive { to = askString("Which consumer would you like to use, 'gapps' or '*.json?", "gapps") @@ -176,7 +182,7 @@ func getConsumer() goldapps.GroupUpdateService { } } -func getProvider() goldapps.GroupService { +func getProvider() goldapps.CollectionService { var from string if flags.interactive { from = askString("which provider would you like to use, 'ldap', 'gapps' or '*.json'?", "ldap") diff --git a/json/service.go b/json/service.go index 3943539..c9b21c9 100644 --- a/json/service.go +++ b/json/service.go @@ -11,6 +11,22 @@ type jsonService struct { path string } +func (s jsonService) DeleteUser(goldapps.User) error { + panic("implement me") +} + +func (s jsonService) UpdateUser(goldapps.UserUpdate) error { + panic("implement me") +} + +func (s jsonService) AddUser(goldapps.User) error { + panic("implement me") +} + +func (s jsonService) GetUsers() ([]goldapps.User, error) { + panic("implement me") +} + func NewJsonService(path string) (jsonService, error) { return jsonService{ path: path, diff --git a/ldap/service.go b/ldap/service.go index c930b79..bdb7157 100644 --- a/ldap/service.go +++ b/ldap/service.go @@ -16,6 +16,10 @@ type ServiceLDAP struct { CustomEntryConfigs []CustomEntryConfig } +func (s ServiceLDAP) GetUsers() ([]goldapps.User, error) { + panic("implement me") +} + type ServerConfig struct { Url string ServerName string diff --git a/services.go b/services.go index 5d0e264..d27d0eb 100644 --- a/services.go +++ b/services.go @@ -1,12 +1,16 @@ package goldapps -type GroupUpdateService interface { - DeleteGroup(group Group) error - UpdateGroup(groupUpdate GroupUpdate) error - AddGroup(group Group) error - GroupService +type UpdateService interface { + DeleteGroup(Group) error + UpdateGroup(GroupUpdate) error + AddGroup(Group) error + DeleteUser(User) error + UpdateUser(UserUpdate) error + AddUser(User) error + CollectionService } -type GroupService interface { +type CollectionService interface { GetGroups() ([]Group, error) + GetUsers() ([]User, error) } diff --git a/user.go b/user.go new file mode 100644 index 0000000..7102a00 --- /dev/null +++ b/user.go @@ -0,0 +1,14 @@ +package goldapps + +type User struct { + Cid string `json:"cid"` + FirstName string `json:"first_name"` + SecondName string `json:"second_name"` + Nick string `json:"nick"` + Mail string `json:"mail"` // Backup email? must be investigated +} + +func (user User) equals(other User) bool { + + return true +} From 542b4f4904ded68a45138c444affc8fcbe8c93fe Mon Sep 17 00:00:00 2001 From: Gurgy Date: Mon, 2 Apr 2018 20:17:25 +0200 Subject: [PATCH 02/64] Add domain logic for users --- action.go => group_action.go | 26 -------- user.go | 36 +++++++++-- user_action.go | 112 +++++++++++++++++++++++++++++++++++ util.go | 25 ++++++++ 4 files changed, 168 insertions(+), 31 deletions(-) rename action.go => group_action.go (85%) create mode 100644 user_action.go create mode 100644 util.go diff --git a/action.go b/group_action.go similarity index 85% rename from action.go rename to group_action.go index 39b542f..51e4580 100644 --- a/action.go +++ b/group_action.go @@ -1,7 +1,6 @@ package goldapps import ( - "bytes" "fmt" ) @@ -10,36 +9,12 @@ type GroupUpdate struct { After Group } -type UserUpdate struct { - Before User - After User -} - type GroupActions struct { Updates []GroupUpdate Additions []Group Deletions []Group } -func printProgress(done int, total int) { - p := (done * 100) / total - builder := bytes.Buffer{} - for i := 0; i < 100; i++ { - if i < p { - builder.WriteByte('=') - } else if i == p { - builder.WriteByte('>') - } else { - builder.WriteByte(' ') - } - - } - fmt.Printf("\rProgress: [%s] %d/%d", builder.String(), done, total) - if done == total { - fmt.Printf("\rDone\n") - } -} - // Commits a set of actions to a service. // Returns all actions performed and a error if not all actions could be performed for some reason. func (actions GroupActions) Commit(service UpdateService) (GroupActions, error) { @@ -74,7 +49,6 @@ func (actions GroupActions) Commit(service UpdateService) (GroupActions, error) printProgress(len(performedActions.Additions), len(actions.Additions)) } - if len(actions.Deletions) > 0 { fmt.Println("Performing deletions") } diff --git a/user.go b/user.go index 7102a00..84762aa 100644 --- a/user.go +++ b/user.go @@ -1,14 +1,40 @@ package goldapps +import "strings" + type User struct { - Cid string `json:"cid"` - FirstName string `json:"first_name"` - SecondName string `json:"second_name"` - Nick string `json:"nick"` - Mail string `json:"mail"` // Backup email? must be investigated + Cid string `json:"cid"` + FirstName string `json:"first_name"` + SecondName string `json:"second_name"` + Nick string `json:"nick"` // Must be sanitized + Mail string `json:"mail"` // Backup email? must be investigated + GdprEducation bool `json:"gdpr_education"` } func (user User) equals(other User) bool { + if strings.ToLower(user.Cid) != strings.ToLower(other.Cid) { + return false + } + + if user.FirstName != other.FirstName { + return false + } + + if user.SecondName != other.SecondName { + return false + } + + if user.Nick != other.Nick { + return false + } + + if strings.ToLower(user.Mail) != strings.ToLower(other.Mail) { + return false + } + + if user.GdprEducation != other.GdprEducation { + return false + } return true } diff --git a/user_action.go b/user_action.go new file mode 100644 index 0000000..5942c26 --- /dev/null +++ b/user_action.go @@ -0,0 +1,112 @@ +package goldapps + +import ( + "fmt" +) + +type UserUpdate struct { + Before User + After User +} + +type UserActions struct { + Updates []UserUpdate + Additions []User + Deletions []User +} + +// Commits a set of actions to a service. +// Returns all actions performed and a error if not all actions could be performed for some reason. +func (actions UserActions) Commit(service UpdateService) (UserActions, error) { + + performedActions := UserActions{} + + if len(actions.Updates) > 0 { + fmt.Println("Performing updates") + } + for _, update := range actions.Updates { + err := service.UpdateUser(update) + if err != nil { + fmt.Println() + return performedActions, err + } + + performedActions.Updates = append(performedActions.Updates, update) + printProgress(len(performedActions.Updates), len(actions.Updates)) + } + + if len(actions.Additions) > 0 { + fmt.Println("Performing additions") + } + for _, user := range actions.Additions { + err := service.AddUser(user) + if err != nil { + fmt.Println() + return performedActions, err + } + + performedActions.Additions = append(performedActions.Additions, user) + printProgress(len(performedActions.Additions), len(actions.Additions)) + } + + if len(actions.Deletions) > 0 { + fmt.Println("Performing deletions") + } + for _, user := range actions.Deletions { + err := service.DeleteUser(user) + if err != nil { + fmt.Println() + return performedActions, err + } + + performedActions.Deletions = append(performedActions.Deletions, user) + printProgress(len(performedActions.Deletions), len(actions.Deletions)) + } + + return performedActions, nil +} + +// Determines actions required to make the "old" user list look as the "new" user list. +// Returns a list with those actions. +func UserActionsRequired(old []User, new []User) UserActions { + requiredActions := UserActions{} + + for _, newUser := range new { + + exists := false + for _, oldUser := range old { + if newUser.Cid == oldUser.Cid { + exists = true + if !newUser.equals(oldUser) { // User exists but is modified + requiredActions.Updates = append(requiredActions.Updates, UserUpdate{ + Before: oldUser, + After: newUser, + }) + } + break + } + } + + if !exists { // User does not exist in old list + requiredActions.Additions = append(requiredActions.Additions, newUser) + } + } + + for _, oldUser := range old { + + exists := false + for _, newUser := range new { + if oldUser.Cid == newUser.Cid { + exists = true + break + } + } + + if !exists { // Old list has user but the new list doesn't + requiredActions.Deletions = append(requiredActions.Deletions, oldUser) + } + + } + + return requiredActions +} diff --git a/util.go b/util.go new file mode 100644 index 0000000..6423653 --- /dev/null +++ b/util.go @@ -0,0 +1,25 @@ +package goldapps + +import ( + "bytes" + "fmt" +) + +func printProgress(done int, total int) { + p := (done * 100) / total + builder := bytes.Buffer{} + for i := 0; i < 100; i++ { + if i < p { + builder.WriteByte('=') + } else if i == p { + builder.WriteByte('>') + } else { + builder.WriteByte(' ') + } + + } + fmt.Printf("\rProgress: [%s] %d/%d", builder.String(), done, total) + if done == total { + fmt.Printf("\rDone\n") + } +} From 85617b3bb6c58657657e0e9eec8eb3aba8c3e5f6 Mon Sep 17 00:00:00 2001 From: Gurgy Date: Mon, 2 Apr 2018 22:55:55 +0200 Subject: [PATCH 03/64] Add Json implementation for users --- json/data.go | 8 +++ json/service.go | 141 ++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 120 insertions(+), 29 deletions(-) create mode 100644 json/data.go diff --git a/json/data.go b/json/data.go new file mode 100644 index 0000000..c2caf6c --- /dev/null +++ b/json/data.go @@ -0,0 +1,8 @@ +package json + +import "github.com/cthit/goldapps" + +type data struct { + Groups []goldapps.Group `json:"groups"` + Users []goldapps.User `json:"users"` +} diff --git a/json/service.go b/json/service.go index c9b21c9..34d6489 100644 --- a/json/service.go +++ b/json/service.go @@ -1,99 +1,182 @@ package json import ( - "github.com/cthit/goldapps" "encoding/json" - "io/ioutil" "fmt" + "github.com/cthit/goldapps" + "io/ioutil" ) -type jsonService struct { +type Service struct { path string } -func (s jsonService) DeleteUser(goldapps.User) error { - panic("implement me") +func (s Service) DeleteUser(user goldapps.User) error { + groups, err := s.GetGroups() + if err != nil { + return err + } + users, err := s.GetUsers() + if err != nil { + return err + } + + for i, u := range users { + if u.Cid == user.Cid { + err = s.save(data{ + groups, + append(users[:i], users[i+1:]...), + }) + return err + } + } + return fmt.Errorf("user not found %v", user) } -func (s jsonService) UpdateUser(goldapps.UserUpdate) error { - panic("implement me") +func (s Service) UpdateUser(update goldapps.UserUpdate) error { + groups, err := s.GetGroups() + if err != nil { + return err + } + users, err := s.GetUsers() + if err != nil { + return err + } + + for i, u := range users { + if u.Cid == update.Before.Cid { + err = s.save(data{ + groups, + append(append(users[:i], update.After), users[i+1:]...), + }) + return err + } + } + return fmt.Errorf("user not found %v", update.Before) } -func (s jsonService) AddUser(goldapps.User) error { - panic("implement me") +func (s Service) AddUser(user goldapps.User) error { + groups, err := s.GetGroups() + if err != nil { + return err + } + users, err := s.GetUsers() + if err != nil { + return err + } + + users = append(users, user) + + err = s.save(data{ + groups, + users, + }) + return err } -func (s jsonService) GetUsers() ([]goldapps.User, error) { - panic("implement me") +func (s Service) GetUsers() ([]goldapps.User, error) { + + bytes, err := ioutil.ReadFile(s.path) + if err != nil { + return nil, err + } + + var data data + err = json.Unmarshal(bytes, &data) + if err != nil { + return nil, err + } + + return data.Users, nil } -func NewJsonService(path string) (jsonService, error) { - return jsonService{ +func NewJsonService(path string) (Service, error) { + return Service{ path: path, }, nil } -func (s jsonService) save(groups []goldapps.Group) error { - data, _ := json.Marshal(groups) +func (s Service) save(data data) error { + json, _ := json.Marshal(data) - err := ioutil.WriteFile(s.path, data, 0666) + err := ioutil.WriteFile(s.path, json, 0666) return err } -func (s jsonService) DeleteGroup(group goldapps.Group) error { +func (s Service) DeleteGroup(group goldapps.Group) error { groups, err := s.GetGroups() if err != nil { return err } + users, err := s.GetUsers() + if err != nil { + return err + } - for i,g := range groups { - if g.Email == group.Email{ - err = s.save(append(groups[:i], groups[i+1:]...)) + for i, g := range groups { + if g.Email == group.Email { + err = s.save(data{append(groups[:i], groups[i+1:]...), + users, + }) return err } } return fmt.Errorf("group not found %v", group) } -func (s jsonService) UpdateGroup(groupUpdate goldapps.GroupUpdate) error { +func (s Service) UpdateGroup(groupUpdate goldapps.GroupUpdate) error { groups, err := s.GetGroups() if err != nil { return err } + users, err := s.GetUsers() + if err != nil { + return err + } - for i,g := range groups { - if g.Email == groupUpdate.Before.Email{ - err = s.save(append(append(groups[:i], groupUpdate.After), groups[i+1:]...)) + for i, g := range groups { + if g.Email == groupUpdate.Before.Email { + err = s.save(data{ + append(append(groups[:i], groupUpdate.After), groups[i+1:]...), + users, + }) return err } } return fmt.Errorf("group not found %v", groupUpdate.Before) } -func (s jsonService) AddGroup(group goldapps.Group) error { +func (s Service) AddGroup(group goldapps.Group) error { groups, err := s.GetGroups() if err != nil { return err } + users, err := s.GetUsers() + if err != nil { + return err + } groups = append(groups, group) - err = s.save(groups) + err = s.save(data{ + groups, + users, + }) return err } -func (s jsonService) GetGroups() ([]goldapps.Group, error) { +func (s Service) GetGroups() ([]goldapps.Group, error) { bytes, err := ioutil.ReadFile(s.path) if err != nil { return nil, err } - var data []goldapps.Group + var data data err = json.Unmarshal(bytes, &data) if err != nil { return nil, err } - return data, nil + return data.Groups, nil } From a1ed341b6e0764863e6027cd011de65296ab3eea Mon Sep 17 00:00:00 2001 From: Gurgy Date: Mon, 2 Apr 2018 23:20:31 +0200 Subject: [PATCH 04/64] Add user support in application head (cmd) --- cmd/main.go | 140 ++++++++++++++++++++++++++++++++++++++---------- group_action.go | 6 +-- user_action.go | 6 +-- 3 files changed, 119 insertions(+), 33 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index 4443129..35901ba 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -22,16 +22,12 @@ func init() { func main() { - /* fmt.Println("Setting up provider") provider := getProvider() - */ + fmt.Println("Setting up consumer") consumer := getConsumer() - consumer.GetUsers() - - /* fmt.Println("Collecting groups from the provider...") providerGroups, err := provider.GetGroups() if err != nil { @@ -40,6 +36,14 @@ func main() { } fmt.Printf("%d groups collected.\n", len(providerGroups)) + fmt.Println("Collecting users from the provider...") + providerUsers, err := provider.GetUsers() + if err != nil { + fmt.Println("Failed to collect users from provider") + panic(err) + } + fmt.Printf("%d users collected.\n", len(providerUsers)) + fmt.Println("Collecting groups from the consumer...") consumerGroups, err := consumer.GetGroups() if err != nil { @@ -48,17 +52,32 @@ func main() { } fmt.Printf("%d groups collected.\n", len(consumerGroups)) - fmt.Println("Colculating difference between the consumer and provider.") - proposedChanges := goldapps.GroupActionsRequired(consumerGroups, providerGroups) - changes := getChanges(proposedChanges) + fmt.Println("Collecting users from the consumer...") + consumerUsers, err := consumer.GetUsers() + if err != nil { + fmt.Println("Failed to collect users from consumer") + panic(err) + } + fmt.Printf("%d users collected.\n", len(consumerUsers)) + + fmt.Println("Colculating difference between the consumer and provider groups.") + proposedGroupChanges := goldapps.GroupActionsRequired(consumerGroups, providerGroups) + groupChanges := getGroupChanges(proposedGroupChanges) + + fmt.Println("Colculating difference between the consumer and provider users.") + proposedUserChanges := goldapps.UserActionsRequired(consumerUsers, providerUsers) + userChanges := getUserChanges(proposedUserChanges) if flags.interactive { proceed := askBool( fmt.Sprintf( - "Are you sure you want to commit these additions(%d), deletions(%d) and updates(%d)?", - len(changes.Additions), - len(changes.Deletions), - len(changes.Updates), + "Are you sure you want to commit these (groups + users) additions(%d + %d), deletions(%d + %d) and updates(%d + %d)?", + len(groupChanges.Additions), + len(userChanges.Additions), + len(groupChanges.Deletions), + len(userChanges.Deletions), + len(groupChanges.Updates), + len(userChanges.Updates), ), true, ) @@ -72,37 +91,43 @@ func main() { return } - performed, err := changes.Commit(consumer) - if err == nil { + groupChangesPerformed, err1 := groupChanges.Commit(consumer) + userChangesPerformed, err2 := userChanges.Commit(consumer) + if err1 == nil && err2 == nil { fmt.Println("All actions performed!") return } else { fmt.Println("All actions could not be performed...") - fmt.Printf("\t Performed %d out of %d Additions\n", len(performed.Additions), len(changes.Additions)) - fmt.Printf("\t Performed %d out of %d Deletions\n", len(performed.Deletions), len(changes.Deletions)) - fmt.Printf("\t Performed %d out of %d Updates\n", len(performed.Updates), len(changes.Updates)) - fmt.Printf("Error: %s", err.Error()) + fmt.Printf("\t For groups:\n") + fmt.Printf("\t\t Performed %d out of %d Additions\n", len(groupChangesPerformed.Additions), len(groupChanges.Additions)) + fmt.Printf("\t\t Performed %d out of %d Deletions\n", len(groupChangesPerformed.Deletions), len(groupChanges.Deletions)) + fmt.Printf("\t\t Performed %d out of %d Updates\n", len(groupChangesPerformed.Updates), len(groupChanges.Updates)) + fmt.Printf("\t\t Error: %s", err1.Error()) + fmt.Printf("\t For users:\n") + fmt.Printf("\t\t Performed %d out of %d Additions\n", len(userChangesPerformed.Additions), len(userChanges.Additions)) + fmt.Printf("\t\t Performed %d out of %d Deletions\n", len(userChangesPerformed.Deletions), len(userChanges.Deletions)) + fmt.Printf("\t\t Performed %d out of %d Updates\n", len(userChangesPerformed.Updates), len(userChanges.Updates)) + fmt.Printf("\t\t Error: %s", err2.Error()) } - */ } -func getChanges(proposedChanges goldapps.GroupActions) goldapps.GroupActions { +func getGroupChanges(proposedChanges goldapps.GroupActions) goldapps.GroupActions { if !flags.interactive && flags.noInteraction { fmt.Printf( - "Automaticly accepting %d addition, %d deletions and %d updates\n", + "(Groups) Automaticly accepting %d addition, %d deletions and %d updates\n", len(proposedChanges.Additions), len(proposedChanges.Deletions), len(proposedChanges.Updates), ) } else { // Handle additions - fmt.Printf("Additions (%d):\n", len(proposedChanges.Additions)) + fmt.Printf("(Groups) Additions (%d):\n", len(proposedChanges.Additions)) if len(proposedChanges.Additions) > 0 { for _, group := range proposedChanges.Additions { fmt.Printf("\t%v\n", group) } add := askBool( - fmt.Sprintf("Do you want to commit those %d additions?", len(proposedChanges.Additions)), + fmt.Sprintf("(Groups) Do you want to commit those %d additions?", len(proposedChanges.Additions)), true, ) if !add { @@ -111,13 +136,74 @@ func getChanges(proposedChanges goldapps.GroupActions) goldapps.GroupActions { } // Handle Deletions - fmt.Printf("Deletions (%d):\n", len(proposedChanges.Deletions)) + fmt.Printf("(Groups) Deletions (%d):\n", len(proposedChanges.Deletions)) if len(proposedChanges.Deletions) > 0 { for _, group := range proposedChanges.Deletions { fmt.Printf("\t%v\n", group) } add := askBool( - fmt.Sprintf("Do you want to commit those %d deletions?", len(proposedChanges.Deletions)), + fmt.Sprintf("(Groups) Do you want to commit those %d deletions?", len(proposedChanges.Deletions)), + true, + ) + if !add { + proposedChanges.Deletions = nil + } + } + + // Handle changes + fmt.Printf("(Groups) Changes (%d):\n", len(proposedChanges.Updates)) + if len(proposedChanges.Updates) > 0 { + for _, update := range proposedChanges.Updates { + fmt.Printf("\tUpdate:\n") + fmt.Printf("\t\tFrom:\n") + fmt.Printf("\t\t\t%v\n", update.Before) + fmt.Printf("\t\tTo:\n") + fmt.Printf("\t\t\t%v\n", update.After) + } + add := askBool( + fmt.Sprintf("(Groups) Do you want to commit those %d updates?", len(proposedChanges.Updates)), + true, + ) + if !add { + proposedChanges.Updates = nil + } + } + } + return proposedChanges +} + +func getUserChanges(proposedChanges goldapps.UserActions) goldapps.UserActions { + if !flags.interactive && flags.noInteraction { + fmt.Printf( + "(Users) Automaticly accepting %d addition, %d deletions and %d updates\n", + len(proposedChanges.Additions), + len(proposedChanges.Deletions), + len(proposedChanges.Updates), + ) + } else { + // Handle additions + fmt.Printf("(Users) Additions (%d):\n", len(proposedChanges.Additions)) + if len(proposedChanges.Additions) > 0 { + for _, user := range proposedChanges.Additions { + fmt.Printf("\t%v\n", user) + } + add := askBool( + fmt.Sprintf("(Users) Do you want to commit those %d additions?", len(proposedChanges.Additions)), + true, + ) + if !add { + proposedChanges.Additions = nil + } + } + + // Handle Deletions + fmt.Printf("(Users) Deletions (%d):\n", len(proposedChanges.Deletions)) + if len(proposedChanges.Deletions) > 0 { + for _, user := range proposedChanges.Deletions { + fmt.Printf("\t%v\n", user) + } + add := askBool( + fmt.Sprintf("(Users) Do you want to commit those %d deletions?", len(proposedChanges.Deletions)), true, ) if !add { @@ -126,7 +212,7 @@ func getChanges(proposedChanges goldapps.GroupActions) goldapps.GroupActions { } // Handle changes - fmt.Printf("Changes (%d):\n", len(proposedChanges.Updates)) + fmt.Printf("(Users) Changes (%d):\n", len(proposedChanges.Updates)) if len(proposedChanges.Updates) > 0 { for _, update := range proposedChanges.Updates { fmt.Printf("\tUpdate:\n") @@ -136,7 +222,7 @@ func getChanges(proposedChanges goldapps.GroupActions) goldapps.GroupActions { fmt.Printf("\t\t\t%v\n", update.After) } add := askBool( - fmt.Sprintf("Do you want to commit those %d updates?", len(proposedChanges.Updates)), + fmt.Sprintf("(Users) Do you want to commit those %d updates?", len(proposedChanges.Updates)), true, ) if !add { diff --git a/group_action.go b/group_action.go index 51e4580..04bf739 100644 --- a/group_action.go +++ b/group_action.go @@ -22,7 +22,7 @@ func (actions GroupActions) Commit(service UpdateService) (GroupActions, error) performedActions := GroupActions{} if len(actions.Updates) > 0 { - fmt.Println("Performing updates") + fmt.Println("(Groups) Performing updates") } for _, update := range actions.Updates { err := service.UpdateGroup(update) @@ -36,7 +36,7 @@ func (actions GroupActions) Commit(service UpdateService) (GroupActions, error) } if len(actions.Additions) > 0 { - fmt.Println("Performing additions") + fmt.Println("(Groups) Performing additions") } for _, group := range actions.Additions { err := service.AddGroup(group) @@ -50,7 +50,7 @@ func (actions GroupActions) Commit(service UpdateService) (GroupActions, error) } if len(actions.Deletions) > 0 { - fmt.Println("Performing deletions") + fmt.Println("(Groups) Performing deletions") } for _, group := range actions.Deletions { err := service.DeleteGroup(group) diff --git a/user_action.go b/user_action.go index 5942c26..6ffd6dd 100644 --- a/user_action.go +++ b/user_action.go @@ -22,7 +22,7 @@ func (actions UserActions) Commit(service UpdateService) (UserActions, error) { performedActions := UserActions{} if len(actions.Updates) > 0 { - fmt.Println("Performing updates") + fmt.Println("(Users) Performing updates") } for _, update := range actions.Updates { err := service.UpdateUser(update) @@ -36,7 +36,7 @@ func (actions UserActions) Commit(service UpdateService) (UserActions, error) { } if len(actions.Additions) > 0 { - fmt.Println("Performing additions") + fmt.Println("(Users) Performing additions") } for _, user := range actions.Additions { err := service.AddUser(user) @@ -50,7 +50,7 @@ func (actions UserActions) Commit(service UpdateService) (UserActions, error) { } if len(actions.Deletions) > 0 { - fmt.Println("Performing deletions") + fmt.Println("(Users) Performing deletions") } for _, user := range actions.Deletions { err := service.DeleteUser(user) From 13c768d645d9c761b5d575b5e38768076462df62 Mon Sep 17 00:00:00 2001 From: Gurgy Date: Wed, 4 Apr 2018 22:44:25 +0200 Subject: [PATCH 05/64] Add gapps implementation for users --- admin/service.go | 173 +++++++++++++++++++++-------------------------- 1 file changed, 77 insertions(+), 96 deletions(-) diff --git a/admin/service.go b/admin/service.go index 229e1d7..593c56f 100644 --- a/admin/service.go +++ b/admin/service.go @@ -1,22 +1,22 @@ package admin import ( + "github.com/cthit/goldapps" + "google.golang.org/api/admin/directory/v1" // Imports as admin - "io/ioutil" + "golang.org/x/net/context" + "golang.org/x/oauth2/google" "bytes" "fmt" - "github.com/cthit/goldapps" - "golang.org/x/net/context" - "golang.org/x/oauth2/google" - "time" + "io/ioutil" "strings" - "google.golang.org/api/googleapi" + "time" ) type googleService struct { - service *admin.Service + google *admin.Service } func NewGoogleService(keyPath string, adminMail string) (goldapps.UpdateService, error) { @@ -44,7 +44,7 @@ func NewGoogleService(keyPath string, adminMail string) (goldapps.UpdateService, } gs := googleService{ - service: service, + google: service, } return gs, nil @@ -71,7 +71,7 @@ func (s googleService) UpdateGroup(groupUpdate goldapps.GroupUpdate) error { if !exists { err := s.addMember(groupUpdate.Before.Email, member) if err != nil { - fmt.Printf("Failed to add menber %s\n",member) + fmt.Printf("Failed to add menber %s\n", member) return err } } @@ -141,7 +141,7 @@ func (s googleService) AddGroup(group goldapps.Group) error { return err } - time.Sleep(time.Second*10) + time.Sleep(time.Second * 10) // Add members for _, member := range group.Members { @@ -205,13 +205,13 @@ func (s googleService) GetGroups() ([]goldapps.Group, error) { } func (s googleService) getGroups(customer string) ([]admin.Group, error) { - groups, err := s.service.Groups.List().Customer(customer).Do() + groups, err := s.google.Groups.List().Customer(customer).Do() if err != nil { return nil, err } for groups.NextPageToken != "" { - newGroups, err := s.service.Groups.List().Customer(customer).PageToken(groups.NextPageToken).Do() + newGroups, err := s.google.Groups.List().Customer(customer).PageToken(groups.NextPageToken).Do() if err != nil { return nil, err } @@ -229,7 +229,7 @@ func (s googleService) getGroups(customer string) ([]admin.Group, error) { } func (s googleService) getMembers(email string) ([]string, error) { - members, err := s.service.Members.List(email).Do() + members, err := s.google.Members.List(email).Do() if err != nil { return nil, err } @@ -243,145 +243,126 @@ func (s googleService) getMembers(email string) ([]string, error) { } func (s googleService) getGroup(email string) (admin.Group, error) { - group, err := s.service.Groups.Get(email).Do() + group, err := s.google.Groups.Get(email).Do() return *group, err } func (s googleService) addGroup(group admin.Group) error { - _, err := s.service.Groups.Insert(&group).Do() + _, err := s.google.Groups.Insert(&group).Do() return err } func (s googleService) updateGroup(group admin.Group) error { - _, err := s.service.Groups.Update(group.Email, &group).Do() + _, err := s.google.Groups.Update(group.Email, &group).Do() return err } func (s googleService) deleteGroup(email string) error { - err := s.service.Groups.Delete(email).Do() + err := s.google.Groups.Delete(email).Do() return err } func (s googleService) deleteMember(groupEmail string, member string) error { - return s.service.Members.Delete(groupEmail, member).Do() + return s.google.Members.Delete(groupEmail, member).Do() } func (s googleService) addMember(groupEmail string, memberEmail string) error { - _, err := s.service.Members.Insert(groupEmail, &admin.Member{Email: memberEmail}).Do() + _, err := s.google.Members.Insert(groupEmail, &admin.Member{Email: memberEmail}).Do() return err } func (s googleService) deleteAlias(groupEmail string, alias string) error { - return s.service.Groups.Aliases.Delete(groupEmail, alias).Do() + return s.google.Groups.Aliases.Delete(groupEmail, alias).Do() } func (s googleService) addAlias(groupEmail string, alias string) error { - _, err := s.service.Groups.Aliases.Insert(groupEmail, &admin.Alias{Alias: alias}).Do() + _, err := s.google.Groups.Aliases.Insert(groupEmail, &admin.Alias{Alias: alias}).Do() return err } func (s googleService) AddUser(user goldapps.User) error { - _,err := s.service.Users.Insert(&admin.User{ - Addresses: nil, - AgreedToTerms: false, - Aliases: nil, - ChangePasswordAtNextLogin: false, - CreationTime: "", - CustomSchemas: nil, - CustomerId: "", - DeletionTime: "", - Emails: nil, - Etag: "", - ExternalIds: nil, - Gender: nil, - HashFunction: "", - Id: "", - Ims: nil, - IncludeInGlobalAddressList: false, - IpWhitelisted: false, - IsAdmin: false, - IsDelegatedAdmin: false, - IsEnforcedIn2Sv: false, - IsEnrolledIn2Sv: false, - IsMailboxSetup: false, - Keywords: nil, - Kind: "", - Languages: nil, - LastLoginTime: "", - Locations: nil, + _, err := s.google.Users.Insert(buildAdminUser(user)).Do() + return err +} + +func buildAdminUser(user goldapps.User) *admin.User { + return &admin.User{ Name: &admin.UserName{ - FamilyName: "", - FullName: "", - GivenName: "", - ForceSendFields: nil, - NullFields: nil, + FamilyName: user.SecondName, + GivenName: fmt.Sprintf("%s / %s", user.Nick, user.FirstName), }, - NonEditableAliases: nil, - Notes: nil, - OrgUnitPath: "", - Organizations: nil, - Password: "", - Phones: nil, - PosixAccounts: nil, - PrimaryEmail: "", - Relations: nil, - SshPublicKeys: nil, - Suspended: false, - SuspensionReason: "", - ThumbnailPhotoEtag: "", - ThumbnailPhotoUrl: "", - Websites: nil, - ServerResponse: googleapi.ServerResponse{ - HTTPStatusCode: 0, - Header: nil, + IncludeInGlobalAddressList: true, + PrimaryEmail: fmt.Sprintf("%s@chalmers.it", user.Cid), + Emails: &[]admin.UserEmail{ + { + Address: fmt.Sprintf("%s@chalmers.it", user.Nick), + Primary: false, + Type: "other", + }, }, - ForceSendFields: nil, - NullFields: nil, - }).Do() - return err + Password: "RandomPassword", // Todo: how to do with passwords? + ChangePasswordAtNextLogin: true, + Suspended: !user.GdprEducation, + SuspensionReason: "You have not attended the GDPR education!", + } } - - -func (s googleService) DeleteUser(goldapps.User) error { - panic("implement me") +func (s googleService) DeleteUser(user goldapps.User) error { + err := s.google.Users.Delete(fmt.Sprintf("%s@chalmers.it", user.Cid)).Do() + return err } -func (s googleService) UpdateUser(goldapps.UserUpdate) error { - panic("implement me") +func (s googleService) UpdateUser(update goldapps.UserUpdate) error { + _, err := s.google.Users.Update( + fmt.Sprintf("%s@chalmers.it", update.Before.Cid), + buildAdminUser(update.After), + ).Do() + return err } func (s googleService) GetUsers() ([]goldapps.User, error) { - users, err := s.getUsers("my_customer") + adminUsers, err := s.getUsers("my_customer") if err != nil { return nil, err } + users := make([]goldapps.User, len(adminUsers)) + + for i, adminUser := range adminUsers { + // Separating nick and firstName from (Nick / FirstName) + givenName := strings.Split(adminUser.Name.GivenName, " / ") + nick := givenName[0] + firstName := "" + if len(givenName) >= 2 { + firstName = givenName[1] + } - fmt.Println(users[1].Name.GivenName) - fmt.Println(users[1].Name.FamilyName) - fmt.Println(users[1].PrimaryEmail) - fmt.Println(users[1].Password) // does not work - fmt.Println(users[1].HashFunction) // does not work - externalId := users[1].ExternalIds.([]interface{}) + // Extracting cid form (cid@example.ex) + cid := strings.Split(adminUser.PrimaryEmail, "@")[0] - for index ,id := range externalId { - for key, value := range id.(map[string]interface{}) { - fmt.Printf("(%d) %s: %s\n", index, key, value.(string)) + // Check suspension and suspension reason to determine GDPR status + gdpr := !(adminUser.Suspended && adminUser.SuspensionReason == "You have not attended the GDPR education!") + + users[i] = goldapps.User{ + Cid: cid, + FirstName: firstName, + SecondName: adminUser.Name.FamilyName, + Nick: nick, + GdprEducation: gdpr, } } - return nil, err + return users, err } func (s googleService) getUsers(customer string) ([]admin.User, error) { - users, err := s.service.Users.List().Customer(customer).Do() + users, err := s.google.Users.List().Customer(customer).Do() if err != nil { return nil, err } for users.NextPageToken != "" { - newUsers, err := s.service.Users.List().Customer(customer).PageToken(users.NextPageToken).Do() + newUsers, err := s.google.Users.List().Customer(customer).PageToken(users.NextPageToken).Do() if err != nil { return nil, err } @@ -396,4 +377,4 @@ func (s googleService) getUsers(customer string) ([]admin.User, error) { } return result, nil -} \ No newline at end of file +} From 27d9cd36ac78f4a65576c33dee976aa5c3d7143e Mon Sep 17 00:00:00 2001 From: Gurgy Date: Wed, 4 Apr 2018 23:23:26 +0200 Subject: [PATCH 06/64] Clean out unused methods from gapps service --- admin/service.go | 238 ++++++++++++++++++++++------------------------- 1 file changed, 112 insertions(+), 126 deletions(-) diff --git a/admin/service.go b/admin/service.go index 593c56f..79d51c5 100644 --- a/admin/service.go +++ b/admin/service.go @@ -15,8 +15,12 @@ import ( "time" ) +const gdprSuspensionText = "You have not attended the GDPR education!" + type googleService struct { google *admin.Service + admin string + domain string } func NewGoogleService(keyPath string, adminMail string) (goldapps.UpdateService, error) { @@ -43,15 +47,23 @@ func NewGoogleService(keyPath string, adminMail string) (goldapps.UpdateService, return nil, err } + // Extract account and mail + s := strings.Split(adminMail, "@") + admin := s[0] + domain := s[1] + gs := googleService{ google: service, + admin: admin, + domain: domain, } return gs, nil } func (s googleService) DeleteGroup(group goldapps.Group) error { - return s.deleteGroup(group.Email) + err := s.google.Groups.Delete(group.Email).Do() + return err } func (s googleService) UpdateGroup(groupUpdate goldapps.GroupUpdate) error { @@ -69,7 +81,7 @@ func (s googleService) UpdateGroup(groupUpdate goldapps.GroupUpdate) error { } } if !exists { - err := s.addMember(groupUpdate.Before.Email, member) + _, err := s.google.Members.Insert(groupUpdate.Before.Email, &admin.Member{Email: member}).Do() if err != nil { fmt.Printf("Failed to add menber %s\n", member) return err @@ -87,7 +99,7 @@ func (s googleService) UpdateGroup(groupUpdate goldapps.GroupUpdate) error { } } if !keep { - err := s.deleteMember(groupUpdate.Before.Email, existingMember) + err := s.google.Members.Delete(groupUpdate.Before.Email, existingMember).Do() if err != nil { return err } @@ -104,7 +116,7 @@ func (s googleService) UpdateGroup(groupUpdate goldapps.GroupUpdate) error { } } if !exists { - err := s.addAlias(groupUpdate.Before.Email, alias) + _, err := s.google.Groups.Aliases.Insert(groupUpdate.Before.Email, &admin.Alias{Alias: alias}).Do() if err != nil { return err } @@ -121,14 +133,15 @@ func (s googleService) UpdateGroup(groupUpdate goldapps.GroupUpdate) error { } } if !keep { - err := s.deleteAlias(groupUpdate.Before.Email, existingAlias) + err := s.google.Groups.Aliases.Delete(groupUpdate.Before.Email, existingAlias).Do() if err != nil { return err } } } - return s.updateGroup(newGroup) + _, err := s.google.Groups.Update(groupUpdate.Before.Email, &newGroup).Do() + return err } func (s googleService) AddGroup(group goldapps.Group) error { @@ -136,7 +149,7 @@ func (s googleService) AddGroup(group goldapps.Group) error { Email: group.Email, } - err := s.addGroup(newGroup) + _, err := s.google.Groups.Insert(&newGroup).Do() if err != nil { return err } @@ -145,7 +158,7 @@ func (s googleService) AddGroup(group goldapps.Group) error { // Add members for _, member := range group.Members { - err = s.addMember(group.Email, member) + _, err := s.google.Members.Insert(group.Email, &admin.Member{Email: member}).Do() if err != nil { return err } @@ -153,7 +166,7 @@ func (s googleService) AddGroup(group goldapps.Group) error { // Add Aliases for _, alias := range group.Aliases { - err = s.addAlias(group.Email, alias) + _, err := s.google.Groups.Aliases.Insert(group.Email, &admin.Alias{Alias: alias}).Do() if err != nil { return err } @@ -163,7 +176,7 @@ func (s googleService) AddGroup(group goldapps.Group) error { func (s googleService) GetGroups() ([]goldapps.Group, error) { - adminGroups, err := s.getGroups("my_customer") + adminGroups, err := s.getGoogleGroups("my_customer") if err != nil { return nil, err } @@ -187,7 +200,7 @@ func (s googleService) GetGroups() ([]goldapps.Group, error) { fmt.Printf("\rProgress: [%s] %d/%d", builder.String(), i+1, len(groups)) - members, err := s.getMembers(group.Email) + members, err := s.getGoogleGroupMembers(group.Email) if err != nil { return nil, err } @@ -204,7 +217,71 @@ func (s googleService) GetGroups() ([]goldapps.Group, error) { } -func (s googleService) getGroups(customer string) ([]admin.Group, error) { +func (s googleService) AddUser(user goldapps.User) error { + _, err := s.google.Users.Insert(buildGoldappsUser(user, s.domain)).Do() + return err +} + +func (s googleService) UpdateUser(update goldapps.UserUpdate) error { + _, err := s.google.Users.Update( + fmt.Sprintf("%s@%s", update.Before.Cid, s.domain), + buildGoldappsUser(update.After, s.domain), + ).Do() + return err +} + +func (s googleService) DeleteUser(user goldapps.User) error { + admin := fmt.Sprintf("%s@%s", s.admin, s.domain) + userId := fmt.Sprintf("%s@%s", user.Cid, s.domain) + if admin == userId { + fmt.Printf("Skipping andmin user: %s\n", admin) + } + + err := s.google.Users.Delete(userId).Do() + return err +} + +func (s googleService) GetUsers() ([]goldapps.User, error) { + adminUsers, err := s.getGoogleUsers("my_customer") + if err != nil { + return nil, err + } + users := make([]goldapps.User, len(adminUsers)-1) + + admin := fmt.Sprintf("%s@%s", s.admin, s.domain) + + i := 0 + for _, adminUser := range adminUsers { + if admin != adminUser.PrimaryEmail { // Don't list admin account + // Separating nick and firstName from (Nick / FirstName) + givenName := strings.Split(adminUser.Name.GivenName, " / ") + nick := givenName[0] + firstName := "" + if len(givenName) >= 2 { + firstName = givenName[1] + } + + // Extracting cid form (cid@example.ex) + cid := strings.Split(adminUser.PrimaryEmail, "@")[0] + + // Check suspension and suspension reason to determine GDPR status + gdpr := !(adminUser.Suspended && adminUser.SuspensionReason == gdprSuspensionText) + + users[i] = goldapps.User{ + Cid: cid, + FirstName: firstName, + SecondName: adminUser.Name.FamilyName, + Nick: nick, + GdprEducation: gdpr, + } + i++ + } + } + + return users, err +} + +func (s googleService) getGoogleGroups(customer string) ([]admin.Group, error) { groups, err := s.google.Groups.List().Customer(customer).Do() if err != nil { return nil, err @@ -228,7 +305,7 @@ func (s googleService) getGroups(customer string) ([]admin.Group, error) { return result, nil } -func (s googleService) getMembers(email string) ([]string, error) { +func (s googleService) getGoogleGroupMembers(email string) ([]string, error) { members, err := s.google.Members.List(email).Do() if err != nil { return nil, err @@ -242,61 +319,41 @@ func (s googleService) getMembers(email string) ([]string, error) { return result, nil } -func (s googleService) getGroup(email string) (admin.Group, error) { - group, err := s.google.Groups.Get(email).Do() - - return *group, err -} - -func (s googleService) addGroup(group admin.Group) error { - _, err := s.google.Groups.Insert(&group).Do() - return err -} - -func (s googleService) updateGroup(group admin.Group) error { - _, err := s.google.Groups.Update(group.Email, &group).Do() - return err -} - -func (s googleService) deleteGroup(email string) error { - err := s.google.Groups.Delete(email).Do() - return err -} - -func (s googleService) deleteMember(groupEmail string, member string) error { - return s.google.Members.Delete(groupEmail, member).Do() -} +func (s googleService) getGoogleUsers(customer string) ([]admin.User, error) { + users, err := s.google.Users.List().Customer(customer).Do() + if err != nil { + return nil, err + } -func (s googleService) addMember(groupEmail string, memberEmail string) error { - _, err := s.google.Members.Insert(groupEmail, &admin.Member{Email: memberEmail}).Do() - return err -} + for users.NextPageToken != "" { + newUsers, err := s.google.Users.List().Customer(customer).PageToken(users.NextPageToken).Do() + if err != nil { + return nil, err + } -func (s googleService) deleteAlias(groupEmail string, alias string) error { - return s.google.Groups.Aliases.Delete(groupEmail, alias).Do() -} + users.Users = append(users.Users, newUsers.Users...) + users.NextPageToken = newUsers.NextPageToken + } -func (s googleService) addAlias(groupEmail string, alias string) error { - _, err := s.google.Groups.Aliases.Insert(groupEmail, &admin.Alias{Alias: alias}).Do() - return err -} + result := make([]admin.User, len(users.Users)) + for i, user := range users.Users { + result[i] = *user + } -func (s googleService) AddUser(user goldapps.User) error { - _, err := s.google.Users.Insert(buildAdminUser(user)).Do() - return err + return result, nil } -func buildAdminUser(user goldapps.User) *admin.User { +func buildGoldappsUser(user goldapps.User, domain string) *admin.User { return &admin.User{ Name: &admin.UserName{ FamilyName: user.SecondName, GivenName: fmt.Sprintf("%s / %s", user.Nick, user.FirstName), }, IncludeInGlobalAddressList: true, - PrimaryEmail: fmt.Sprintf("%s@chalmers.it", user.Cid), + PrimaryEmail: fmt.Sprintf("%s@%s", user.Cid, domain), Emails: &[]admin.UserEmail{ { - Address: fmt.Sprintf("%s@chalmers.it", user.Nick), + Address: fmt.Sprintf("%s@%s", user.Nick, domain), Primary: false, Type: "other", }, @@ -304,77 +361,6 @@ func buildAdminUser(user goldapps.User) *admin.User { Password: "RandomPassword", // Todo: how to do with passwords? ChangePasswordAtNextLogin: true, Suspended: !user.GdprEducation, - SuspensionReason: "You have not attended the GDPR education!", - } -} - -func (s googleService) DeleteUser(user goldapps.User) error { - err := s.google.Users.Delete(fmt.Sprintf("%s@chalmers.it", user.Cid)).Do() - return err -} - -func (s googleService) UpdateUser(update goldapps.UserUpdate) error { - _, err := s.google.Users.Update( - fmt.Sprintf("%s@chalmers.it", update.Before.Cid), - buildAdminUser(update.After), - ).Do() - return err -} - -func (s googleService) GetUsers() ([]goldapps.User, error) { - adminUsers, err := s.getUsers("my_customer") - if err != nil { - return nil, err + SuspensionReason: gdprSuspensionText, } - users := make([]goldapps.User, len(adminUsers)) - - for i, adminUser := range adminUsers { - // Separating nick and firstName from (Nick / FirstName) - givenName := strings.Split(adminUser.Name.GivenName, " / ") - nick := givenName[0] - firstName := "" - if len(givenName) >= 2 { - firstName = givenName[1] - } - - // Extracting cid form (cid@example.ex) - cid := strings.Split(adminUser.PrimaryEmail, "@")[0] - - // Check suspension and suspension reason to determine GDPR status - gdpr := !(adminUser.Suspended && adminUser.SuspensionReason == "You have not attended the GDPR education!") - - users[i] = goldapps.User{ - Cid: cid, - FirstName: firstName, - SecondName: adminUser.Name.FamilyName, - Nick: nick, - GdprEducation: gdpr, - } - } - - return users, err -} - -func (s googleService) getUsers(customer string) ([]admin.User, error) { - users, err := s.google.Users.List().Customer(customer).Do() - if err != nil { - return nil, err - } - - for users.NextPageToken != "" { - newUsers, err := s.google.Users.List().Customer(customer).PageToken(users.NextPageToken).Do() - if err != nil { - return nil, err - } - - users.Users = append(users.Users, newUsers.Users...) - users.NextPageToken = newUsers.NextPageToken - } - - result := make([]admin.User, len(users.Users)) - for i, user := range users.Users { - result[i] = *user - } - - return result, nil } From ac74e28c45b528819aba1603986a7a793a6dc41b Mon Sep 17 00:00:00 2001 From: Gurgy Date: Wed, 4 Apr 2018 23:50:51 +0200 Subject: [PATCH 07/64] Remove mail from user struct --- user.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/user.go b/user.go index 84762aa..2332204 100644 --- a/user.go +++ b/user.go @@ -6,8 +6,7 @@ type User struct { Cid string `json:"cid"` FirstName string `json:"first_name"` SecondName string `json:"second_name"` - Nick string `json:"nick"` // Must be sanitized - Mail string `json:"mail"` // Backup email? must be investigated + Nick string `json:"nick"` GdprEducation bool `json:"gdpr_education"` } @@ -28,10 +27,6 @@ func (user User) equals(other User) bool { return false } - if strings.ToLower(user.Mail) != strings.ToLower(other.Mail) { - return false - } - if user.GdprEducation != other.GdprEducation { return false } From db9be4b29703bc595a464c3a9922401929dbbf74 Mon Sep 17 00:00:00 2001 From: Gurgy Date: Thu, 5 Apr 2018 10:25:27 +0200 Subject: [PATCH 08/64] Solve big with print for only one error while updating actions --- cmd/main.go | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index 35901ba..0c3e400 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -98,16 +98,20 @@ func main() { return } else { fmt.Println("All actions could not be performed...") - fmt.Printf("\t For groups:\n") - fmt.Printf("\t\t Performed %d out of %d Additions\n", len(groupChangesPerformed.Additions), len(groupChanges.Additions)) - fmt.Printf("\t\t Performed %d out of %d Deletions\n", len(groupChangesPerformed.Deletions), len(groupChanges.Deletions)) - fmt.Printf("\t\t Performed %d out of %d Updates\n", len(groupChangesPerformed.Updates), len(groupChanges.Updates)) - fmt.Printf("\t\t Error: %s", err1.Error()) - fmt.Printf("\t For users:\n") - fmt.Printf("\t\t Performed %d out of %d Additions\n", len(userChangesPerformed.Additions), len(userChanges.Additions)) - fmt.Printf("\t\t Performed %d out of %d Deletions\n", len(userChangesPerformed.Deletions), len(userChanges.Deletions)) - fmt.Printf("\t\t Performed %d out of %d Updates\n", len(userChangesPerformed.Updates), len(userChanges.Updates)) - fmt.Printf("\t\t Error: %s", err2.Error()) + if err1 != nil { + fmt.Printf("\t For groups:\n") + fmt.Printf("\t\t Performed %d out of %d Additions\n", len(groupChangesPerformed.Additions), len(groupChanges.Additions)) + fmt.Printf("\t\t Performed %d out of %d Deletions\n", len(groupChangesPerformed.Deletions), len(groupChanges.Deletions)) + fmt.Printf("\t\t Performed %d out of %d Updates\n", len(groupChangesPerformed.Updates), len(groupChanges.Updates)) + fmt.Printf("\t\t Error: %s", err1.Error()) + } + if err2 != nil { + fmt.Printf("\t For users:\n") + fmt.Printf("\t\t Performed %d out of %d Additions\n", len(userChangesPerformed.Additions), len(userChanges.Additions)) + fmt.Printf("\t\t Performed %d out of %d Deletions\n", len(userChangesPerformed.Deletions), len(userChanges.Deletions)) + fmt.Printf("\t\t Performed %d out of %d Updates\n", len(userChangesPerformed.Updates), len(userChanges.Updates)) + fmt.Printf("\t\t Error: %s", err2.Error()) + } } } From f976135511af125bcf17ca02c68d7adb19c0b450 Mon Sep 17 00:00:00 2001 From: Gurgy Date: Thu, 5 Apr 2018 10:52:16 +0200 Subject: [PATCH 09/64] Add support for only updating users or groups --- README.md | 3 ++ cmd/flags.go | 4 +++ cmd/main.go | 80 ++++++++++++++++++++++++++++++++-------------------- 3 files changed, 57 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 8e85c12..5cbe8b3 100644 --- a/README.md +++ b/README.md @@ -29,5 +29,8 @@ The following flags are available: * `-dry`: Makes sure the program does not change anything. * `-from someString`: Set the group source to `ldap`, `gapps` or `*.json`. In case of `gapps` config value `gapps.provider` will be used. * `-to someString`: Set the group consumer to 'gapps' or '*.json'. In case of `gapps` config value `gapps.consumer` will be used. +* `-users`: Only collect and sync users +* `-groups`: Only collect and sync groups + Notice that flags should be combined on the form `goldapps -a -b` and **NOT** on the form `goldapps -ab`. \ No newline at end of file diff --git a/cmd/flags.go b/cmd/flags.go index 043872e..230dc35 100644 --- a/cmd/flags.go +++ b/cmd/flags.go @@ -8,6 +8,8 @@ type flagStruct struct { interactive bool noInteraction bool dryRun bool + onlyGroups bool + onlyUsers bool } var flags = flagStruct{} @@ -18,5 +20,7 @@ func loadFlags() { flag.BoolVar(&flags.dryRun, "dry", false, "Setting this flag will cause the application to only print information and not update any groups") flag.BoolVar(&flags.noInteraction, "y", false, "Setting this flag will cause the application to not ask for any user confirmation") flag.BoolVar(&flags.interactive, "i", false, "Setting this flag will cause the application to ask the user for input in every stage ") + flag.BoolVar(&flags.onlyGroups, "groups", false, "Setting this flag will cause the application to only collect and update groups") + flag.BoolVar(&flags.onlyUsers, "users", false, "Setting this flag will cause the application to only collect and update users ") flag.Parse() } diff --git a/cmd/main.go b/cmd/main.go index 0c3e400..7143302 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -28,45 +28,65 @@ func main() { fmt.Println("Setting up consumer") consumer := getConsumer() - fmt.Println("Collecting groups from the provider...") - providerGroups, err := provider.GetGroups() - if err != nil { - fmt.Println("Failed to collect groups from provider") - panic(err) + var err error + + var providerGroups []goldapps.Group + if !flags.onlyUsers { + fmt.Println("Collecting groups from the provider...") + providerGroups, err = provider.GetGroups() + if err != nil { + fmt.Println("Failed to collect groups from provider") + panic(err) + } + fmt.Printf("%d groups collected.\n", len(providerGroups)) } - fmt.Printf("%d groups collected.\n", len(providerGroups)) - fmt.Println("Collecting users from the provider...") - providerUsers, err := provider.GetUsers() - if err != nil { - fmt.Println("Failed to collect users from provider") - panic(err) + var providerUsers []goldapps.User + if !flags.onlyGroups { + fmt.Println("Collecting users from the provider...") + providerUsers, err = provider.GetUsers() + if err != nil { + fmt.Println("Failed to collect users from provider") + panic(err) + } + fmt.Printf("%d users collected.\n", len(providerUsers)) } - fmt.Printf("%d users collected.\n", len(providerUsers)) - fmt.Println("Collecting groups from the consumer...") - consumerGroups, err := consumer.GetGroups() - if err != nil { - fmt.Println("Failed to collect groups from consumer") - panic(err) + var consumerGroups []goldapps.Group + if !flags.onlyUsers { + fmt.Println("Collecting groups from the consumer...") + consumerGroups, err = consumer.GetGroups() + if err != nil { + fmt.Println("Failed to collect groups from consumer") + panic(err) + } + fmt.Printf("%d groups collected.\n", len(consumerGroups)) } - fmt.Printf("%d groups collected.\n", len(consumerGroups)) - fmt.Println("Collecting users from the consumer...") - consumerUsers, err := consumer.GetUsers() - if err != nil { - fmt.Println("Failed to collect users from consumer") - panic(err) + var consumerUsers []goldapps.User + if !flags.onlyGroups { + fmt.Println("Collecting users from the consumer...") + consumerUsers, err = consumer.GetUsers() + if err != nil { + fmt.Println("Failed to collect users from consumer") + panic(err) + } + fmt.Printf("%d users collected.\n", len(consumerUsers)) } - fmt.Printf("%d users collected.\n", len(consumerUsers)) - fmt.Println("Colculating difference between the consumer and provider groups.") - proposedGroupChanges := goldapps.GroupActionsRequired(consumerGroups, providerGroups) - groupChanges := getGroupChanges(proposedGroupChanges) + groupChanges := goldapps.GroupActions{} + if !flags.onlyUsers { + fmt.Println("Colculating difference between the consumer and provider groups.") + proposedGroupChanges := goldapps.GroupActionsRequired(consumerGroups, providerGroups) + groupChanges = getGroupChanges(proposedGroupChanges) + } - fmt.Println("Colculating difference between the consumer and provider users.") - proposedUserChanges := goldapps.UserActionsRequired(consumerUsers, providerUsers) - userChanges := getUserChanges(proposedUserChanges) + userChanges := goldapps.UserActions{} + if !flags.onlyGroups { + fmt.Println("Colculating difference between the consumer and provider users.") + proposedUserChanges := goldapps.UserActionsRequired(consumerUsers, providerUsers) + userChanges = getUserChanges(proposedUserChanges) + } if flags.interactive { proceed := askBool( From e559df4ec7e165c0e4b29d2561b05c51a1a277f8 Mon Sep 17 00:00:00 2001 From: Gurgy Date: Thu, 5 Apr 2018 10:59:56 +0200 Subject: [PATCH 10/64] Add support for user aliases in gapps --- admin/service.go | 55 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 15 deletions(-) diff --git a/admin/service.go b/admin/service.go index 79d51c5..59245bf 100644 --- a/admin/service.go +++ b/admin/service.go @@ -17,6 +17,11 @@ import ( const gdprSuspensionText = "You have not attended the GDPR education!" +const googleDuplicateEntryError = "googleapi: Error 409: Entity already exists., duplicate" + +// my_customer seems to work... +const googleCustomer = "my_customer" + type googleService struct { google *admin.Service admin string @@ -176,7 +181,7 @@ func (s googleService) AddGroup(group goldapps.Group) error { func (s googleService) GetGroups() ([]goldapps.Group, error) { - adminGroups, err := s.getGoogleGroups("my_customer") + adminGroups, err := s.getGoogleGroups(googleCustomer) if err != nil { return nil, err } @@ -219,7 +224,15 @@ func (s googleService) GetGroups() ([]goldapps.Group, error) { func (s googleService) AddUser(user goldapps.User) error { _, err := s.google.Users.Insert(buildGoldappsUser(user, s.domain)).Do() - return err + if err != nil { + return err + } + + // Google needs time for the addition to propagate + time.Sleep(time.Second) + + // Add alias for nick@example.ex + return s.addUserAlias(fmt.Sprintf("%s@%s", user.Cid, s.domain), fmt.Sprintf("%s@%s", user.Nick, s.domain)) } func (s googleService) UpdateUser(update goldapps.UserUpdate) error { @@ -227,7 +240,12 @@ func (s googleService) UpdateUser(update goldapps.UserUpdate) error { fmt.Sprintf("%s@%s", update.Before.Cid, s.domain), buildGoldappsUser(update.After, s.domain), ).Do() - return err + if err != nil { + return err + } + + // Add alias for nick@example.ex + return s.addUserAlias(fmt.Sprintf("%s@%s", update.After.Cid, s.domain), fmt.Sprintf("%s@%s", update.After.Nick, s.domain)) } func (s googleService) DeleteUser(user goldapps.User) error { @@ -242,7 +260,7 @@ func (s googleService) DeleteUser(user goldapps.User) error { } func (s googleService) GetUsers() ([]goldapps.User, error) { - adminUsers, err := s.getGoogleUsers("my_customer") + adminUsers, err := s.getGoogleUsers(googleCustomer) if err != nil { return nil, err } @@ -343,6 +361,20 @@ func (s googleService) getGoogleUsers(customer string) ([]admin.User, error) { return result, nil } +func (s googleService) addUserAlias(userKey string, alias string) error { + _, err := s.google.Users.Aliases.Insert(userKey, &admin.Alias{ + Alias: alias, + }).Do() + if err != nil { + if err.Error() == googleDuplicateEntryError { + fmt.Printf("Warning: Could not add alias for %s. It already exists. \n", alias) + } else { + return err + } + } + return nil +} + func buildGoldappsUser(user goldapps.User, domain string) *admin.User { return &admin.User{ Name: &admin.UserName{ @@ -351,16 +383,9 @@ func buildGoldappsUser(user goldapps.User, domain string) *admin.User { }, IncludeInGlobalAddressList: true, PrimaryEmail: fmt.Sprintf("%s@%s", user.Cid, domain), - Emails: &[]admin.UserEmail{ - { - Address: fmt.Sprintf("%s@%s", user.Nick, domain), - Primary: false, - Type: "other", - }, - }, - Password: "RandomPassword", // Todo: how to do with passwords? - ChangePasswordAtNextLogin: true, - Suspended: !user.GdprEducation, - SuspensionReason: gdprSuspensionText, + Password: "RandomPassword", // Todo: how to do with passwords? + ChangePasswordAtNextLogin: true, + Suspended: !user.GdprEducation, + SuspensionReason: gdprSuspensionText, } } From dc532706d507d3ca1fc5279f3c5f29fac1855f9a Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Sat, 21 Apr 2018 17:36:32 +0200 Subject: [PATCH 11/64] Collect "privileged" users from LDAP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Privileged meaning users who are in a committée --- cmd/chalmers.it.config.toml | 2 +- ldap/service.go | 82 ++++++++++++++++++++++++++++++++++--- 2 files changed, 78 insertions(+), 6 deletions(-) diff --git a/cmd/chalmers.it.config.toml b/cmd/chalmers.it.config.toml index 05b7e2f..4c1a735 100644 --- a/cmd/chalmers.it.config.toml +++ b/cmd/chalmers.it.config.toml @@ -21,7 +21,7 @@ [ldap.users] basedn = "ou=people,dc=chalmers,dc=it" filter = "(&(objectClass=chalmersstudent))" - attibutes = ["uid", "mail"] + attributes = ["uid", "givenName", "sn", "nickname", "mail"] #### CUSTOM FILTERS #### [ldap.fkit] diff --git a/ldap/service.go b/ldap/service.go index bdb7157..063fcdb 100644 --- a/ldap/service.go +++ b/ldap/service.go @@ -6,6 +6,7 @@ import ( "github.com/cthit/goldapps" "gopkg.in/ldap.v2" "strings" + "fmt" ) type ServiceLDAP struct { @@ -16,10 +17,6 @@ type ServiceLDAP struct { CustomEntryConfigs []CustomEntryConfig } -func (s ServiceLDAP) GetUsers() ([]goldapps.User, error) { - panic("implement me") -} - type ServerConfig struct { Url string ServerName string @@ -88,6 +85,81 @@ func (s ServiceLDAP) users() ([]*ldap.Entry, error) { return result.Entries, nil } +// Collect all users who are members of a committee +func (s ServiceLDAP) GetUsers() ([]goldapps.User, error) { + users, err := s.users() + if err != nil { + return nil, err + } + + // Create a search request to collect all groups from LDAP + searchRequest := ldap.NewSearchRequest( + s.GroupsConfig.BaseDN, // The base dn to search + ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, + s.GroupsConfig.Filter, // The filter to apply + s.GroupsConfig.Attributes, // A list attributes to retrieve + nil, + ) + + // Collect the group entries + groups, err := s.Connection.Search(searchRequest) + if err != nil { + return nil, err + } + + // Create an empty goldapps.Group slice + privilegedUsers := make([]goldapps.User, 0) + + for _, group := range groups.Entries { + // TODO: What qualified as a privileged group should be made configurable. See FIXME:s + if group.GetAttributeValue("type") != "Committee" /* FIXME */ { + continue // Only Committees are considered privileged groups + } + + cn := group.GetAttributeValue("cn") + // Check if RDN is the same as the groups parent. FIXME + if strings.HasPrefix(group.DN, fmt.Sprintf("cn=%s,ou=%s", cn, cn)) { + for _, member := range group.GetAttributeValues("member") { + for _, user := range parsePrivilegedGroupMember(member, users, groups.Entries) { + privilegedUsers = append(privilegedUsers, goldapps.User{ + // TODO: Make these attribute values configurable + Cid: user.GetAttributeValue("uid"), + Nick: user.GetAttributeValue("nickname"), + FirstName: user.GetAttributeValue("givenName"), + SecondName: user.GetAttributeValue("sn"), + GdprEducation: false, // TODO + }) + } + } + } + } + + return privilegedUsers, nil +} + +// Recursively parse member tree and return users +func parsePrivilegedGroupMember(memberDN string, users []*ldap.Entry, groups []*ldap.Entry) ([]*ldap.Entry) { + res := make([]*ldap.Entry, 0) + if dnIsUser(memberDN) { + for _, user := range users { + if user.DN == memberDN { + res = append(res, user) + break + } + } + } else { + for _, group := range groups { + if group.DN == memberDN { + for _, subMember := range group.GetAttributeValues("member") { + res = append(res, parsePrivilegedGroupMember(subMember, users, groups)...) + } + break + } + } + } + return res +} + // Collects all committees from LDAP and then creates a // goldapps.Group slice. func (s ServiceLDAP) GetGroups() ([]goldapps.Group, error) { @@ -187,7 +259,7 @@ func (s ServiceLDAP) GetCustomGroups() ([]goldapps.Group, error) { entry.BaseDN, // The base dn to search ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, strings.Replace(entry.ParentFilter, "%childRDN%", getRDN(member.DN), -1), // The filter to apply - entry.Attributes, // A list attributes to retrieve + entry.Attributes, // A list attributes to retrieve nil, ) From 29ef01b7ea920fe335fa94bca16f3138ed204c5d Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Sat, 21 Apr 2018 17:41:33 +0200 Subject: [PATCH 12/64] Fix typos in example configuration --- cmd/chalmers.it.config.toml | 14 +++++++------- cmd/example.config.toml | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cmd/chalmers.it.config.toml b/cmd/chalmers.it.config.toml index 4c1a735..14af080 100644 --- a/cmd/chalmers.it.config.toml +++ b/cmd/chalmers.it.config.toml @@ -16,7 +16,7 @@ [ldap.groups] basedn = "ou=groups,dc=chalmers,dc=it" filter = "(|(objectClass=itGroup)(objectClass=itPosition))" - attibutes = ["cn", "displayName", "mail", "member"] + attributes = ["cn", "displayName", "mail", "member"] [ldap.users] basedn = "ou=people,dc=chalmers,dc=it" @@ -29,36 +29,36 @@ basedn = "ou=fkit,ou=groups,dc=chalmers,dc=it" filter = "(&(objectClass=itGroup))" parent_filter = "(&(ou=%childRDN%))" - attibutes = ["cn", "displayName", "mail"] + attributes = ["cn", "displayName", "mail"] [ldap.chairman] mail = "ordforande@chalmers.it" basedn = "ou=styrit,ou=fkit,ou=groups,dc=chalmers,dc=it" filter = "(&(objectClass=itPosition)(cn=ordf))" - attibutes = ["cn", "displayName", "mail"] + attributes = ["cn", "displayName", "mail"] [ldap.chairmen.fkit] mail = "ordforanden@chalmers.it" basedn = "ou=fkit,ou=groups,dc=chalmers,dc=it" filter = "(&(objectClass=itPosition)(cn=ordf))" - attibutes = ["cn", "displayName", "mail"] + attributes = ["cn", "displayName", "mail"] [ldap.chairmen.committees] mail = "ordforanden.kommiteer@chalmers.it" basedn = "ou=fkit,ou=groups,dc=chalmers,dc=it" filter = "(&(objectClass=itPosition)(cn=ordf))" parent_filter = "(&(objectClass=itGroup)(type=Committee))" - attibutes = ["cn", "displayName", "mail", "type"] + attributes = ["cn", "displayName", "mail", "type"] [ldap.treasurers] mail = "kassorer@chalmers.it" basedn = "ou=fkit,ou=groups,dc=chalmers,dc=it" filter = "(&(objectClass=itPosition)(cn=kassor))" - attibutes = ["cn", "displayName", "mail"] + attributes = ["cn", "displayName", "mail"] [ldap.phadderchef] mail = "phadderchef@chalmers.it" basedn = "ou=nollkit,ou=fkit,ou=groups,dc=chalmers,dc=it" filter = "(&(objectClass=itPosition)(cn=phadderchef))" - attibutes = ["cn", "displayName", "mail"] + attributes = ["cn", "displayName", "mail"] #### ============== #### diff --git a/cmd/example.config.toml b/cmd/example.config.toml index 956dad0..9c0f9de 100644 --- a/cmd/example.config.toml +++ b/cmd/example.config.toml @@ -16,16 +16,16 @@ [ldap.groups] basedn = "ou=some,ou=groups,dc=example,dc=ex" filter = "(&(objectClass=Group))" - attibutes = ["cn", "displayName", "mail", "member"] + attributes = ["cn", "displayName", "mail", "member"] [ldap.users] basedn = "ou=people,dc=example,dc=ex" filter = "(&(objectClass=Group))" - attibutes = ["uid", "mail"] + attributes = ["uid", "mail"] [ldap.my_custom_filter] mail = "custom@example.ex" basedn = "ou=groups,dc=chalmers,dc=it" filter = "(&(objectClass=Group))" parent_filter = "(&(ou=%childRDN%))" - attibutes = ["cn", "displayName", "mail"] + attributes = ["cn", "displayName", "mail"] From 03c4888df401f36c447f8bf22068a03f17884d41 Mon Sep 17 00:00:00 2001 From: Gurgy Date: Sat, 5 May 2018 23:48:44 +0200 Subject: [PATCH 13/64] Expand user model with passsword hash and function --- user.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/user.go b/user.go index 2332204..7f7b6d5 100644 --- a/user.go +++ b/user.go @@ -1,6 +1,8 @@ package goldapps -import "strings" +import ( + "strings" +) type User struct { Cid string `json:"cid"` @@ -8,6 +10,8 @@ type User struct { SecondName string `json:"second_name"` Nick string `json:"nick"` GdprEducation bool `json:"gdpr_education"` + PasswordHash string `json:"password_hash"` // For example "WjRIu6NlRX5PukqfMkAEb7xpOHJICasd" + HashFunction string `json:"hash_function"` // "crypt", "SHA-1" or "MD5", if not set PasswordHash will be interpreted as plaintext } func (user User) equals(other User) bool { @@ -31,5 +35,9 @@ func (user User) equals(other User) bool { return false } + /* + Do not check PasswordHash nor HashFunction + */ + return true } From 675b097678480d2827b6c358559ff4f396bb2503 Mon Sep 17 00:00:00 2001 From: Gurgy Date: Sat, 5 May 2018 23:50:35 +0200 Subject: [PATCH 14/64] Set password on newly created users --- admin/service.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/admin/service.go b/admin/service.go index 59245bf..5d6a0e6 100644 --- a/admin/service.go +++ b/admin/service.go @@ -223,7 +223,12 @@ func (s googleService) GetGroups() ([]goldapps.Group, error) { } func (s googleService) AddUser(user goldapps.User) error { - _, err := s.google.Users.Insert(buildGoldappsUser(user, s.domain)).Do() + + usr := buildGoldappsUser(user, s.domain) + usr.Password = user.PasswordHash + usr.HashFunction = user.HashFunction + + _, err := s.google.Users.Insert(usr).Do() if err != nil { return err } @@ -383,8 +388,6 @@ func buildGoldappsUser(user goldapps.User, domain string) *admin.User { }, IncludeInGlobalAddressList: true, PrimaryEmail: fmt.Sprintf("%s@%s", user.Cid, domain), - Password: "RandomPassword", // Todo: how to do with passwords? - ChangePasswordAtNextLogin: true, Suspended: !user.GdprEducation, SuspensionReason: gdprSuspensionText, } From 14ce9a7e9012b56b1e366866ccf9307b183f7ae6 Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Tue, 15 May 2018 17:30:13 +0200 Subject: [PATCH 15/64] Add "type" attribute to chalmers.it example conf --- cmd/chalmers.it.config.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/chalmers.it.config.toml b/cmd/chalmers.it.config.toml index 14af080..f1d362d 100644 --- a/cmd/chalmers.it.config.toml +++ b/cmd/chalmers.it.config.toml @@ -16,7 +16,7 @@ [ldap.groups] basedn = "ou=groups,dc=chalmers,dc=it" filter = "(|(objectClass=itGroup)(objectClass=itPosition))" - attributes = ["cn", "displayName", "mail", "member"] + attributes = ["cn", "displayName", "mail", "member", "type"] [ldap.users] basedn = "ou=people,dc=chalmers,dc=it" From 8f2df9bb9c32d5308e15f860683a2db87affac10 Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Tue, 15 May 2018 17:31:27 +0200 Subject: [PATCH 16/64] Remove password hash attribute from User --- admin/service.go | 5 +++-- user.go | 2 -- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/admin/service.go b/admin/service.go index 5d6a0e6..2bd5fc3 100644 --- a/admin/service.go +++ b/admin/service.go @@ -225,8 +225,9 @@ func (s googleService) GetGroups() ([]goldapps.Group, error) { func (s googleService) AddUser(user goldapps.User) error { usr := buildGoldappsUser(user, s.domain) - usr.Password = user.PasswordHash - usr.HashFunction = user.HashFunction + // FIXME: + // usr.Password = user.PasswordHash + // usr.HashFunction = user.HashFunction _, err := s.google.Users.Insert(usr).Do() if err != nil { diff --git a/user.go b/user.go index 7f7b6d5..59c5af9 100644 --- a/user.go +++ b/user.go @@ -10,8 +10,6 @@ type User struct { SecondName string `json:"second_name"` Nick string `json:"nick"` GdprEducation bool `json:"gdpr_education"` - PasswordHash string `json:"password_hash"` // For example "WjRIu6NlRX5PukqfMkAEb7xpOHJICasd" - HashFunction string `json:"hash_function"` // "crypt", "SHA-1" or "MD5", if not set PasswordHash will be interpreted as plaintext } func (user User) equals(other User) bool { From 6978593f0f38ed93bcbe8a04068da90139c27421 Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Tue, 15 May 2018 17:31:57 +0200 Subject: [PATCH 17/64] Add mail and gdprEducation attributes to User --- cmd/chalmers.it.config.toml | 2 +- ldap/service.go | 3 ++- user.go | 5 +++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/cmd/chalmers.it.config.toml b/cmd/chalmers.it.config.toml index f1d362d..d37f219 100644 --- a/cmd/chalmers.it.config.toml +++ b/cmd/chalmers.it.config.toml @@ -21,7 +21,7 @@ [ldap.users] basedn = "ou=people,dc=chalmers,dc=it" filter = "(&(objectClass=chalmersstudent))" - attributes = ["uid", "givenName", "sn", "nickname", "mail"] + attributes = ["uid", "givenName", "sn", "nickname", "mail", "gdprEducated"] #### CUSTOM FILTERS #### [ldap.fkit] diff --git a/ldap/service.go b/ldap/service.go index 063fcdb..c3766fc 100644 --- a/ldap/service.go +++ b/ldap/service.go @@ -127,7 +127,8 @@ func (s ServiceLDAP) GetUsers() ([]goldapps.User, error) { Nick: user.GetAttributeValue("nickname"), FirstName: user.GetAttributeValue("givenName"), SecondName: user.GetAttributeValue("sn"), - GdprEducation: false, // TODO + Mail: user.GetAttributeValue("mail"), + GdprEducation: user.GetAttributeValue("gdprEducated") == "TRUE", }) } } diff --git a/user.go b/user.go index 59c5af9..78760c3 100644 --- a/user.go +++ b/user.go @@ -9,6 +9,7 @@ type User struct { FirstName string `json:"first_name"` SecondName string `json:"second_name"` Nick string `json:"nick"` + Mail string `json:"mail"` GdprEducation bool `json:"gdpr_education"` } @@ -29,6 +30,10 @@ func (user User) equals(other User) bool { return false } + if user.Mail != other.Mail { + return false + } + if user.GdprEducation != other.GdprEducation { return false } From 3ee2d1522bd45c25640c43e90a871c8f81c5ae97 Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Tue, 15 May 2018 19:04:10 +0200 Subject: [PATCH 18/64] Set proper group scope in chalmers.it-config --- cmd/chalmers.it.config.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/chalmers.it.config.toml b/cmd/chalmers.it.config.toml index d37f219..d134f80 100644 --- a/cmd/chalmers.it.config.toml +++ b/cmd/chalmers.it.config.toml @@ -14,7 +14,7 @@ custom = ["chairman", "chairmen.fkit", "chairmen.committees", "treasurers", "phadderchef", "fkit"] [ldap.groups] - basedn = "ou=groups,dc=chalmers,dc=it" + basedn = "ou=fkit,ou=groups,dc=chalmers,dc=it" filter = "(|(objectClass=itGroup)(objectClass=itPosition))" attributes = ["cn", "displayName", "mail", "member", "type"] From 8be0bdc629822877eed94f2e522dd45eb0eb95ca Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Tue, 15 May 2018 19:05:18 +0200 Subject: [PATCH 19/64] Replace committee user email with auto-generated --- group.go | 4 ++++ ldap/service.go | 27 ++++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/group.go b/group.go index b0c6377..d95d446 100644 --- a/group.go +++ b/group.go @@ -8,6 +8,7 @@ import "strings" // Aliases are alternative email addresses for the group. type Group struct { Email string `json:"email"` + Type string `json:"type"` Members []string `json:"members"` Aliases []string `json:"aliases"` } @@ -16,6 +17,9 @@ func (group Group) equals(other Group) bool { if strings.ToLower(group.Email) != strings.ToLower(other.Email) { return false } + if group.Type != other.Type { + return false + } if len(group.Members) != len(other.Members) { return false } diff --git a/ldap/service.go b/ldap/service.go index c3766fc..af5d8a0 100644 --- a/ldap/service.go +++ b/ldap/service.go @@ -193,9 +193,14 @@ func (s ServiceLDAP) GetGroups() ([]goldapps.Group, error) { // Creates a goldapps.Group with it's mail committee := goldapps.Group{ Email: entry.GetAttributeValue("mail"), + Type: entry.GetAttributeValue("type"), Members: nil, } + if committee.Email == "" { + continue + } + // Creates an empty members slice members := make([]string, 0) // len(users) might break if we have all users and some groups in the members field @@ -222,11 +227,30 @@ func (s ServiceLDAP) GetGroups() ([]goldapps.Group, error) { groups = append(groups, committee) } + // Dear god just please let me die + // TODO: FIXME: Refactor this, please. + for _, group := range groups { + if group.Type == "Committee" { + for _, subGroup := range groups { + for _, memberEmail := range group.Members { + if subGroup.Email == memberEmail { + for i, userMail := range subGroup.Members { + for _, user := range users { + if userMail == user.GetAttributeValue("mail") { + subGroup.Members[i] = user.GetAttributeValue("uid") + "@chalmers.it" + } + } + } + } + } + } + } + } + customGroups, err := s.GetCustomGroups() if err != nil { return nil, err } - groups = append(groups, customGroups...) return groups, nil @@ -259,6 +283,7 @@ func (s ServiceLDAP) GetCustomGroups() ([]goldapps.Group, error) { parentSearchRequest := ldap.NewSearchRequest( entry.BaseDN, // The base dn to search ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, + // FIXME: The %childRDN% is only necessary since year groups (e.g. snit14) are the same type as their Committee/Society. strings.Replace(entry.ParentFilter, "%childRDN%", getRDN(member.DN), -1), // The filter to apply entry.Attributes, // A list attributes to retrieve nil, From 50f9c7e624157ac5f3e6bac35f0df975b56c6afe Mon Sep 17 00:00:00 2001 From: Gurgy Date: Tue, 15 May 2018 19:41:19 +0200 Subject: [PATCH 20/64] Add password generation functionality --- admin/password.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 admin/password.go diff --git a/admin/password.go b/admin/password.go new file mode 100644 index 0000000..134b546 --- /dev/null +++ b/admin/password.go @@ -0,0 +1,16 @@ +package admin + +import ( + "github.com/sethvargo/go-password/password" + "math/rand" +) + +func newPassword() string { + numbers := rand.Intn(10) + 5 + symbols := rand.Intn(10) + 5 + pass, err := password.Generate(64, numbers, symbols, false, true) + if err != nil { + panic("Password generation failed") + } + return pass +} From 6f7ba393d048b257bab4072bd19fb848fd5218a4 Mon Sep 17 00:00:00 2001 From: Gurgy Date: Tue, 15 May 2018 19:42:08 +0200 Subject: [PATCH 21/64] Add Google mail scope --- admin/scope.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/admin/scope.go b/admin/scope.go index 6c4408f..0c220d0 100644 --- a/admin/scope.go +++ b/admin/scope.go @@ -1,11 +1,15 @@ package admin -import "google.golang.org/api/admin/directory/v1" +import ( + "google.golang.org/api/admin/directory/v1" + "google.golang.org/api/gmail/v1" +) func Scopes() []string { return []string{ admin.AdminDirectoryGroupScope, admin.AdminDirectoryUserScope, + gmail.GmailSendScope, } } // https://www.googleapis.com/auth/admin.directory.group, https://www.googleapis.com/auth/admin.directory.user \ No newline at end of file From ff40e52948a55327e5a98dc729067bda9208935b Mon Sep 17 00:00:00 2001 From: Gurgy Date: Tue, 15 May 2018 19:44:17 +0200 Subject: [PATCH 22/64] Add mailing functionality --- admin/service.go | 86 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 60 insertions(+), 26 deletions(-) diff --git a/admin/service.go b/admin/service.go index 5d6a0e6..0a35c3f 100644 --- a/admin/service.go +++ b/admin/service.go @@ -4,11 +4,13 @@ import ( "github.com/cthit/goldapps" "google.golang.org/api/admin/directory/v1" // Imports as admin + "google.golang.org/api/gmail/v1" // Imports as gmail "golang.org/x/net/context" "golang.org/x/oauth2/google" "bytes" + "encoding/base64" "fmt" "io/ioutil" "strings" @@ -19,13 +21,17 @@ const gdprSuspensionText = "You have not attended the GDPR education!" const googleDuplicateEntryError = "googleapi: Error 409: Entity already exists., duplicate" +const passwordMailBody = "Here is your password: %s" +const passwordMailSubject = "Login details for google services at chalmers.it" + // my_customer seems to work... const googleCustomer = "my_customer" type googleService struct { - google *admin.Service - admin string - domain string + adminService *admin.Service + mailService *gmail.Service + admin string + domain string } func NewGoogleService(keyPath string, adminMail string) (goldapps.UpdateService, error) { @@ -52,22 +58,46 @@ func NewGoogleService(keyPath string, adminMail string) (goldapps.UpdateService, return nil, err } + mailService, err := gmail.New(client) + if err != nil { + return nil, err + } + // Extract account and mail s := strings.Split(adminMail, "@") admin := s[0] domain := s[1] gs := googleService{ - google: service, - admin: admin, - domain: domain, + adminService: service, + mailService: mailService, + admin: admin, + domain: domain, } return gs, nil } +func (g googleService) sendPassword(to string, password string) error { + + from := "no-reply@" + g.domain + body := fmt.Sprintf(passwordMailBody, password) + + msgRaw := "From: " + from + "\r\n" + + "To: " + to + "\r\n" + + "Subject: " + passwordMailSubject + "\r\n\r\n" + + body + "\r\n" + + msg := &gmail.Message{ + Raw: base64.StdEncoding.EncodeToString([]byte(msgRaw)), + } + _, err := g.mailService.Users.Messages.Send(from, msg).Do() + + return err +} + func (s googleService) DeleteGroup(group goldapps.Group) error { - err := s.google.Groups.Delete(group.Email).Do() + err := s.adminService.Groups.Delete(group.Email).Do() return err } @@ -86,7 +116,7 @@ func (s googleService) UpdateGroup(groupUpdate goldapps.GroupUpdate) error { } } if !exists { - _, err := s.google.Members.Insert(groupUpdate.Before.Email, &admin.Member{Email: member}).Do() + _, err := s.adminService.Members.Insert(groupUpdate.Before.Email, &admin.Member{Email: member}).Do() if err != nil { fmt.Printf("Failed to add menber %s\n", member) return err @@ -104,7 +134,7 @@ func (s googleService) UpdateGroup(groupUpdate goldapps.GroupUpdate) error { } } if !keep { - err := s.google.Members.Delete(groupUpdate.Before.Email, existingMember).Do() + err := s.adminService.Members.Delete(groupUpdate.Before.Email, existingMember).Do() if err != nil { return err } @@ -121,7 +151,7 @@ func (s googleService) UpdateGroup(groupUpdate goldapps.GroupUpdate) error { } } if !exists { - _, err := s.google.Groups.Aliases.Insert(groupUpdate.Before.Email, &admin.Alias{Alias: alias}).Do() + _, err := s.adminService.Groups.Aliases.Insert(groupUpdate.Before.Email, &admin.Alias{Alias: alias}).Do() if err != nil { return err } @@ -138,14 +168,14 @@ func (s googleService) UpdateGroup(groupUpdate goldapps.GroupUpdate) error { } } if !keep { - err := s.google.Groups.Aliases.Delete(groupUpdate.Before.Email, existingAlias).Do() + err := s.adminService.Groups.Aliases.Delete(groupUpdate.Before.Email, existingAlias).Do() if err != nil { return err } } } - _, err := s.google.Groups.Update(groupUpdate.Before.Email, &newGroup).Do() + _, err := s.adminService.Groups.Update(groupUpdate.Before.Email, &newGroup).Do() return err } @@ -154,7 +184,7 @@ func (s googleService) AddGroup(group goldapps.Group) error { Email: group.Email, } - _, err := s.google.Groups.Insert(&newGroup).Do() + _, err := s.adminService.Groups.Insert(&newGroup).Do() if err != nil { return err } @@ -163,7 +193,7 @@ func (s googleService) AddGroup(group goldapps.Group) error { // Add members for _, member := range group.Members { - _, err := s.google.Members.Insert(group.Email, &admin.Member{Email: member}).Do() + _, err := s.adminService.Members.Insert(group.Email, &admin.Member{Email: member}).Do() if err != nil { return err } @@ -171,7 +201,7 @@ func (s googleService) AddGroup(group goldapps.Group) error { // Add Aliases for _, alias := range group.Aliases { - _, err := s.google.Groups.Aliases.Insert(group.Email, &admin.Alias{Alias: alias}).Do() + _, err := s.adminService.Groups.Aliases.Insert(group.Email, &admin.Alias{Alias: alias}).Do() if err != nil { return err } @@ -225,14 +255,18 @@ func (s googleService) GetGroups() ([]goldapps.Group, error) { func (s googleService) AddUser(user goldapps.User) error { usr := buildGoldappsUser(user, s.domain) - usr.Password = user.PasswordHash - usr.HashFunction = user.HashFunction + password := newPassword() + + usr.Password = password + usr.ChangePasswordAtNextLogin = true - _, err := s.google.Users.Insert(usr).Do() + _, err := s.adminService.Users.Insert(usr).Do() if err != nil { return err } + //s.sendPassword(user.Mail, password) + // Google needs time for the addition to propagate time.Sleep(time.Second) @@ -241,7 +275,7 @@ func (s googleService) AddUser(user goldapps.User) error { } func (s googleService) UpdateUser(update goldapps.UserUpdate) error { - _, err := s.google.Users.Update( + _, err := s.adminService.Users.Update( fmt.Sprintf("%s@%s", update.Before.Cid, s.domain), buildGoldappsUser(update.After, s.domain), ).Do() @@ -260,7 +294,7 @@ func (s googleService) DeleteUser(user goldapps.User) error { fmt.Printf("Skipping andmin user: %s\n", admin) } - err := s.google.Users.Delete(userId).Do() + err := s.adminService.Users.Delete(userId).Do() return err } @@ -305,13 +339,13 @@ func (s googleService) GetUsers() ([]goldapps.User, error) { } func (s googleService) getGoogleGroups(customer string) ([]admin.Group, error) { - groups, err := s.google.Groups.List().Customer(customer).Do() + groups, err := s.adminService.Groups.List().Customer(customer).Do() if err != nil { return nil, err } for groups.NextPageToken != "" { - newGroups, err := s.google.Groups.List().Customer(customer).PageToken(groups.NextPageToken).Do() + newGroups, err := s.adminService.Groups.List().Customer(customer).PageToken(groups.NextPageToken).Do() if err != nil { return nil, err } @@ -329,7 +363,7 @@ func (s googleService) getGoogleGroups(customer string) ([]admin.Group, error) { } func (s googleService) getGoogleGroupMembers(email string) ([]string, error) { - members, err := s.google.Members.List(email).Do() + members, err := s.adminService.Members.List(email).Do() if err != nil { return nil, err } @@ -343,13 +377,13 @@ func (s googleService) getGoogleGroupMembers(email string) ([]string, error) { } func (s googleService) getGoogleUsers(customer string) ([]admin.User, error) { - users, err := s.google.Users.List().Customer(customer).Do() + users, err := s.adminService.Users.List().Customer(customer).Do() if err != nil { return nil, err } for users.NextPageToken != "" { - newUsers, err := s.google.Users.List().Customer(customer).PageToken(users.NextPageToken).Do() + newUsers, err := s.adminService.Users.List().Customer(customer).PageToken(users.NextPageToken).Do() if err != nil { return nil, err } @@ -367,7 +401,7 @@ func (s googleService) getGoogleUsers(customer string) ([]admin.User, error) { } func (s googleService) addUserAlias(userKey string, alias string) error { - _, err := s.google.Users.Aliases.Insert(userKey, &admin.Alias{ + _, err := s.adminService.Users.Aliases.Insert(userKey, &admin.Alias{ Alias: alias, }).Do() if err != nil { From da66891672fed3d9d80179e724a12fd63bddb3f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20=27NaN=27=20Wikstr=C3=B6m?= Date: Tue, 15 May 2018 23:09:58 +0200 Subject: [PATCH 23/64] Fix problem with broken custom groups --- ldap/service.go | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/ldap/service.go b/ldap/service.go index af5d8a0..a12a5f8 100644 --- a/ldap/service.go +++ b/ldap/service.go @@ -257,6 +257,11 @@ func (s ServiceLDAP) GetGroups() ([]goldapps.Group, error) { } func (s ServiceLDAP) GetCustomGroups() ([]goldapps.Group, error) { + users, err := s.users() + if err != nil { + return nil, err + } + customGroups := make([]goldapps.Group, 0) for _, entry := range s.CustomEntryConfigs { @@ -308,9 +313,21 @@ func (s ServiceLDAP) GetCustomGroups() ([]goldapps.Group, error) { if addMember { mail := member.GetAttributeValue("mail") - if mail != "" { - members = append(members, mail) + localMembers := member.GetAttributeValues("member") + // Check if the found entry has a mail associated with it + if mail == "" { // if not it should have members which do + for _, localMember := range localMembers { + mail = findEntry(users, localMember).GetAttributeValue("mail") + //fmt.Println(mail) + members = append(members, mail) + } + } else { + mail := member.GetAttributeValue("mail") + if mail != "" { + members = append(members, mail) + } } + } } From df0fc612efb27faf0c6cb7dee9ef208c93da0f70 Mon Sep 17 00:00:00 2001 From: Gurgy Date: Tue, 15 May 2018 20:30:44 +0200 Subject: [PATCH 24/64] Remove mail check from equal-check --- user.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/user.go b/user.go index 78760c3..eb3cedd 100644 --- a/user.go +++ b/user.go @@ -30,9 +30,9 @@ func (user User) equals(other User) bool { return false } - if user.Mail != other.Mail { + /*if user.Mail != other.Mail { return false - } + }*/ if user.GdprEducation != other.GdprEducation { return false From ef25301b720ab017d97805587bd25867ae28d844 Mon Sep 17 00:00:00 2001 From: Gurgy Date: Tue, 15 May 2018 20:33:23 +0200 Subject: [PATCH 25/64] Send password mail from admin account --- admin/service.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/admin/service.go b/admin/service.go index aeae2f3..5d2c3af 100644 --- a/admin/service.go +++ b/admin/service.go @@ -80,7 +80,7 @@ func NewGoogleService(keyPath string, adminMail string) (goldapps.UpdateService, func (g googleService) sendPassword(to string, password string) error { - from := "no-reply@" + g.domain + from := g.admin + "@" + g.domain body := fmt.Sprintf(passwordMailBody, password) msgRaw := "From: " + from + "\r\n" + @@ -266,7 +266,10 @@ func (s googleService) AddUser(user goldapps.User) error { return err } - //s.sendPassword(user.Mail, password) + err = s.sendPassword(user.Mail, password) + if err != nil { + return err + } // Google needs time for the addition to propagate time.Sleep(time.Second) From b6894406bc4708debf697d68f5e3be01934668c3 Mon Sep 17 00:00:00 2001 From: Gurgy Date: Wed, 16 May 2018 13:59:28 +0200 Subject: [PATCH 26/64] Remove duplicate users --- ldap/service.go | 29 ++++++++++++++++------------- user.go | 11 +++++++++++ 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/ldap/service.go b/ldap/service.go index a12a5f8..26295fc 100644 --- a/ldap/service.go +++ b/ldap/service.go @@ -3,10 +3,10 @@ package ldap import ( "crypto/tls" + "fmt" "github.com/cthit/goldapps" "gopkg.in/ldap.v2" "strings" - "fmt" ) type ServiceLDAP struct { @@ -108,7 +108,7 @@ func (s ServiceLDAP) GetUsers() ([]goldapps.User, error) { } // Create an empty goldapps.Group slice - privilegedUsers := make([]goldapps.User, 0) + privilegedUsers := make(goldapps.Users, 0) for _, group := range groups.Entries { // TODO: What qualified as a privileged group should be made configurable. See FIXME:s @@ -121,15 +121,18 @@ func (s ServiceLDAP) GetUsers() ([]goldapps.User, error) { if strings.HasPrefix(group.DN, fmt.Sprintf("cn=%s,ou=%s", cn, cn)) { for _, member := range group.GetAttributeValues("member") { for _, user := range parsePrivilegedGroupMember(member, users, groups.Entries) { - privilegedUsers = append(privilegedUsers, goldapps.User{ - // TODO: Make these attribute values configurable - Cid: user.GetAttributeValue("uid"), - Nick: user.GetAttributeValue("nickname"), - FirstName: user.GetAttributeValue("givenName"), - SecondName: user.GetAttributeValue("sn"), - Mail: user.GetAttributeValue("mail"), - GdprEducation: user.GetAttributeValue("gdprEducated") == "TRUE", - }) + if !privilegedUsers.Contains(user.GetAttributeValue("uid")) { + privilegedUsers = append(privilegedUsers, goldapps.User{ + // TODO: Make these attribute values configurable + Cid: user.GetAttributeValue("uid"), + Nick: user.GetAttributeValue("nickname"), + FirstName: user.GetAttributeValue("givenName"), + SecondName: user.GetAttributeValue("sn"), + Mail: user.GetAttributeValue("mail"), + GdprEducation: user.GetAttributeValue("gdprEducated") == "TRUE", + }) + fmt.Println(user.GetAttributeValue("uid")) + } } } } @@ -139,7 +142,7 @@ func (s ServiceLDAP) GetUsers() ([]goldapps.User, error) { } // Recursively parse member tree and return users -func parsePrivilegedGroupMember(memberDN string, users []*ldap.Entry, groups []*ldap.Entry) ([]*ldap.Entry) { +func parsePrivilegedGroupMember(memberDN string, users []*ldap.Entry, groups []*ldap.Entry) []*ldap.Entry { res := make([]*ldap.Entry, 0) if dnIsUser(memberDN) { for _, user := range users { @@ -290,7 +293,7 @@ func (s ServiceLDAP) GetCustomGroups() ([]goldapps.Group, error) { ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, // FIXME: The %childRDN% is only necessary since year groups (e.g. snit14) are the same type as their Committee/Society. strings.Replace(entry.ParentFilter, "%childRDN%", getRDN(member.DN), -1), // The filter to apply - entry.Attributes, // A list attributes to retrieve + entry.Attributes, // A list attributes to retrieve nil, ) diff --git a/user.go b/user.go index eb3cedd..7954536 100644 --- a/user.go +++ b/user.go @@ -13,6 +13,17 @@ type User struct { GdprEducation bool `json:"gdpr_education"` } +type Users []User + +func (users Users) Contains(cid string) bool { + for _, user := range users { + if user.Cid == cid { + return true + } + } + return false +} + func (user User) equals(other User) bool { if strings.ToLower(user.Cid) != strings.ToLower(other.Cid) { return false From af8874331afd7220b32659aa13761838d73dab04 Mon Sep 17 00:00:00 2001 From: Gurgy Date: Wed, 16 May 2018 16:02:25 +0200 Subject: [PATCH 27/64] Allow for additional groups and users from file --- cmd/flags.go | 6 +++-- cmd/main.go | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++-- group.go | 11 ++++++++++ 3 files changed, 75 insertions(+), 4 deletions(-) diff --git a/cmd/flags.go b/cmd/flags.go index 230dc35..c3c11b1 100644 --- a/cmd/flags.go +++ b/cmd/flags.go @@ -5,6 +5,7 @@ import "flag" type flagStruct struct { from string to string + additions string interactive bool noInteraction bool dryRun bool @@ -15,8 +16,9 @@ type flagStruct struct { var flags = flagStruct{} func loadFlags() { - flag.StringVar(&flags.from, "from", "ldap", "Set the group source to 'ldap', 'gapps' or '*.json'. In case of gapps config value 'gappsProvider' will be used") - flag.StringVar(&flags.to, "to", "gapps", "Set the group consumer to 'gapps' or '*.json'") + flag.StringVar(&flags.from, "from", "ldap", "Set the source to 'ldap', 'gapps' or '*.json'. In case of gapps config value 'gappsProvider' will be used") + flag.StringVar(&flags.additions, "additions", "", "Set a json file for additional groups and users") + flag.StringVar(&flags.to, "to", "gapps", "Set the consumer to 'gapps' or '*.json'") flag.BoolVar(&flags.dryRun, "dry", false, "Setting this flag will cause the application to only print information and not update any groups") flag.BoolVar(&flags.noInteraction, "y", false, "Setting this flag will cause the application to not ask for any user confirmation") flag.BoolVar(&flags.interactive, "i", false, "Setting this flag will cause the application to ask the user for input in every stage ") diff --git a/cmd/main.go b/cmd/main.go index 7143302..63e6e45 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -30,7 +30,7 @@ func main() { var err error - var providerGroups []goldapps.Group + var providerGroups goldapps.Groups if !flags.onlyUsers { fmt.Println("Collecting groups from the provider...") providerGroups, err = provider.GetGroups() @@ -41,7 +41,7 @@ func main() { fmt.Printf("%d groups collected.\n", len(providerGroups)) } - var providerUsers []goldapps.User + var providerUsers goldapps.Users if !flags.onlyGroups { fmt.Println("Collecting users from the provider...") providerUsers, err = provider.GetUsers() @@ -74,6 +74,28 @@ func main() { fmt.Printf("%d users collected.\n", len(consumerUsers)) } + fmt.Println("Collecting additions") + additionUsers, additionGroups := getAdditions() + if additionUsers != nil && additionGroups != nil { + fmt.Printf("%d usersAdditions and %d groupAdditions collected.\n", len(additionUsers), len(additionGroups)) + fmt.Print("Adding groups... ") + for _, group := range additionGroups { + if !providerGroups.Contains(group.Email) { + providerGroups = append(providerGroups, group) + } + } + fmt.Println("Done!") + fmt.Print("Adding users... ") + for _, user := range additionUsers { + if !providerUsers.Contains(user.Cid) { + providerUsers = append(providerUsers, user) + } + } + fmt.Println("Done!") + } else { + fmt.Println("Skipping additions") + } + groupChanges := goldapps.GroupActions{} if !flags.onlyUsers { fmt.Println("Colculating difference between the consumer and provider groups.") @@ -257,6 +279,42 @@ func getUserChanges(proposedChanges goldapps.UserActions) goldapps.UserActions { return proposedChanges } +func getAdditions() ([]goldapps.User, []goldapps.Group) { + + var from string + if flags.interactive { + from = askString("Which file would you like to use for additions?, Just press enter to skip", "") + } else { + from = flags.additions + } + + if from == "" { + return nil, nil + } + + isJson, _ := regexp.MatchString(`.+\.json$`, from) + if isJson { + provider, _ := json.NewJsonService(from) + groups, err := provider.GetGroups() + if err != nil { + panic(err) + } + users, err := provider.GetUsers() + if err != nil { + panic(err) + } + return users, groups + } else { + fmt.Println("You must specify a valid json file") + previous := flags.interactive + flags.interactive = true + defer func() { + flags.interactive = previous + }() + return getAdditions() + } +} + func getConsumer() goldapps.UpdateService { var to string if flags.interactive { diff --git a/group.go b/group.go index d95d446..e66504f 100644 --- a/group.go +++ b/group.go @@ -13,6 +13,17 @@ type Group struct { Aliases []string `json:"aliases"` } +type Groups []Group + +func (groups Groups) Contains(email string) bool { + for _, group := range groups { + if group.Email == email { + return true + } + } + return false +} + func (group Group) equals(other Group) bool { if strings.ToLower(group.Email) != strings.ToLower(other.Email) { return false From e9c9c0760090cb880fcfeb41c3d2ada479dbf837 Mon Sep 17 00:00:00 2001 From: Gurgy Date: Wed, 16 May 2018 18:31:23 +0200 Subject: [PATCH 28/64] Remove debug print --- ldap/service.go | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/ldap/service.go b/ldap/service.go index 26295fc..aa4040d 100644 --- a/ldap/service.go +++ b/ldap/service.go @@ -131,7 +131,6 @@ func (s ServiceLDAP) GetUsers() ([]goldapps.User, error) { Mail: user.GetAttributeValue("mail"), GdprEducation: user.GetAttributeValue("gdprEducated") == "TRUE", }) - fmt.Println(user.GetAttributeValue("uid")) } } } @@ -230,6 +229,12 @@ func (s ServiceLDAP) GetGroups() ([]goldapps.Group, error) { groups = append(groups, committee) } + customGroups, err := s.GetCustomGroups() + if err != nil { + return nil, err + } + groups = append(groups, customGroups...) + // Dear god just please let me die // TODO: FIXME: Refactor this, please. for _, group := range groups { @@ -250,12 +255,6 @@ func (s ServiceLDAP) GetGroups() ([]goldapps.Group, error) { } } - customGroups, err := s.GetCustomGroups() - if err != nil { - return nil, err - } - groups = append(groups, customGroups...) - return groups, nil } From 2930760d91420080dc3047e5712bf0bc7f02dfbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20=27NaN=27=20Wikstr=C3=B6m?= Date: Thu, 17 May 2018 09:32:50 +0200 Subject: [PATCH 29/64] Add cn.cn@chalmers.it and replace committee mails --- ldap/service.go | 98 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/ldap/service.go b/ldap/service.go index aa4040d..3104c59 100644 --- a/ldap/service.go +++ b/ldap/service.go @@ -235,6 +235,12 @@ func (s ServiceLDAP) GetGroups() ([]goldapps.Group, error) { } groups = append(groups, customGroups...) + positionGroups, err := s.getPositionGroups() + if err != nil { + return nil, err + } + groups = append(groups, positionGroups...) + // Dear god just please let me die // TODO: FIXME: Refactor this, please. for _, group := range groups { @@ -255,6 +261,31 @@ func (s ServiceLDAP) GetGroups() ([]goldapps.Group, error) { } } + //FIXME!!! + // See above comments (only two for loops :D) + // Fulhack deluxe :ok_hand: + // this is fine.... + for _, group := range groups { + if group.Type == "CommitteeDirect" { + for i, member := range group.Members { + replacementFound := false + xusers, err := s.GetUsers() // Åh nej... + if err != nil { + return nil, err + } + for _, user := range xusers { + if user.Mail == member { + replacementFound = true + group.Members[i] = user.Cid + "@chalmers.it" + } + } + if !replacementFound { + return nil, fmt.Errorf("no replacement could be found for %s", member) + } + } + } + } + return groups, nil } @@ -344,6 +375,55 @@ func (s ServiceLDAP) GetCustomGroups() ([]goldapps.Group, error) { return customGroups, nil } +func (s ServiceLDAP) getPositionGroups() ([]goldapps.Group, error) { + users, err := s.users() + if err != nil { + return nil, err + } + + var positionGroups []goldapps.Group + + + + searchRequest := ldap.NewSearchRequest( + "ou=fkit,ou=groups,dc=chalmers,dc=it", // The base dn to search + ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, + "(&(objectClass=itPosition))", // The filter to apply + []string{"cn", "member"}, // A list attributes to retrieve + nil, + ) + + result, err := s.Connection.Search(searchRequest) + if err != nil { + return nil, err + } + + for _, entry := range result.Entries { + groupType, err := dnPositionType(s, entry.DN) + DnSplit := strings.SplitN(entry.DN, ",", 3) + pos := DnSplit[0][3:] + grp := DnSplit[1][3:] + if err != nil { + return nil, err + } + + posGroup := goldapps.Group{ + Email: fmt.Sprintf("%s.%s@chalmers.it", pos, grp), + Type: groupType + "Direct", + } + + for _, member := range entry.GetAttributeValues("member") { + mail := findEntry(users, member).GetAttributeValue("mail") + posGroup.Members = append(posGroup.Members, mail) + } + + positionGroups = append(positionGroups,posGroup) + + } + return positionGroups, nil + +} + func findEntry(ldapEntries []*ldap.Entry, DN string) *ldap.Entry { for _, entry := range ldapEntries { if entry.DN == DN { @@ -364,3 +444,21 @@ func dnIsParentOf(parent string, node string) bool { func dnIsUser(DN string) bool { return len(DN) >= 4 && DN[0:4] == "uid=" } + +func dnPositionType(s ServiceLDAP, DN string) (string, error) { + newDN := strings.SplitN(DN, ",", 2)[1] // Creates the dn for the group + sr := ldap.NewSearchRequest( + newDN, // The base dn to search + ldap.ScopeBaseObject, ldap.NeverDerefAliases, 0, 0, false, + "(&(objectClass=*))", // The filter to apply + []string{"type"}, // A list attributes to retrieve + nil, + ) + + result, err := s.Connection.Search(sr) + if err != nil { + return "", err + } + + return result.Entries[0].GetAttributeValue("type"), nil +} From de6c5dc0febf89c810a65affe39a0fbfba69ff8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20=27NaN=27=20Wikstr=C3=B6m?= Date: Thu, 17 May 2018 10:21:35 +0200 Subject: [PATCH 30/64] Add functions for committees, chairmen in those, chairmen and treasurers --- ldap/service.go | 104 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/ldap/service.go b/ldap/service.go index 3104c59..a57cbd2 100644 --- a/ldap/service.go +++ b/ldap/service.go @@ -241,6 +241,33 @@ func (s ServiceLDAP) GetGroups() ([]goldapps.Group, error) { } groups = append(groups, positionGroups...) + chairmenGroupMembers, err := s.getChairmenGroup() + if err != nil { + return nil, err + } + groups = append(groups, goldapps.Group{ + Email: "ordforanden@chalmers.it", + Members: chairmenGroupMembers, + }) + + chairmenInCommitteesGroupMembers, err := s.getChairmenInCommitteesGroup() + if err != nil { + return nil, err + } + groups = append(groups, goldapps.Group{ + Email: "ordforanden.kommitteer@chalmers.it", + Members: chairmenInCommitteesGroupMembers, + }) + + treasurersGroupMembers, err := s.getTreasurersGroup() + if err != nil { + return nil, err + } + groups = append(groups, goldapps.Group{ + Email: "kassorer@chalmers.it", + Members: treasurersGroupMembers, + }) + // Dear god just please let me die // TODO: FIXME: Refactor this, please. for _, group := range groups { @@ -424,6 +451,83 @@ func (s ServiceLDAP) getPositionGroups() ([]goldapps.Group, error) { } +func (s ServiceLDAP) getChairmenGroup() ([]string, error) { + searchRequest := ldap.NewSearchRequest( + "ou=fkit,ou=groups,dc=chalmers,dc=it", // The base dn to search + ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, + "(&(objectClass=itPosition)(cn=ordf))", // The filter to apply + []string{"cn"}, // A list attributes to retrieve + nil, + ) + + result, err := s.Connection.Search(searchRequest) + if err != nil { + return nil, err + } + + var chairmenGroup []string + + for _, entry := range result.Entries { + dnSplit := strings.SplitN(entry.DN, ",", 3) + chairmenGroup = append(chairmenGroup, "ordf." + dnSplit[1][3:] + "@chalmers.it") + } + return chairmenGroup, nil +} + +func (s ServiceLDAP) getTreasurersGroup() ([]string, error) { + searchRequest := ldap.NewSearchRequest( + "ou=fkit,ou=groups,dc=chalmers,dc=it", // The base dn to search + ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, + "(&(objectClass=itPosition)(cn=kassor))", // The filter to apply + []string{"cn"}, // A list attributes to retrieve + nil, + ) + + result, err := s.Connection.Search(searchRequest) + if err != nil { + return nil, err + } + + var chairmenGroup []string + + for _, entry := range result.Entries { + dnSplit := strings.SplitN(entry.DN, ",", 3) + chairmenGroup = append(chairmenGroup, "kassor." + dnSplit[1][3:] + "@chalmers.it") + } + return chairmenGroup, nil +} + +func (s ServiceLDAP) getChairmenInCommitteesGroup() ([]string, error) { + searchRequest := ldap.NewSearchRequest( + "ou=fkit,ou=groups,dc=chalmers,dc=it", // The base dn to search + ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, + "(&(objectClass=itPosition)(cn=ordf))", // The filter to apply + []string{"cn"}, // A list attributes to retrieve + nil, + ) + + result, err := s.Connection.Search(searchRequest) + if err != nil { + return nil, err + } + + var chairmenInCommitteeGroup []string + + for _, entry := range result.Entries { + gtype, err := dnPositionType(s, entry.DN) + if err != nil { + return nil, err + } + if gtype == "Committee" { + dnSplit := strings.SplitN(entry.DN, ",", 3) + chairmenInCommitteeGroup = append(chairmenInCommitteeGroup, "ordf." + dnSplit[1][3:] + "@chalmers.it") + } + + } + return chairmenInCommitteeGroup, nil +} + + func findEntry(ldapEntries []*ldap.Entry, DN string) *ldap.Entry { for _, entry := range ldapEntries { if entry.DN == DN { From e5997dd8121f9b42a6a8c95c98ea62a1be4ca329 Mon Sep 17 00:00:00 2001 From: Gurgy Date: Thu, 17 May 2018 10:29:18 +0200 Subject: [PATCH 31/64] Add deeper functionality for additions --- cmd/main.go | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index 63e6e45..07361a9 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -80,14 +80,65 @@ func main() { fmt.Printf("%d usersAdditions and %d groupAdditions collected.\n", len(additionUsers), len(additionGroups)) fmt.Print("Adding groups... ") for _, group := range additionGroups { - if !providerGroups.Contains(group.Email) { + found := false + for i, pgroup := range providerGroups { + if pgroup.Email == group.Email { + found = true + + for _, alias := range group.Aliases { + aliasFound := false + for _, other := range pgroup.Aliases { + if other == alias { + aliasFound = true + } + } + if !aliasFound { + providerGroups[i].Aliases = append(pgroup.Aliases, alias) + } + } + + for _, member := range group.Members { + memberFound := false + for _, other := range pgroup.Members { + if other == member { + memberFound = true + } + } + if !memberFound { + providerGroups[i].Members = append(pgroup.Members, member) + } + } + } + } + if !found { providerGroups = append(providerGroups, group) } } fmt.Println("Done!") fmt.Print("Adding users... ") for _, user := range additionUsers { - if !providerUsers.Contains(user.Cid) { + found := false + for i, pUser := range providerUsers { + if pUser.Cid == user.Cid { + found = true + if user.Nick != "" { + providerUsers[i].Nick = user.Nick + } + if user.SecondName != "" { + providerUsers[i].SecondName = user.SecondName + } + if user.FirstName != "" { + providerUsers[i].FirstName = user.FirstName + } + if user.Mail != "" { + providerUsers[i].Mail = user.Mail + } + if user.GdprEducation { + providerUsers[i].GdprEducation = user.GdprEducation + } + } + } + if !found { providerUsers = append(providerUsers, user) } } From 94f362de61735486a638eafda213d167ead99bd6 Mon Sep 17 00:00:00 2001 From: Gurgy Date: Thu, 17 May 2018 14:15:05 +0200 Subject: [PATCH 32/64] Check and handle duplicates --- cmd/main.go | 3 +++ duplicates.go | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 duplicates.go diff --git a/cmd/main.go b/cmd/main.go index 07361a9..a44df74 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -147,6 +147,9 @@ func main() { fmt.Println("Skipping additions") } + // Check for and handle duplicates + providerUsers, providerGroups = goldapps.CheckDuplicates(providerUsers, providerGroups) + groupChanges := goldapps.GroupActions{} if !flags.onlyUsers { fmt.Println("Colculating difference between the consumer and provider groups.") diff --git a/duplicates.go b/duplicates.go new file mode 100644 index 0000000..085f9cf --- /dev/null +++ b/duplicates.go @@ -0,0 +1,74 @@ +package goldapps + +import ( + "strings" +) + +func CheckDuplicates(users Users, groups Groups) (Users, Groups) { + + // User <-> Group + for i, user := range users { + for j, group := range groups { + if strings.ToLower(user.Cid+"@chalmers.it") == strings.ToLower(group.Email) { // check cid with group mail + panic(user.Cid + "@chalmers.it" + "==" + group.Email) // panic, this is bad + } + if strings.ToLower(user.Nick+"@chalmers.it") == strings.ToLower(group.Email) { // check nick with group mail + if len(group.Members) == 1 && strings.SplitN(group.Members[0], "@", 2)[1] != "chalmers.it" { // special case for digit pateter. + // Remove element without breaking the loop? + groups = append(append(groups[:j], groups[len(groups)-1]), groups[j+1:len(groups)-1]...) + } else { + users[i].Nick = "" // Simply remove the conflicting Nick + } + } + + for _, alias := range group.Aliases { + if strings.ToLower(user.Cid+"@chalmers.it") == strings.ToLower(alias) { // check cid with all aliases + panic(user.Cid + "@chalmers.it" + "==" + group.Email) // panic, this is bad + } + if strings.ToLower(user.Nick+"@chalmers.it") == strings.ToLower(alias) { // check nick with all aliases + users[i].Nick = "" // Remove nick because it's stupid + } + } + } + } + + // User <-> User + for i, user := range users { + for j, other := range users { + if i != j { // Don't check with itself + if strings.ToLower(user.Cid) == strings.ToLower(other.Cid) { // cid vs cid + panic("two users with cid: " + user.Cid) // this shouldn't happen + } + if strings.ToLower(user.Nick) == strings.ToLower(other.Nick) { // don't compete over nicks + users[i].Nick = "" + users[j].Nick = "" + } + if strings.ToLower(user.Cid) == strings.ToLower(other.Nick) { // cid vs nick + users[j].Nick = "" // cid takes precedence + } + } + } + } + + // Group <-> group + for i, group := range groups { + for j, other := range groups { + if i != j { // don't check with itself + if strings.ToLower(group.Email) == strings.ToLower(other.Email) { // mail vs mail + panic("two groups with email: " + group.Email) // panic, something is set up wrong + } + for _, alias := range group.Aliases { + if strings.ToLower(alias) == strings.ToLower(other.Email) { //email vs alias + panic("two groups with alias/email: " + group.Email + ", " + other.Email) // panic, something is set up wrong + } + for _, oalias := range other.Aliases { + if strings.ToLower(alias) == strings.ToLower(oalias) { // alias vs alias + panic("two groups with alias: " + alias) // panic, something is set up wrong + } + } + } + } + } + } + return users, groups +} From b1d9468242c4142e8d03fcca7380c087754acdc1 Mon Sep 17 00:00:00 2001 From: Gurgy Date: Thu, 17 May 2018 14:15:23 +0200 Subject: [PATCH 33/64] Remove equals check for type --- group.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/group.go b/group.go index e66504f..64148a2 100644 --- a/group.go +++ b/group.go @@ -28,9 +28,7 @@ func (group Group) equals(other Group) bool { if strings.ToLower(group.Email) != strings.ToLower(other.Email) { return false } - if group.Type != other.Type { - return false - } + if len(group.Members) != len(other.Members) { return false } From 7f3ef0b7b569453f3b1ccce0311dcf02e528c762 Mon Sep 17 00:00:00 2001 From: Gurgy Date: Thu, 17 May 2018 14:36:25 +0200 Subject: [PATCH 34/64] Update email text --- admin/service.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin/service.go b/admin/service.go index 5d2c3af..40bc6ac 100644 --- a/admin/service.go +++ b/admin/service.go @@ -21,7 +21,7 @@ const gdprSuspensionText = "You have not attended the GDPR education!" const googleDuplicateEntryError = "googleapi: Error 409: Entity already exists., duplicate" -const passwordMailBody = "Here is your password: %s" +const passwordMailBody = "Action required! You are a member of a committee at the IT-section and have therefor been provided a google-account by the section. Login within the following week to setup two-factor-authentication or you might get locked out from your account. You can login on any google service such as gmail.google.com or drive.google.com with cid@chalmers.it and your provided password: %s" const passwordMailSubject = "Login details for google services at chalmers.it" // my_customer seems to work... From b91daf3985d5346c80d61794a073ae7581e3c57c Mon Sep 17 00:00:00 2001 From: Gurgy Date: Sat, 18 Aug 2018 15:01:19 +0200 Subject: [PATCH 35/64] Fix concurrent modification error --- duplicates.go | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/duplicates.go b/duplicates.go index 085f9cf..2d0a5c9 100644 --- a/duplicates.go +++ b/duplicates.go @@ -8,22 +8,25 @@ func CheckDuplicates(users Users, groups Groups) (Users, Groups) { // User <-> Group for i, user := range users { - for j, group := range groups { - if strings.ToLower(user.Cid+"@chalmers.it") == strings.ToLower(group.Email) { // check cid with group mail - panic(user.Cid + "@chalmers.it" + "==" + group.Email) // panic, this is bad + deleted := 0 + for j := range groups { + k := j - deleted + if strings.ToLower(user.Cid+"@chalmers.it") == strings.ToLower(groups[k].Email) { // check cid with group mail + panic(user.Cid + "@chalmers.it" + "==" + groups[k].Email) // panic, this is bad } - if strings.ToLower(user.Nick+"@chalmers.it") == strings.ToLower(group.Email) { // check nick with group mail - if len(group.Members) == 1 && strings.SplitN(group.Members[0], "@", 2)[1] != "chalmers.it" { // special case for digit pateter. - // Remove element without breaking the loop? - groups = append(append(groups[:j], groups[len(groups)-1]), groups[j+1:len(groups)-1]...) + if strings.ToLower(user.Nick+"@chalmers.it") == strings.ToLower(groups[k].Email) { // check nick with group mail + if len(groups[k].Members) == 1 && strings.SplitN(groups[k].Members[0], "@", 2)[1] != "chalmers.it" { // special case for digit pateter. + // Remove element without breaking the loop or list + groups = groups[:k+copy(groups[k:], groups[k+1:])] + deleted++ } else { users[i].Nick = "" // Simply remove the conflicting Nick } } - for _, alias := range group.Aliases { + for _, alias := range groups[k].Aliases { if strings.ToLower(user.Cid+"@chalmers.it") == strings.ToLower(alias) { // check cid with all aliases - panic(user.Cid + "@chalmers.it" + "==" + group.Email) // panic, this is bad + panic(user.Cid + "@chalmers.it" + "==" + groups[k].Email) // panic, this is bad } if strings.ToLower(user.Nick+"@chalmers.it") == strings.ToLower(alias) { // check nick with all aliases users[i].Nick = "" // Remove nick because it's stupid From b17f28a02777428a7bdb1fe6b51ff6a057058fe6 Mon Sep 17 00:00:00 2001 From: Gurgy Date: Sat, 18 Aug 2018 15:01:52 +0200 Subject: [PATCH 36/64] Delete before adding --- group_action.go | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/group_action.go b/group_action.go index 04bf739..ae40c03 100644 --- a/group_action.go +++ b/group_action.go @@ -21,17 +21,32 @@ func (actions GroupActions) Commit(service UpdateService) (GroupActions, error) performedActions := GroupActions{} + if len(actions.Deletions) > 0 { + fmt.Println("(Groups) Performing deletions") + } + for _, group := range actions.Deletions { + err := service.DeleteGroup(group) + if err != nil { + fmt.Println() + return performedActions, err + } + + performedActions.Deletions = append(performedActions.Deletions, group) + printProgress(len(performedActions.Deletions), len(actions.Deletions)) + } + if len(actions.Updates) > 0 { fmt.Println("(Groups) Performing updates") } for _, update := range actions.Updates { err := service.UpdateGroup(update) if err != nil { - fmt.Println() - return performedActions, err + fmt.Println(update) + fmt.Println(err) + //return performedActions, err + } else { + performedActions.Updates = append(performedActions.Updates, update) } - - performedActions.Updates = append(performedActions.Updates, update) printProgress(len(performedActions.Updates), len(actions.Updates)) } @@ -41,7 +56,7 @@ func (actions GroupActions) Commit(service UpdateService) (GroupActions, error) for _, group := range actions.Additions { err := service.AddGroup(group) if err != nil { - fmt.Println() + fmt.Println(group) return performedActions, err } @@ -49,20 +64,6 @@ func (actions GroupActions) Commit(service UpdateService) (GroupActions, error) printProgress(len(performedActions.Additions), len(actions.Additions)) } - if len(actions.Deletions) > 0 { - fmt.Println("(Groups) Performing deletions") - } - for _, group := range actions.Deletions { - err := service.DeleteGroup(group) - if err != nil { - fmt.Println() - return performedActions, err - } - - performedActions.Deletions = append(performedActions.Deletions, group) - printProgress(len(performedActions.Deletions), len(actions.Deletions)) - } - return performedActions, nil } From a2ca899f904e8e6dacdd75f4c5f2a33eb7adf786 Mon Sep 17 00:00:00 2001 From: Gurgy Date: Sat, 18 Aug 2018 15:02:20 +0200 Subject: [PATCH 37/64] Print errors on newline --- cmd/main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index a44df74..1c3ded3 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -199,14 +199,14 @@ func main() { fmt.Printf("\t\t Performed %d out of %d Additions\n", len(groupChangesPerformed.Additions), len(groupChanges.Additions)) fmt.Printf("\t\t Performed %d out of %d Deletions\n", len(groupChangesPerformed.Deletions), len(groupChanges.Deletions)) fmt.Printf("\t\t Performed %d out of %d Updates\n", len(groupChangesPerformed.Updates), len(groupChanges.Updates)) - fmt.Printf("\t\t Error: %s", err1.Error()) + fmt.Printf("\t\t Error: %s\n", err1.Error()) } if err2 != nil { fmt.Printf("\t For users:\n") fmt.Printf("\t\t Performed %d out of %d Additions\n", len(userChangesPerformed.Additions), len(userChanges.Additions)) fmt.Printf("\t\t Performed %d out of %d Deletions\n", len(userChangesPerformed.Deletions), len(userChanges.Deletions)) fmt.Printf("\t\t Performed %d out of %d Updates\n", len(userChangesPerformed.Updates), len(userChanges.Updates)) - fmt.Printf("\t\t Error: %s", err2.Error()) + fmt.Printf("\t\t Error: %s\n", err2.Error()) } } } From f597eb70b6e566a25eb58c3911c320cbb23f45f6 Mon Sep 17 00:00:00 2001 From: Gurgy Date: Sat, 18 Aug 2018 15:02:59 +0200 Subject: [PATCH 38/64] Remove symbols from passwords as they destroy mailing service --- admin/password.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/admin/password.go b/admin/password.go index 134b546..fec17b4 100644 --- a/admin/password.go +++ b/admin/password.go @@ -7,8 +7,8 @@ import ( func newPassword() string { numbers := rand.Intn(10) + 5 - symbols := rand.Intn(10) + 5 - pass, err := password.Generate(64, numbers, symbols, false, true) + //symbols := rand.Intn(10) + 5 + pass, err := password.Generate(64, numbers, 0, false, true) if err != nil { panic("Password generation failed") } From f3e0508eb75dd828234f7e95091826f31d05bd94 Mon Sep 17 00:00:00 2001 From: Gurgy Date: Sat, 18 Aug 2018 15:03:40 +0200 Subject: [PATCH 39/64] formating --- admin/scope.go | 3 ++- ldap/service.go | 45 ++++++++++++++++++++++----------------------- services.go | 2 +- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/admin/scope.go b/admin/scope.go index 0c220d0..9e85ad0 100644 --- a/admin/scope.go +++ b/admin/scope.go @@ -12,4 +12,5 @@ func Scopes() []string { gmail.GmailSendScope, } } -// https://www.googleapis.com/auth/admin.directory.group, https://www.googleapis.com/auth/admin.directory.user \ No newline at end of file + +// https://www.googleapis.com/auth/admin.directory.group, https://www.googleapis.com/auth/admin.directory.user diff --git a/ldap/service.go b/ldap/service.go index a57cbd2..8127573 100644 --- a/ldap/service.go +++ b/ldap/service.go @@ -246,7 +246,7 @@ func (s ServiceLDAP) GetGroups() ([]goldapps.Group, error) { return nil, err } groups = append(groups, goldapps.Group{ - Email: "ordforanden@chalmers.it", + Email: "ordforanden@chalmers.it", Members: chairmenGroupMembers, }) @@ -255,7 +255,7 @@ func (s ServiceLDAP) GetGroups() ([]goldapps.Group, error) { return nil, err } groups = append(groups, goldapps.Group{ - Email: "ordforanden.kommitteer@chalmers.it", + Email: "ordforanden.kommitteer@chalmers.it", Members: chairmenInCommitteesGroupMembers, }) @@ -264,7 +264,7 @@ func (s ServiceLDAP) GetGroups() ([]goldapps.Group, error) { return nil, err } groups = append(groups, goldapps.Group{ - Email: "kassorer@chalmers.it", + Email: "kassorer@chalmers.it", Members: treasurersGroupMembers, }) @@ -410,13 +410,11 @@ func (s ServiceLDAP) getPositionGroups() ([]goldapps.Group, error) { var positionGroups []goldapps.Group - - searchRequest := ldap.NewSearchRequest( "ou=fkit,ou=groups,dc=chalmers,dc=it", // The base dn to search ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, - "(&(objectClass=itPosition))", // The filter to apply - []string{"cn", "member"}, // A list attributes to retrieve + "(&(objectClass=itPosition))", // The filter to apply + []string{"cn", "member"}, // A list attributes to retrieve nil, ) @@ -435,16 +433,18 @@ func (s ServiceLDAP) getPositionGroups() ([]goldapps.Group, error) { } posGroup := goldapps.Group{ - Email: fmt.Sprintf("%s.%s@chalmers.it", pos, grp), - Type: groupType + "Direct", + Email: fmt.Sprintf("%s.%s@chalmers.it", pos, grp), + Type: groupType + "Direct", } for _, member := range entry.GetAttributeValues("member") { - mail := findEntry(users, member).GetAttributeValue("mail") + ent := findEntry(users, member) + // TODO detta krashar om användaren ej finns + mail := ent.GetAttributeValue("mail") posGroup.Members = append(posGroup.Members, mail) } - positionGroups = append(positionGroups,posGroup) + positionGroups = append(positionGroups, posGroup) } return positionGroups, nil @@ -455,8 +455,8 @@ func (s ServiceLDAP) getChairmenGroup() ([]string, error) { searchRequest := ldap.NewSearchRequest( "ou=fkit,ou=groups,dc=chalmers,dc=it", // The base dn to search ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, - "(&(objectClass=itPosition)(cn=ordf))", // The filter to apply - []string{"cn"}, // A list attributes to retrieve + "(&(objectClass=itPosition)(cn=ordf))", // The filter to apply + []string{"cn"}, // A list attributes to retrieve nil, ) @@ -469,7 +469,7 @@ func (s ServiceLDAP) getChairmenGroup() ([]string, error) { for _, entry := range result.Entries { dnSplit := strings.SplitN(entry.DN, ",", 3) - chairmenGroup = append(chairmenGroup, "ordf." + dnSplit[1][3:] + "@chalmers.it") + chairmenGroup = append(chairmenGroup, "ordf."+dnSplit[1][3:]+"@chalmers.it") } return chairmenGroup, nil } @@ -478,8 +478,8 @@ func (s ServiceLDAP) getTreasurersGroup() ([]string, error) { searchRequest := ldap.NewSearchRequest( "ou=fkit,ou=groups,dc=chalmers,dc=it", // The base dn to search ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, - "(&(objectClass=itPosition)(cn=kassor))", // The filter to apply - []string{"cn"}, // A list attributes to retrieve + "(&(objectClass=itPosition)(cn=kassor))", // The filter to apply + []string{"cn"}, // A list attributes to retrieve nil, ) @@ -492,7 +492,7 @@ func (s ServiceLDAP) getTreasurersGroup() ([]string, error) { for _, entry := range result.Entries { dnSplit := strings.SplitN(entry.DN, ",", 3) - chairmenGroup = append(chairmenGroup, "kassor." + dnSplit[1][3:] + "@chalmers.it") + chairmenGroup = append(chairmenGroup, "kassor."+dnSplit[1][3:]+"@chalmers.it") } return chairmenGroup, nil } @@ -501,8 +501,8 @@ func (s ServiceLDAP) getChairmenInCommitteesGroup() ([]string, error) { searchRequest := ldap.NewSearchRequest( "ou=fkit,ou=groups,dc=chalmers,dc=it", // The base dn to search ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, - "(&(objectClass=itPosition)(cn=ordf))", // The filter to apply - []string{"cn"}, // A list attributes to retrieve + "(&(objectClass=itPosition)(cn=ordf))", // The filter to apply + []string{"cn"}, // A list attributes to retrieve nil, ) @@ -520,14 +520,13 @@ func (s ServiceLDAP) getChairmenInCommitteesGroup() ([]string, error) { } if gtype == "Committee" { dnSplit := strings.SplitN(entry.DN, ",", 3) - chairmenInCommitteeGroup = append(chairmenInCommitteeGroup, "ordf." + dnSplit[1][3:] + "@chalmers.it") + chairmenInCommitteeGroup = append(chairmenInCommitteeGroup, "ordf."+dnSplit[1][3:]+"@chalmers.it") } } return chairmenInCommitteeGroup, nil } - func findEntry(ldapEntries []*ldap.Entry, DN string) *ldap.Entry { for _, entry := range ldapEntries { if entry.DN == DN { @@ -554,8 +553,8 @@ func dnPositionType(s ServiceLDAP, DN string) (string, error) { sr := ldap.NewSearchRequest( newDN, // The base dn to search ldap.ScopeBaseObject, ldap.NeverDerefAliases, 0, 0, false, - "(&(objectClass=*))", // The filter to apply - []string{"type"}, // A list attributes to retrieve + "(&(objectClass=*))", // The filter to apply + []string{"type"}, // A list attributes to retrieve nil, ) diff --git a/services.go b/services.go index d27d0eb..2d630ca 100644 --- a/services.go +++ b/services.go @@ -12,5 +12,5 @@ type UpdateService interface { type CollectionService interface { GetGroups() ([]Group, error) - GetUsers() ([]User, error) + GetUsers() ([]User, error) } From 29a6ee086cb9d3f0885027690f83a2a3efd95323 Mon Sep 17 00:00:00 2001 From: Gurgy Date: Sat, 18 Aug 2018 15:04:59 +0200 Subject: [PATCH 40/64] Do not check gdpr suspension --- user.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/user.go b/user.go index 7954536..74e4cb8 100644 --- a/user.go +++ b/user.go @@ -45,9 +45,9 @@ func (user User) equals(other User) bool { return false }*/ - if user.GdprEducation != other.GdprEducation { + /*if user.GdprEducation != other.GdprEducation { return false - } + }*/ /* Do not check PasswordHash nor HashFunction From 21a8e4c4f142e8b4742786c51303e0dcd5a85fef Mon Sep 17 00:00:00 2001 From: Gurgy Date: Sat, 18 Aug 2018 16:07:03 +0200 Subject: [PATCH 41/64] Restructure project --- Dockerfile | 2 +- cmd/chalmers.it.config.toml => chalmers.it.config.toml | 0 cmd/example.config.toml => example.config.toml | 0 .../github.com/cthit/goldapps/admin}/password.go | 0 .../github.com/cthit/goldapps/admin}/scope.go | 0 .../github.com/cthit/goldapps/admin}/service.go | 2 +- {cmd => src/github.com/cthit/goldapps/cmd}/config.go | 0 {cmd => src/github.com/cthit/goldapps/cmd}/flags.go | 0 {cmd => src/github.com/cthit/goldapps/cmd}/ldap.go | 2 +- {cmd => src/github.com/cthit/goldapps/cmd}/main.go | 10 ++++++---- {cmd => src/github.com/cthit/goldapps/cmd}/util.go | 0 .../github.com/cthit/goldapps/duplicates.go | 0 group.go => src/github.com/cthit/goldapps/group.go | 0 .../github.com/cthit/goldapps/group_action.go | 0 {json => src/github.com/cthit/goldapps/json}/data.go | 2 +- .../github.com/cthit/goldapps/json}/service.go | 2 +- .../github.com/cthit/goldapps/ldap}/service.go | 2 +- .../github.com/cthit/goldapps/services.go | 0 user.go => src/github.com/cthit/goldapps/user.go | 0 .../github.com/cthit/goldapps/user_action.go | 0 util.go => src/github.com/cthit/goldapps/util.go | 0 21 files changed, 12 insertions(+), 10 deletions(-) rename cmd/chalmers.it.config.toml => chalmers.it.config.toml (100%) rename cmd/example.config.toml => example.config.toml (100%) rename {admin => src/github.com/cthit/goldapps/admin}/password.go (100%) rename {admin => src/github.com/cthit/goldapps/admin}/scope.go (100%) rename {admin => src/github.com/cthit/goldapps/admin}/service.go (99%) rename {cmd => src/github.com/cthit/goldapps/cmd}/config.go (100%) rename {cmd => src/github.com/cthit/goldapps/cmd}/flags.go (100%) rename {cmd => src/github.com/cthit/goldapps/cmd}/ldap.go (97%) rename {cmd => src/github.com/cthit/goldapps/cmd}/main.go (99%) rename {cmd => src/github.com/cthit/goldapps/cmd}/util.go (100%) rename duplicates.go => src/github.com/cthit/goldapps/duplicates.go (100%) rename group.go => src/github.com/cthit/goldapps/group.go (100%) rename group_action.go => src/github.com/cthit/goldapps/group_action.go (100%) rename {json => src/github.com/cthit/goldapps/json}/data.go (76%) rename {json => src/github.com/cthit/goldapps/json}/service.go (99%) rename {ldap => src/github.com/cthit/goldapps/ldap}/service.go (99%) rename services.go => src/github.com/cthit/goldapps/services.go (100%) rename user.go => src/github.com/cthit/goldapps/user.go (100%) rename user_action.go => src/github.com/cthit/goldapps/user_action.go (100%) rename util.go => src/github.com/cthit/goldapps/util.go (100%) diff --git a/Dockerfile b/Dockerfile index 50d8c89..006ae10 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,7 +9,7 @@ RUN apk add --update git # Copy sources RUN mkdir -p $GOPATH/src/github.com/cthit/goldapps -COPY . $GOPATH/src/github.com/cthit/goldapps +COPY ./src/github.com/cthit/goldapps $GOPATH/src/github.com/cthit/goldapps WORKDIR $GOPATH/src/github.com/cthit/goldapps/cmd # Grab dependencies diff --git a/cmd/chalmers.it.config.toml b/chalmers.it.config.toml similarity index 100% rename from cmd/chalmers.it.config.toml rename to chalmers.it.config.toml diff --git a/cmd/example.config.toml b/example.config.toml similarity index 100% rename from cmd/example.config.toml rename to example.config.toml diff --git a/admin/password.go b/src/github.com/cthit/goldapps/admin/password.go similarity index 100% rename from admin/password.go rename to src/github.com/cthit/goldapps/admin/password.go diff --git a/admin/scope.go b/src/github.com/cthit/goldapps/admin/scope.go similarity index 100% rename from admin/scope.go rename to src/github.com/cthit/goldapps/admin/scope.go diff --git a/admin/service.go b/src/github.com/cthit/goldapps/admin/service.go similarity index 99% rename from admin/service.go rename to src/github.com/cthit/goldapps/admin/service.go index 40bc6ac..60110fe 100644 --- a/admin/service.go +++ b/src/github.com/cthit/goldapps/admin/service.go @@ -1,7 +1,7 @@ package admin import ( - "github.com/cthit/goldapps" + "../../goldapps" "google.golang.org/api/admin/directory/v1" // Imports as admin "google.golang.org/api/gmail/v1" // Imports as gmail diff --git a/cmd/config.go b/src/github.com/cthit/goldapps/cmd/config.go similarity index 100% rename from cmd/config.go rename to src/github.com/cthit/goldapps/cmd/config.go diff --git a/cmd/flags.go b/src/github.com/cthit/goldapps/cmd/flags.go similarity index 100% rename from cmd/flags.go rename to src/github.com/cthit/goldapps/cmd/flags.go diff --git a/cmd/ldap.go b/src/github.com/cthit/goldapps/cmd/ldap.go similarity index 97% rename from cmd/ldap.go rename to src/github.com/cthit/goldapps/cmd/ldap.go index b4e3395..e05d95b 100644 --- a/cmd/ldap.go +++ b/src/github.com/cthit/goldapps/cmd/ldap.go @@ -1,7 +1,7 @@ package main import ( - "github.com/cthit/goldapps/ldap" + "../ldap" "github.com/spf13/viper" ) diff --git a/cmd/main.go b/src/github.com/cthit/goldapps/cmd/main.go similarity index 99% rename from cmd/main.go rename to src/github.com/cthit/goldapps/cmd/main.go index 1c3ded3..9318d02 100644 --- a/cmd/main.go +++ b/src/github.com/cthit/goldapps/cmd/main.go @@ -2,11 +2,13 @@ package main import ( "fmt" - "github.com/cthit/goldapps" - "github.com/cthit/goldapps/admin" - "github.com/cthit/goldapps/json" - "github.com/spf13/viper" "regexp" + + "github.com/spf13/viper" + + "../../goldapps" + "../admin" + "../json" ) func init() { diff --git a/cmd/util.go b/src/github.com/cthit/goldapps/cmd/util.go similarity index 100% rename from cmd/util.go rename to src/github.com/cthit/goldapps/cmd/util.go diff --git a/duplicates.go b/src/github.com/cthit/goldapps/duplicates.go similarity index 100% rename from duplicates.go rename to src/github.com/cthit/goldapps/duplicates.go diff --git a/group.go b/src/github.com/cthit/goldapps/group.go similarity index 100% rename from group.go rename to src/github.com/cthit/goldapps/group.go diff --git a/group_action.go b/src/github.com/cthit/goldapps/group_action.go similarity index 100% rename from group_action.go rename to src/github.com/cthit/goldapps/group_action.go diff --git a/json/data.go b/src/github.com/cthit/goldapps/json/data.go similarity index 76% rename from json/data.go rename to src/github.com/cthit/goldapps/json/data.go index c2caf6c..848a007 100644 --- a/json/data.go +++ b/src/github.com/cthit/goldapps/json/data.go @@ -1,6 +1,6 @@ package json -import "github.com/cthit/goldapps" +import "../../goldapps" type data struct { Groups []goldapps.Group `json:"groups"` diff --git a/json/service.go b/src/github.com/cthit/goldapps/json/service.go similarity index 99% rename from json/service.go rename to src/github.com/cthit/goldapps/json/service.go index 34d6489..4ec4c26 100644 --- a/json/service.go +++ b/src/github.com/cthit/goldapps/json/service.go @@ -3,7 +3,7 @@ package json import ( "encoding/json" "fmt" - "github.com/cthit/goldapps" + "../../goldapps" "io/ioutil" ) diff --git a/ldap/service.go b/src/github.com/cthit/goldapps/ldap/service.go similarity index 99% rename from ldap/service.go rename to src/github.com/cthit/goldapps/ldap/service.go index 8127573..0f228a7 100644 --- a/ldap/service.go +++ b/src/github.com/cthit/goldapps/ldap/service.go @@ -4,7 +4,7 @@ import ( "crypto/tls" "fmt" - "github.com/cthit/goldapps" + "../../goldapps" "gopkg.in/ldap.v2" "strings" ) diff --git a/services.go b/src/github.com/cthit/goldapps/services.go similarity index 100% rename from services.go rename to src/github.com/cthit/goldapps/services.go diff --git a/user.go b/src/github.com/cthit/goldapps/user.go similarity index 100% rename from user.go rename to src/github.com/cthit/goldapps/user.go diff --git a/user_action.go b/src/github.com/cthit/goldapps/user_action.go similarity index 100% rename from user_action.go rename to src/github.com/cthit/goldapps/user_action.go diff --git a/util.go b/src/github.com/cthit/goldapps/util.go similarity index 100% rename from util.go rename to src/github.com/cthit/goldapps/util.go From 9c0c1a7fbe9ff410e8ab0da6d21b5f8b7c30edd4 Mon Sep 17 00:00:00 2001 From: Gurgy Date: Sat, 18 Aug 2018 17:14:47 +0200 Subject: [PATCH 42/64] make sure deletions doesn't break loop --- src/github.com/cthit/goldapps/duplicates.go | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/github.com/cthit/goldapps/duplicates.go b/src/github.com/cthit/goldapps/duplicates.go index 2d0a5c9..2b81032 100644 --- a/src/github.com/cthit/goldapps/duplicates.go +++ b/src/github.com/cthit/goldapps/duplicates.go @@ -8,17 +8,15 @@ func CheckDuplicates(users Users, groups Groups) (Users, Groups) { // User <-> Group for i, user := range users { - deleted := 0 - for j := range groups { - k := j - deleted + for k := 0; k < len(groups); k++ { if strings.ToLower(user.Cid+"@chalmers.it") == strings.ToLower(groups[k].Email) { // check cid with group mail panic(user.Cid + "@chalmers.it" + "==" + groups[k].Email) // panic, this is bad } if strings.ToLower(user.Nick+"@chalmers.it") == strings.ToLower(groups[k].Email) { // check nick with group mail if len(groups[k].Members) == 1 && strings.SplitN(groups[k].Members[0], "@", 2)[1] != "chalmers.it" { // special case for digit pateter. - // Remove element without breaking the loop or list - groups = groups[:k+copy(groups[k:], groups[k+1:])] - deleted++ + + groups = remove(groups, k) + k-- // dont breaking the loop } else { users[i].Nick = "" // Simply remove the conflicting Nick } @@ -75,3 +73,8 @@ func CheckDuplicates(users Users, groups Groups) (Users, Groups) { } return users, groups } + +func remove(s Groups, i int) Groups { + s[len(s)-1], s[i] = s[i], s[len(s)-1] + return s[:len(s)-1] +} \ No newline at end of file From cc3c369af5a0ace9533f903a403613b6e6b97e58 Mon Sep 17 00:00:00 2001 From: Gurgy Date: Sat, 18 Aug 2018 17:15:19 +0200 Subject: [PATCH 43/64] Don't crash if target file doe snot exist --- src/github.com/cthit/goldapps/json/data.go | 8 --- src/github.com/cthit/goldapps/json/service.go | 68 +++++++++++++------ 2 files changed, 47 insertions(+), 29 deletions(-) delete mode 100644 src/github.com/cthit/goldapps/json/data.go diff --git a/src/github.com/cthit/goldapps/json/data.go b/src/github.com/cthit/goldapps/json/data.go deleted file mode 100644 index 848a007..0000000 --- a/src/github.com/cthit/goldapps/json/data.go +++ /dev/null @@ -1,8 +0,0 @@ -package json - -import "../../goldapps" - -type data struct { - Groups []goldapps.Group `json:"groups"` - Users []goldapps.User `json:"users"` -} diff --git a/src/github.com/cthit/goldapps/json/service.go b/src/github.com/cthit/goldapps/json/service.go index 4ec4c26..02c1eb1 100644 --- a/src/github.com/cthit/goldapps/json/service.go +++ b/src/github.com/cthit/goldapps/json/service.go @@ -5,12 +5,18 @@ import ( "fmt" "../../goldapps" "io/ioutil" + "os" ) type Service struct { path string } +type dataObject struct { + Groups []goldapps.Group `json:"groups"` + Users []goldapps.User `json:"users"` +} + func (s Service) DeleteUser(user goldapps.User) error { groups, err := s.GetGroups() if err != nil { @@ -23,7 +29,7 @@ func (s Service) DeleteUser(user goldapps.User) error { for i, u := range users { if u.Cid == user.Cid { - err = s.save(data{ + err = s.save(dataObject{ groups, append(users[:i], users[i+1:]...), }) @@ -45,7 +51,7 @@ func (s Service) UpdateUser(update goldapps.UserUpdate) error { for i, u := range users { if u.Cid == update.Before.Cid { - err = s.save(data{ + err = s.save(dataObject{ groups, append(append(users[:i], update.After), users[i+1:]...), }) @@ -67,7 +73,7 @@ func (s Service) AddUser(user goldapps.User) error { users = append(users, user) - err = s.save(data{ + err = s.save(dataObject{ groups, users, }) @@ -76,13 +82,7 @@ func (s Service) AddUser(user goldapps.User) error { func (s Service) GetUsers() ([]goldapps.User, error) { - bytes, err := ioutil.ReadFile(s.path) - if err != nil { - return nil, err - } - - var data data - err = json.Unmarshal(bytes, &data) + data, err := s.get() if err != nil { return nil, err } @@ -91,18 +91,50 @@ func (s Service) GetUsers() ([]goldapps.User, error) { } func NewJsonService(path string) (Service, error) { + + // Check if file exists + _, err := os.Stat(path) + if os.IsNotExist(err) { + // Create file + _, err := os.Create("path") + if err != nil { + return Service{}, err + } + // Write empty object to file + err = Service{path:path}.save(dataObject{}) + if err != nil { + return Service{}, err + } + } + return Service{ path: path, }, nil } -func (s Service) save(data data) error { +func (s Service) save(data dataObject) error { json, _ := json.Marshal(data) err := ioutil.WriteFile(s.path, json, 0666) return err } +func (s Service) get() (dataObject, error) { + + bytes, err := ioutil.ReadFile(s.path) + if err != nil { + return dataObject{}, err + } + + var data dataObject + err = json.Unmarshal(bytes, &data) + if err != nil { + return dataObject{}, err + } + + return data, nil +} + func (s Service) DeleteGroup(group goldapps.Group) error { groups, err := s.GetGroups() if err != nil { @@ -115,7 +147,7 @@ func (s Service) DeleteGroup(group goldapps.Group) error { for i, g := range groups { if g.Email == group.Email { - err = s.save(data{append(groups[:i], groups[i+1:]...), + err = s.save(dataObject{append(groups[:i], groups[i+1:]...), users, }) return err @@ -136,7 +168,7 @@ func (s Service) UpdateGroup(groupUpdate goldapps.GroupUpdate) error { for i, g := range groups { if g.Email == groupUpdate.Before.Email { - err = s.save(data{ + err = s.save(dataObject{ append(append(groups[:i], groupUpdate.After), groups[i+1:]...), users, }) @@ -158,7 +190,7 @@ func (s Service) AddGroup(group goldapps.Group) error { groups = append(groups, group) - err = s.save(data{ + err = s.save(dataObject{ groups, users, }) @@ -167,13 +199,7 @@ func (s Service) AddGroup(group goldapps.Group) error { func (s Service) GetGroups() ([]goldapps.Group, error) { - bytes, err := ioutil.ReadFile(s.path) - if err != nil { - return nil, err - } - - var data data - err = json.Unmarshal(bytes, &data) + data, err := s.get() if err != nil { return nil, err } From a7e492f1c23c92abcbd5fd772a9b3763e1a48c72 Mon Sep 17 00:00:00 2001 From: Gurgy Date: Sat, 18 Aug 2018 22:33:08 +0200 Subject: [PATCH 44/64] Cleanup duplicates script --- src/github.com/cthit/goldapps/cmd/main.go | 4 +- src/github.com/cthit/goldapps/duplicates.go | 118 ++++++++++++++------ src/github.com/cthit/goldapps/group.go | 9 +- 3 files changed, 91 insertions(+), 40 deletions(-) diff --git a/src/github.com/cthit/goldapps/cmd/main.go b/src/github.com/cthit/goldapps/cmd/main.go index 9318d02..1c596c1 100644 --- a/src/github.com/cthit/goldapps/cmd/main.go +++ b/src/github.com/cthit/goldapps/cmd/main.go @@ -6,9 +6,9 @@ import ( "github.com/spf13/viper" - "../../goldapps" "../admin" "../json" + "../../goldapps" ) func init() { @@ -150,7 +150,7 @@ func main() { } // Check for and handle duplicates - providerUsers, providerGroups = goldapps.CheckDuplicates(providerUsers, providerGroups) + providerUsers, providerGroups = goldapps.RemoveDuplicates(providerUsers, providerGroups) groupChanges := goldapps.GroupActions{} if !flags.onlyUsers { diff --git a/src/github.com/cthit/goldapps/duplicates.go b/src/github.com/cthit/goldapps/duplicates.go index 2b81032..e5806a7 100644 --- a/src/github.com/cthit/goldapps/duplicates.go +++ b/src/github.com/cthit/goldapps/duplicates.go @@ -4,67 +4,103 @@ import ( "strings" ) -func CheckDuplicates(users Users, groups Groups) (Users, Groups) { +func RemoveDuplicates(users Users, groups Groups) (Users, Groups) { - // User <-> Group + // Compare Users with Groups for i, user := range users { for k := 0; k < len(groups); k++ { - if strings.ToLower(user.Cid+"@chalmers.it") == strings.ToLower(groups[k].Email) { // check cid with group mail - panic(user.Cid + "@chalmers.it" + "==" + groups[k].Email) // panic, this is bad + // Check if any cid conflicts with any group name + if strings.ToLower(user.Cid) == extractIdentifier(groups[k].Email) { + if groups[k].Expendable { + groups = removeArrayGroup(groups, k) + k-- // don't breaking the loop + } else { + // No good strategy exists, simply panic and let admins handle the situation + // This would probably also cause tremendous problems in other applications + panic(user.Cid + "==" + extractIdentifier(groups[k].Email)) + } } - if strings.ToLower(user.Nick+"@chalmers.it") == strings.ToLower(groups[k].Email) { // check nick with group mail - if len(groups[k].Members) == 1 && strings.SplitN(groups[k].Members[0], "@", 2)[1] != "chalmers.it" { // special case for digit pateter. - - groups = remove(groups, k) - k-- // dont breaking the loop + // Check if any user nick conflicts with any group name + if strings.ToLower(user.Nick) == extractIdentifier(groups[k].Email) { + if groups[k].Expendable { + groups = removeArrayGroup(groups, k) + k-- // don't breaking the loop } else { - users[i].Nick = "" // Simply remove the conflicting Nick + // Nicks are not that important + users[i].Nick = "" } } - for _, alias := range groups[k].Aliases { - if strings.ToLower(user.Cid+"@chalmers.it") == strings.ToLower(alias) { // check cid with all aliases - panic(user.Cid + "@chalmers.it" + "==" + groups[k].Email) // panic, this is bad + for aliasIndex, alias := range groups[k].Aliases { + // Check if any cid conflicts with any group alias + if strings.ToLower(user.Cid) == extractIdentifier(alias) { + if groups[k].Expendable { + groups[k] = removeAlias(groups[k], aliasIndex) + } else { + // No good strategy exists, simply panic and let admins handle the situation + // This would probably also cause tremendous problems in other applications + panic(user.Cid + "== (alias)" + extractIdentifier(groups[k].Email)) + } } - if strings.ToLower(user.Nick+"@chalmers.it") == strings.ToLower(alias) { // check nick with all aliases - users[i].Nick = "" // Remove nick because it's stupid + // Check if any Nick conflicts with any group alias + if strings.ToLower(user.Nick) == extractIdentifier(alias) { + if groups[k].Expendable { + groups[k] = removeAlias(groups[k], aliasIndex) + } else { + // Nicks are not that important + users[i].Nick = "" + } } } } } - // User <-> User + // Compare Users with Users for i, user := range users { - for j, other := range users { - if i != j { // Don't check with itself - if strings.ToLower(user.Cid) == strings.ToLower(other.Cid) { // cid vs cid - panic("two users with cid: " + user.Cid) // this shouldn't happen + for j, otherUser := range users { + // Don't check with itself + if i != j { + // Compare cids + if strings.ToLower(user.Cid) == strings.ToLower(otherUser.Cid) { + // Should not be able to happen + panic("two users with cid: " + user.Cid) } - if strings.ToLower(user.Nick) == strings.ToLower(other.Nick) { // don't compete over nicks + // Compare Nicks + if strings.ToLower(user.Nick) == strings.ToLower(otherUser.Nick) { + // Nicks are not that important users[i].Nick = "" users[j].Nick = "" } - if strings.ToLower(user.Cid) == strings.ToLower(other.Nick) { // cid vs nick - users[j].Nick = "" // cid takes precedence + // Compare cids with nicks + if strings.ToLower(user.Cid) == strings.ToLower(otherUser.Nick) { + // Nicks are not that important + users[j].Nick = "" } } } } - // Group <-> group + // Compare Groups with Groups for i, group := range groups { - for j, other := range groups { - if i != j { // don't check with itself - if strings.ToLower(group.Email) == strings.ToLower(other.Email) { // mail vs mail - panic("two groups with email: " + group.Email) // panic, something is set up wrong + for j, otherGroup := range groups { + // Don't check with itself + if i != j { + // Compare Emails + if strings.ToLower(group.Email) == strings.ToLower(otherGroup.Email) { + // Something is set up wrong + panic("two groups with email: " + group.Email) } for _, alias := range group.Aliases { - if strings.ToLower(alias) == strings.ToLower(other.Email) { //email vs alias - panic("two groups with alias/email: " + group.Email + ", " + other.Email) // panic, something is set up wrong + // Compare emails with aliases + if strings.ToLower(alias) == strings.ToLower(otherGroup.Email) { + // Something is set up wrong + panic("two groups with alias/email: " + group.Email + ", " + otherGroup.Email) } - for _, oalias := range other.Aliases { - if strings.ToLower(alias) == strings.ToLower(oalias) { // alias vs alias - panic("two groups with alias: " + alias) // panic, something is set up wrong + for _, otherAlias := range otherGroup.Aliases { + // Compare aliases with aliases + if strings.ToLower(alias) == strings.ToLower(otherAlias) { + // Something is set up wrong + panic("two groups with alias: " + alias) } } } @@ -74,7 +110,21 @@ func CheckDuplicates(users Users, groups Groups) (Users, Groups) { return users, groups } -func remove(s Groups, i int) Groups { +func removeArrayGroup(s Groups, i int) Groups { + s[len(s)-1], s[i] = s[i], s[len(s)-1] + return s[:len(s)-1] +} + +func removeArrayString(s []string, i int) []string { s[len(s)-1], s[i] = s[i], s[len(s)-1] return s[:len(s)-1] +} + +func removeAlias(group Group, aliasIndex int) Group { + group.Aliases = removeArrayString(group.Aliases, aliasIndex) + return group +} + +func extractIdentifier(email string) string { + return strings.ToLower(strings.Split(email, "@")[0]) } \ No newline at end of file diff --git a/src/github.com/cthit/goldapps/group.go b/src/github.com/cthit/goldapps/group.go index 64148a2..b84604e 100644 --- a/src/github.com/cthit/goldapps/group.go +++ b/src/github.com/cthit/goldapps/group.go @@ -7,10 +7,11 @@ import "strings" // Members is a lost of email addresses that are members of this group. // Aliases are alternative email addresses for the group. type Group struct { - Email string `json:"email"` - Type string `json:"type"` - Members []string `json:"members"` - Aliases []string `json:"aliases"` + Email string `json:"email"` + Type string `json:"type"` + Members []string `json:"members"` + Aliases []string `json:"aliases"` + Expendable bool `json:"expendable"` } type Groups []Group From a2332b06244ef1a58288eb404292eabf626db1fa Mon Sep 17 00:00:00 2001 From: Gurgy Date: Thu, 23 Aug 2018 19:36:06 +0200 Subject: [PATCH 45/64] Refactorations in domain and main package * Split stuff into multiple functions * Split stuff into multiple files * Remove GDPR flag --- .../cthit/goldapps/admin/service.go | 8 - .../cthit/goldapps/cmd/additions.go | 136 +++++++++ src/github.com/cthit/goldapps/cmd/ldap.go | 47 --- src/github.com/cthit/goldapps/cmd/main.go | 276 ++---------------- src/github.com/cthit/goldapps/cmd/services.go | 148 ++++++++++ src/github.com/cthit/goldapps/duplicates.go | 2 +- src/github.com/cthit/goldapps/group.go | 14 +- src/github.com/cthit/goldapps/group_action.go | 126 +++++--- src/github.com/cthit/goldapps/json/service.go | 4 +- src/github.com/cthit/goldapps/ldap/service.go | 21 +- src/github.com/cthit/goldapps/user.go | 21 +- src/github.com/cthit/goldapps/user_action.go | 137 +++++---- src/github.com/cthit/goldapps/util.go | 16 +- 13 files changed, 525 insertions(+), 431 deletions(-) create mode 100644 src/github.com/cthit/goldapps/cmd/additions.go delete mode 100644 src/github.com/cthit/goldapps/cmd/ldap.go create mode 100644 src/github.com/cthit/goldapps/cmd/services.go diff --git a/src/github.com/cthit/goldapps/admin/service.go b/src/github.com/cthit/goldapps/admin/service.go index 60110fe..567d78b 100644 --- a/src/github.com/cthit/goldapps/admin/service.go +++ b/src/github.com/cthit/goldapps/admin/service.go @@ -17,8 +17,6 @@ import ( "time" ) -const gdprSuspensionText = "You have not attended the GDPR education!" - const googleDuplicateEntryError = "googleapi: Error 409: Entity already exists., duplicate" const passwordMailBody = "Action required! You are a member of a committee at the IT-section and have therefor been provided a google-account by the section. Login within the following week to setup two-factor-authentication or you might get locked out from your account. You can login on any google service such as gmail.google.com or drive.google.com with cid@chalmers.it and your provided password: %s" @@ -325,15 +323,11 @@ func (s googleService) GetUsers() ([]goldapps.User, error) { // Extracting cid form (cid@example.ex) cid := strings.Split(adminUser.PrimaryEmail, "@")[0] - // Check suspension and suspension reason to determine GDPR status - gdpr := !(adminUser.Suspended && adminUser.SuspensionReason == gdprSuspensionText) - users[i] = goldapps.User{ Cid: cid, FirstName: firstName, SecondName: adminUser.Name.FamilyName, Nick: nick, - GdprEducation: gdpr, } i++ } @@ -426,7 +420,5 @@ func buildGoldappsUser(user goldapps.User, domain string) *admin.User { }, IncludeInGlobalAddressList: true, PrimaryEmail: fmt.Sprintf("%s@%s", user.Cid, domain), - Suspended: !user.GdprEducation, - SuspensionReason: gdprSuspensionText, } } diff --git a/src/github.com/cthit/goldapps/cmd/additions.go b/src/github.com/cthit/goldapps/cmd/additions.go new file mode 100644 index 0000000..4ea60fc --- /dev/null +++ b/src/github.com/cthit/goldapps/cmd/additions.go @@ -0,0 +1,136 @@ +package main + +import ( + "../../goldapps" + "../json" + "regexp" + "fmt" +) + +func addAdditions(providerGroups goldapps.Groups, providerUsers goldapps.Users) (goldapps.Groups, goldapps.Users) { + fmt.Println("Collecting additions") + additionUsers, additionGroups := getAdditions() + if additionUsers != nil && additionGroups != nil { + fmt.Printf("%d usersAdditions and %d groupAdditions collected.\n", len(additionUsers), len(additionGroups)) + + fmt.Print("Merging groups... ") + providerGroups = mergeAdditionGroups(additionGroups, providerGroups) + fmt.Println("Done!") + + fmt.Print("Merging users... ") + providerUsers = mergeAdditionalUsers(additionUsers, providerUsers) + fmt.Println("Done!") + } else { + fmt.Println("Skipping additions") + } + return providerGroups, providerUsers +} + +func getAdditions() ([]goldapps.User, []goldapps.Group) { + + var from string + if flags.interactive { + from = askString("Which file would you like to use for additions?, Just press enter to skip", "") + } else { + from = flags.additions + } + + if from == "" { + return nil, nil + } + + isJson, _ := regexp.MatchString(`.+\.json$`, from) + if isJson { + provider, _ := json.NewJsonService(from) + groups, err := provider.GetGroups() + if err != nil { + panic(err) + } + users, err := provider.GetUsers() + if err != nil { + panic(err) + } + return users, groups + } else { + fmt.Println("You must specify a valid json file") + previous := flags.interactive + flags.interactive = true + defer func() { + flags.interactive = previous + }() + return getAdditions() + } +} + +func mergeAdditionGroups(additionGroups goldapps.Groups, providerGroups goldapps.Groups) goldapps.Groups { + for _, group := range additionGroups { + found := false + for i, pgroup := range providerGroups { + if pgroup.Email == group.Email { + found = true + + // Add properties if found + for _, alias := range group.Aliases { + aliasFound := false + for _, other := range pgroup.Aliases { + if other == alias { + aliasFound = true + } + } + if !aliasFound { + providerGroups[i].Aliases = append(pgroup.Aliases, alias) + } + } + + for _, member := range group.Members { + memberFound := false + for _, other := range pgroup.Members { + if other == member { + memberFound = true + } + } + if !memberFound { + providerGroups[i].Members = append(pgroup.Members, member) + } + } + } + } + + // Otherwise simply append it + if !found { + providerGroups = append(providerGroups, group) + } + } + return providerGroups +} +func mergeAdditionalUsers(additionUsers goldapps.Users, providerUsers goldapps.Users) goldapps.Users { + for _, user := range additionUsers { + found := false + for i, pUser := range providerUsers { + + // Add Properties if found, never replace tho + if pUser.Cid == user.Cid { + found = true + if user.Nick != "" { + providerUsers[i].Nick = user.Nick + } + if user.SecondName != "" { + providerUsers[i].SecondName = user.SecondName + } + if user.FirstName != "" { + providerUsers[i].FirstName = user.FirstName + } + if user.Mail != "" { + providerUsers[i].Mail = user.Mail + } + } + } + + // Just add user if it wasn't found + if !found { + providerUsers = append(providerUsers, user) + } + } + return providerUsers +} + diff --git a/src/github.com/cthit/goldapps/cmd/ldap.go b/src/github.com/cthit/goldapps/cmd/ldap.go deleted file mode 100644 index e05d95b..0000000 --- a/src/github.com/cthit/goldapps/cmd/ldap.go +++ /dev/null @@ -1,47 +0,0 @@ -package main - -import ( - "../ldap" - "github.com/spf13/viper" -) - -func NewLdapService() (*ldap.ServiceLDAP, error) { - dbConfig := ldap.ServerConfig{ - Url: viper.GetString("ldap.url"), - ServerName: viper.GetString("ldap.servername"), - } - - groupsConfig := ldap.EntryConfig{ - BaseDN: viper.GetString("ldap.groups.basedn"), - Filter: viper.GetString("ldap.groups.filter"), - Attributes: viper.GetStringSlice("ldap.groups.attributes"), - } - - usersConfig := ldap.EntryConfig{ - BaseDN: viper.GetString("ldap.users.basedn"), - Filter: viper.GetString("ldap.users.filter"), - Attributes: viper.GetStringSlice("ldap.users.attributes"), - } - - // Add custom entries - customEntryNames := viper.GetStringSlice("ldap.custom") - customEntryConfigs := make([]ldap.CustomEntryConfig, 0) - for _, entry := range customEntryNames { - customEntryConfigs = append(customEntryConfigs, - ldap.CustomEntryConfig{ - BaseDN: viper.GetString("ldap." + entry + ".basedn"), - Filter: viper.GetString("ldap." + entry + ".filter"), - ParentFilter: viper.GetString("ldap." + entry + ".parent_filter"), - Attributes: viper.GetStringSlice("ldap." + entry + ".attributes"), - Mail: viper.GetString("ldap." + entry + ".mail"), - }, - ) - } - - loginConfig := ldap.LoginConfig{ - UserName: viper.GetString("ldap.user"), - Password: viper.GetString("ldap.password"), - } - - return ldap.NewLDAPService(dbConfig, loginConfig, usersConfig, groupsConfig, customEntryConfigs) -} diff --git a/src/github.com/cthit/goldapps/cmd/main.go b/src/github.com/cthit/goldapps/cmd/main.go index 1c596c1..6167e2d 100644 --- a/src/github.com/cthit/goldapps/cmd/main.go +++ b/src/github.com/cthit/goldapps/cmd/main.go @@ -2,12 +2,6 @@ package main import ( "fmt" - "regexp" - - "github.com/spf13/viper" - - "../admin" - "../json" "../../goldapps" ) @@ -30,135 +24,37 @@ func main() { fmt.Println("Setting up consumer") consumer := getConsumer() - var err error - + // Collect users and groups + var providerUsers goldapps.Users + var consumerUsers goldapps.Users var providerGroups goldapps.Groups + var consumerGroups goldapps.Groups if !flags.onlyUsers { fmt.Println("Collecting groups from the provider...") - providerGroups, err = provider.GetGroups() - if err != nil { - fmt.Println("Failed to collect groups from provider") - panic(err) - } - fmt.Printf("%d groups collected.\n", len(providerGroups)) - } - - var providerUsers goldapps.Users - if !flags.onlyGroups { - fmt.Println("Collecting users from the provider...") - providerUsers, err = provider.GetUsers() - if err != nil { - fmt.Println("Failed to collect users from provider") - panic(err) - } - fmt.Printf("%d users collected.\n", len(providerUsers)) - } - - var consumerGroups []goldapps.Group - if !flags.onlyUsers { + providerGroups = collectGroups(provider) fmt.Println("Collecting groups from the consumer...") - consumerGroups, err = consumer.GetGroups() - if err != nil { - fmt.Println("Failed to collect groups from consumer") - panic(err) - } - fmt.Printf("%d groups collected.\n", len(consumerGroups)) + consumerGroups = collectGroups(consumer) } - - var consumerUsers []goldapps.User if !flags.onlyGroups { + fmt.Println("Collecting users from the provider...") + providerUsers = collectUsers(provider) fmt.Println("Collecting users from the consumer...") - consumerUsers, err = consumer.GetUsers() - if err != nil { - fmt.Println("Failed to collect users from consumer") - panic(err) - } - fmt.Printf("%d users collected.\n", len(consumerUsers)) + consumerUsers = collectUsers(consumer) } - fmt.Println("Collecting additions") - additionUsers, additionGroups := getAdditions() - if additionUsers != nil && additionGroups != nil { - fmt.Printf("%d usersAdditions and %d groupAdditions collected.\n", len(additionUsers), len(additionGroups)) - fmt.Print("Adding groups... ") - for _, group := range additionGroups { - found := false - for i, pgroup := range providerGroups { - if pgroup.Email == group.Email { - found = true - - for _, alias := range group.Aliases { - aliasFound := false - for _, other := range pgroup.Aliases { - if other == alias { - aliasFound = true - } - } - if !aliasFound { - providerGroups[i].Aliases = append(pgroup.Aliases, alias) - } - } - - for _, member := range group.Members { - memberFound := false - for _, other := range pgroup.Members { - if other == member { - memberFound = true - } - } - if !memberFound { - providerGroups[i].Members = append(pgroup.Members, member) - } - } - } - } - if !found { - providerGroups = append(providerGroups, group) - } - } - fmt.Println("Done!") - fmt.Print("Adding users... ") - for _, user := range additionUsers { - found := false - for i, pUser := range providerUsers { - if pUser.Cid == user.Cid { - found = true - if user.Nick != "" { - providerUsers[i].Nick = user.Nick - } - if user.SecondName != "" { - providerUsers[i].SecondName = user.SecondName - } - if user.FirstName != "" { - providerUsers[i].FirstName = user.FirstName - } - if user.Mail != "" { - providerUsers[i].Mail = user.Mail - } - if user.GdprEducation { - providerUsers[i].GdprEducation = user.GdprEducation - } - } - } - if !found { - providerUsers = append(providerUsers, user) - } - } - fmt.Println("Done!") - } else { - fmt.Println("Skipping additions") - } + // Get and process additions + providerGroups, providerUsers = addAdditions(providerGroups, providerUsers) // Check for and handle duplicates providerUsers, providerGroups = goldapps.RemoveDuplicates(providerUsers, providerGroups) + // Get changes to make groupChanges := goldapps.GroupActions{} if !flags.onlyUsers { fmt.Println("Colculating difference between the consumer and provider groups.") proposedGroupChanges := goldapps.GroupActionsRequired(consumerGroups, providerGroups) groupChanges = getGroupChanges(proposedGroupChanges) } - userChanges := goldapps.UserActions{} if !flags.onlyGroups { fmt.Println("Colculating difference between the consumer and provider users.") @@ -166,6 +62,7 @@ func main() { userChanges = getUserChanges(proposedUserChanges) } + // Ask for confirmation if we are in interactive mode if flags.interactive { proceed := askBool( fmt.Sprintf( @@ -184,32 +81,29 @@ func main() { return } } + + // Stop application if dryrun if flags.dryRun { fmt.Println("Done! (No changes made, dryrun) Stopping application...") return } - groupChangesPerformed, err1 := groupChanges.Commit(consumer) - userChangesPerformed, err2 := userChanges.Commit(consumer) - if err1 == nil && err2 == nil { - fmt.Println("All actions performed!") - return + // Commit changes + groupErrors := groupChanges.Commit(consumer) + userErrors := userChanges.Commit(consumer) + + // Print result + if groupErrors.Amount() == 0 { + fmt.Println("All groups actions performed!") } else { - fmt.Println("All actions could not be performed...") - if err1 != nil { - fmt.Printf("\t For groups:\n") - fmt.Printf("\t\t Performed %d out of %d Additions\n", len(groupChangesPerformed.Additions), len(groupChanges.Additions)) - fmt.Printf("\t\t Performed %d out of %d Deletions\n", len(groupChangesPerformed.Deletions), len(groupChanges.Deletions)) - fmt.Printf("\t\t Performed %d out of %d Updates\n", len(groupChangesPerformed.Updates), len(groupChanges.Updates)) - fmt.Printf("\t\t Error: %s\n", err1.Error()) - } - if err2 != nil { - fmt.Printf("\t For users:\n") - fmt.Printf("\t\t Performed %d out of %d Additions\n", len(userChangesPerformed.Additions), len(userChanges.Additions)) - fmt.Printf("\t\t Performed %d out of %d Deletions\n", len(userChangesPerformed.Deletions), len(userChanges.Deletions)) - fmt.Printf("\t\t Performed %d out of %d Updates\n", len(userChangesPerformed.Updates), len(userChanges.Updates)) - fmt.Printf("\t\t Error: %s\n", err2.Error()) - } + fmt.Printf("&d out of %d group actions performed\n", groupChanges.Amount()-groupErrors.Amount(), ) + fmt.Print(groupErrors.String()) + } + if userErrors.Amount() == 0 { + fmt.Println("All groups actions performed!") + } else { + fmt.Printf("&d out of %d group actions performed\n", userChanges.Amount()-userErrors.Amount(), ) + fmt.Print(userErrors.String()) } } @@ -335,113 +229,3 @@ func getUserChanges(proposedChanges goldapps.UserActions) goldapps.UserActions { return proposedChanges } -func getAdditions() ([]goldapps.User, []goldapps.Group) { - - var from string - if flags.interactive { - from = askString("Which file would you like to use for additions?, Just press enter to skip", "") - } else { - from = flags.additions - } - - if from == "" { - return nil, nil - } - - isJson, _ := regexp.MatchString(`.+\.json$`, from) - if isJson { - provider, _ := json.NewJsonService(from) - groups, err := provider.GetGroups() - if err != nil { - panic(err) - } - users, err := provider.GetUsers() - if err != nil { - panic(err) - } - return users, groups - } else { - fmt.Println("You must specify a valid json file") - previous := flags.interactive - flags.interactive = true - defer func() { - flags.interactive = previous - }() - return getAdditions() - } -} - -func getConsumer() goldapps.UpdateService { - var to string - if flags.interactive { - to = askString("Which consumer would you like to use, 'gapps' or '*.json?", "gapps") - } else { - to = flags.to - } - - switch to { - case "gapps": - consumer, err := admin.NewGoogleService( - viper.GetString("gapps.consumer.servicekeyfile"), - viper.GetString("gapps.consumer.adminaccount")) - if err != nil { - fmt.Println("Failed to create gapps connection.") - panic(err) - } - return consumer - default: - isJson, _ := regexp.MatchString(`.+\.json$`, to) - if isJson { - consumer, _ := json.NewJsonService(to) - return consumer - } else { - fmt.Println("You must specify 'gapps' or '*.json' as consumer.") - previous := flags.interactive - flags.interactive = true - defer func() { - flags.interactive = previous - }() - return getConsumer() - } - } -} - -func getProvider() goldapps.CollectionService { - var from string - if flags.interactive { - from = askString("which provider would you like to use, 'ldap', 'gapps' or '*.json'?", "ldap") - } else { - from = flags.from - } - - switch from { - case "ldap": - provider, err := NewLdapService() - if err != nil { - fmt.Println("Failed to create LDAP connection.") - panic(err) - } - return provider - case "gapps": - provider, err := admin.NewGoogleService(viper.GetString("gapps.provider.servicekeyfile"), viper.GetString("gapps.provider.adminaccount")) - if err != nil { - fmt.Println("Failed to create gapps connection, make sure you have setup gappsProvider in the config file.") - panic(err) - } - return provider - default: - isJson, _ := regexp.MatchString(`.+\.json$`, from) - if isJson { - provider, _ := json.NewJsonService(from) - return provider - } else { - fmt.Println("You must specify 'gapps', 'ldap' or '*.json' as provider.") - previous := flags.interactive - flags.interactive = true - defer func() { - flags.interactive = previous - }() - return getProvider() - } - } -} diff --git a/src/github.com/cthit/goldapps/cmd/services.go b/src/github.com/cthit/goldapps/cmd/services.go new file mode 100644 index 0000000..807dd84 --- /dev/null +++ b/src/github.com/cthit/goldapps/cmd/services.go @@ -0,0 +1,148 @@ +package main + +import ( + "../ldap" + "../json" + "../admin" + "github.com/spf13/viper" + "../../goldapps" + "fmt" + "regexp" +) + + +func getConsumer() goldapps.UpdateService { + var to string + if flags.interactive { + to = askString("Which consumer would you like to use, 'gapps' or '*.json?", "gapps") + } else { + to = flags.to + } + + switch to { + case "gapps": + consumer, err := admin.NewGoogleService( + viper.GetString("gapps.consumer.servicekeyfile"), + viper.GetString("gapps.consumer.adminaccount")) + if err != nil { + fmt.Println("Failed to create gapps connection.") + panic(err) + } + return consumer + default: + isJson, _ := regexp.MatchString(`.+\.json$`, to) + if isJson { + consumer, _ := json.NewJsonService(to) + return consumer + } else { + fmt.Println("You must specify 'gapps' or '*.json' as consumer.") + previous := flags.interactive + flags.interactive = true + defer func() { + flags.interactive = previous + }() + return getConsumer() + } + } +} + +func getProvider() goldapps.CollectionService { + var from string + if flags.interactive { + from = askString("which provider would you like to use, 'ldap', 'gapps' or '*.json'?", "ldap") + } else { + from = flags.from + } + + switch from { + case "ldap": + provider, err := newLdapService() + if err != nil { + fmt.Println("Failed to create LDAP connection.") + panic(err) + } + return provider + case "gapps": + provider, err := admin.NewGoogleService(viper.GetString("gapps.provider.servicekeyfile"), viper.GetString("gapps.provider.adminaccount")) + if err != nil { + fmt.Println("Failed to create gapps connection, make sure you have setup gappsProvider in the config file.") + panic(err) + } + return provider + default: + isJson, _ := regexp.MatchString(`.+\.json$`, from) + if isJson { + provider, _ := json.NewJsonService(from) + return provider + } else { + fmt.Println("You must specify 'gapps', 'ldap' or '*.json' as provider.") + previous := flags.interactive + flags.interactive = true + defer func() { + flags.interactive = previous + }() + return getProvider() + } + } +} + +func collectGroups(service goldapps.CollectionService) (goldapps.Groups) { + groups, err := service.GetGroups() + if err != nil { + fmt.Println("Failed to collect groups") + panic(err) + } + fmt.Printf("%d groups collected.\n", len(groups)) + return groups +} + +func collectUsers(service goldapps.CollectionService) (goldapps.Users) { + users, err := service.GetUsers() + if err != nil { + fmt.Println("Failed to collect users") + panic(err) + } + fmt.Printf("%d users collected.\n", len(users)) + return users +} + +func newLdapService() (*ldap.ServiceLDAP, error) { + dbConfig := ldap.ServerConfig{ + Url: viper.GetString("ldap.url"), + ServerName: viper.GetString("ldap.servername"), + } + + groupsConfig := ldap.EntryConfig{ + BaseDN: viper.GetString("ldap.groups.basedn"), + Filter: viper.GetString("ldap.groups.filter"), + Attributes: viper.GetStringSlice("ldap.groups.attributes"), + } + + usersConfig := ldap.EntryConfig{ + BaseDN: viper.GetString("ldap.users.basedn"), + Filter: viper.GetString("ldap.users.filter"), + Attributes: viper.GetStringSlice("ldap.users.attributes"), + } + + // Add custom entries + customEntryNames := viper.GetStringSlice("ldap.custom") + customEntryConfigs := make([]ldap.CustomEntryConfig, 0) + for _, entry := range customEntryNames { + customEntryConfigs = append(customEntryConfigs, + ldap.CustomEntryConfig{ + BaseDN: viper.GetString("ldap." + entry + ".basedn"), + Filter: viper.GetString("ldap." + entry + ".filter"), + ParentFilter: viper.GetString("ldap." + entry + ".parent_filter"), + Attributes: viper.GetStringSlice("ldap." + entry + ".attributes"), + Mail: viper.GetString("ldap." + entry + ".mail"), + }, + ) + } + + loginConfig := ldap.LoginConfig{ + UserName: viper.GetString("ldap.user"), + Password: viper.GetString("ldap.password"), + } + + return ldap.NewLDAPService(dbConfig, loginConfig, usersConfig, groupsConfig, customEntryConfigs) +} diff --git a/src/github.com/cthit/goldapps/duplicates.go b/src/github.com/cthit/goldapps/duplicates.go index e5806a7..b476549 100644 --- a/src/github.com/cthit/goldapps/duplicates.go +++ b/src/github.com/cthit/goldapps/duplicates.go @@ -127,4 +127,4 @@ func removeAlias(group Group, aliasIndex int) Group { func extractIdentifier(email string) string { return strings.ToLower(strings.Split(email, "@")[0]) -} \ No newline at end of file +} diff --git a/src/github.com/cthit/goldapps/group.go b/src/github.com/cthit/goldapps/group.go index b84604e..530f00a 100644 --- a/src/github.com/cthit/goldapps/group.go +++ b/src/github.com/cthit/goldapps/group.go @@ -11,11 +11,12 @@ type Group struct { Type string `json:"type"` Members []string `json:"members"` Aliases []string `json:"aliases"` - Expendable bool `json:"expendable"` + Expendable bool `json:"expendable"` // Not used in comparision } type Groups []Group +// Search for groupname(email) in list of groups func (groups Groups) Contains(email string) bool { for _, group := range groups { if group.Email == email { @@ -33,10 +34,6 @@ func (group Group) equals(other Group) bool { if len(group.Members) != len(other.Members) { return false } - if len(group.Aliases) != len(other.Aliases) { - return false - } - for _, member := range group.Members { contains := false for _, otherMember := range other.Members { @@ -50,10 +47,13 @@ func (group Group) equals(other Group) bool { } } + if len(group.Aliases) != len(other.Aliases) { + return false + } for _, alias := range group.Aliases { contains := false - for _, otheralias := range other.Aliases { - if strings.ToLower(alias) == strings.ToLower(otheralias) { + for _, otherAlias := range other.Aliases { + if strings.ToLower(alias) == strings.ToLower(otherAlias) { contains = true break } diff --git a/src/github.com/cthit/goldapps/group_action.go b/src/github.com/cthit/goldapps/group_action.go index ae40c03..a576a07 100644 --- a/src/github.com/cthit/goldapps/group_action.go +++ b/src/github.com/cthit/goldapps/group_action.go @@ -2,69 +2,103 @@ package goldapps import ( "fmt" + "bytes" ) -type GroupUpdate struct { - Before Group - After Group -} - +// Set of action, to be performed on a set of groups type GroupActions struct { Updates []GroupUpdate Additions []Group Deletions []Group } +func (actions GroupActions) Amount() int { + return len(actions.Additions) + len(actions.Deletions) + len(actions.Updates) +} + +// Set of actions that could not be performed with accompanying errors +type GroupActionErrors struct { + Updates []GroupUpdateError + Additions []GroupAddOrDelError + Deletions []GroupAddOrDelError +} +type GroupUpdateError struct { + Action GroupUpdate + Error error +} +type GroupAddOrDelError struct { + Action Group + Error error +} +func (actions GroupActionErrors) Amount() int { + return len(actions.Additions) + len(actions.Deletions) + len(actions.Updates) +} +func (actions GroupActionErrors) String() string { + builder := bytes.Buffer{} + for _,deletion := range actions.Deletions { + builder.WriteString(fmt.Sprintf("Deletion of group \"%s\" failed with error %s\n", deletion.Action.Email, deletion.Error.Error())) + } + for _,update := range actions.Updates { + builder.WriteString(fmt.Sprintf("Update of group \"%s\" failed with error %s\n", update.Action.After.Email, update.Error.Error())) + } + for _,addition := range actions.Additions { + builder.WriteString(fmt.Sprintf("Addition of group \"%s\" failed with error %s\n", addition.Action.Email, addition.Error.Error())) + } + return builder.String() +} + +// Data struct representing how a group looks not and how it should look after an update +// Allows for efficient updates as application doesn't have to re-upload whole group +type GroupUpdate struct { + Before Group + After Group +} // Commits a set of actions to a service. // Returns all actions performed and a error if not all actions could be performed for some reason. -func (actions GroupActions) Commit(service UpdateService) (GroupActions, error) { +func (actions GroupActions) Commit(service UpdateService) GroupActionErrors { - performedActions := GroupActions{} + errors := GroupActionErrors{} if len(actions.Deletions) > 0 { fmt.Println("(Groups) Performing deletions") - } - for _, group := range actions.Deletions { - err := service.DeleteGroup(group) - if err != nil { - fmt.Println() - return performedActions, err + printProgress(0, len(actions.Deletions), 0) + for deletionsIndex, group := range actions.Deletions { + err := service.DeleteGroup(group) + if err != nil { + // Save error + errors.Deletions = append(errors.Deletions, GroupAddOrDelError{Action: group, Error: err}) + } + printProgress(deletionsIndex+1, len(actions.Deletions), len(errors.Deletions)) } - - performedActions.Deletions = append(performedActions.Deletions, group) - printProgress(len(performedActions.Deletions), len(actions.Deletions)) } if len(actions.Updates) > 0 { fmt.Println("(Groups) Performing updates") - } - for _, update := range actions.Updates { - err := service.UpdateGroup(update) - if err != nil { - fmt.Println(update) - fmt.Println(err) - //return performedActions, err - } else { - performedActions.Updates = append(performedActions.Updates, update) + printProgress(0, len(actions.Updates), 0) + for updatesIndex, update := range actions.Updates { + err := service.UpdateGroup(update) + if err != nil { + // Save error + errors.Updates = append(errors.Updates, GroupUpdateError{Action: update, Error: err}) + } + printProgress(updatesIndex+1, len(actions.Updates), len(errors.Updates)) } - printProgress(len(performedActions.Updates), len(actions.Updates)) } if len(actions.Additions) > 0 { fmt.Println("(Groups) Performing additions") - } - for _, group := range actions.Additions { - err := service.AddGroup(group) - if err != nil { - fmt.Println(group) - return performedActions, err + printProgress(0, len(actions.Additions), 0) + for additionsIndex, group := range actions.Additions { + err := service.AddGroup(group) + if err != nil { + // Save error + errors.Additions = append(errors.Additions, GroupAddOrDelError{Action: group, Error: err}) + } + printProgress(additionsIndex+1, len(actions.Additions), len(errors.Additions)) } - - performedActions.Additions = append(performedActions.Additions, group) - printProgress(len(performedActions.Additions), len(actions.Additions)) } - return performedActions, nil + return errors } // Determines actions required to make the "old" group list look as the "new" group list. @@ -73,12 +107,15 @@ func GroupActionsRequired(old []Group, new []Group) GroupActions { requiredActions := GroupActions{} for _, newGroup := range new { - exists := false for _, oldGroup := range old { + // identify by Email if newGroup.Email == oldGroup.Email { + // Groups exists exists = true - if !newGroup.equals(oldGroup) { // Group exists but is modified + // check if group has to be updates + if !newGroup.equals(oldGroup) { + // Add group update requiredActions.Updates = append(requiredActions.Updates, GroupUpdate{ Before: oldGroup, After: newGroup, @@ -88,25 +125,26 @@ func GroupActionsRequired(old []Group, new []Group) GroupActions { } } - if !exists { // Group does not exist in old list + // Add group creation action if group doesn't exist + if !exists { requiredActions.Additions = append(requiredActions.Additions, newGroup) } } for _, oldGroup := range old { - - exists := false + // check if group should be removed + removed := true for _, newGroup := range new { if oldGroup.Email == newGroup.Email { - exists = true + removed = false break } } - if !exists { // Old list has group but the new list doesn't + if removed { + // Add group deletion action requiredActions.Deletions = append(requiredActions.Deletions, oldGroup) } - } return requiredActions diff --git a/src/github.com/cthit/goldapps/json/service.go b/src/github.com/cthit/goldapps/json/service.go index 02c1eb1..5177c88 100644 --- a/src/github.com/cthit/goldapps/json/service.go +++ b/src/github.com/cthit/goldapps/json/service.go @@ -1,9 +1,9 @@ package json import ( + "../../goldapps" "encoding/json" "fmt" - "../../goldapps" "io/ioutil" "os" ) @@ -101,7 +101,7 @@ func NewJsonService(path string) (Service, error) { return Service{}, err } // Write empty object to file - err = Service{path:path}.save(dataObject{}) + err = Service{path: path}.save(dataObject{}) if err != nil { return Service{}, err } diff --git a/src/github.com/cthit/goldapps/ldap/service.go b/src/github.com/cthit/goldapps/ldap/service.go index 0f228a7..e08c7c9 100644 --- a/src/github.com/cthit/goldapps/ldap/service.go +++ b/src/github.com/cthit/goldapps/ldap/service.go @@ -3,8 +3,8 @@ package ldap import ( "crypto/tls" - "fmt" "../../goldapps" + "fmt" "gopkg.in/ldap.v2" "strings" ) @@ -122,15 +122,16 @@ func (s ServiceLDAP) GetUsers() ([]goldapps.User, error) { for _, member := range group.GetAttributeValues("member") { for _, user := range parsePrivilegedGroupMember(member, users, groups.Entries) { if !privilegedUsers.Contains(user.GetAttributeValue("uid")) { - privilegedUsers = append(privilegedUsers, goldapps.User{ - // TODO: Make these attribute values configurable - Cid: user.GetAttributeValue("uid"), - Nick: user.GetAttributeValue("nickname"), - FirstName: user.GetAttributeValue("givenName"), - SecondName: user.GetAttributeValue("sn"), - Mail: user.GetAttributeValue("mail"), - GdprEducation: user.GetAttributeValue("gdprEducated") == "TRUE", - }) + if user.GetAttributeValue("gdprEducated") == "TRUE" { // only add user if he's gdpr educated + privilegedUsers = append(privilegedUsers, goldapps.User{ + // TODO: Make these attribute values configurable + Cid: user.GetAttributeValue("uid"), + Nick: user.GetAttributeValue("nickname"), + FirstName: user.GetAttributeValue("givenName"), + SecondName: user.GetAttributeValue("sn"), + Mail: user.GetAttributeValue("mail"), + }) + } } } } diff --git a/src/github.com/cthit/goldapps/user.go b/src/github.com/cthit/goldapps/user.go index 74e4cb8..6cc4109 100644 --- a/src/github.com/cthit/goldapps/user.go +++ b/src/github.com/cthit/goldapps/user.go @@ -5,16 +5,16 @@ import ( ) type User struct { - Cid string `json:"cid"` - FirstName string `json:"first_name"` - SecondName string `json:"second_name"` - Nick string `json:"nick"` - Mail string `json:"mail"` - GdprEducation bool `json:"gdpr_education"` + Cid string `json:"cid"` + FirstName string `json:"first_name"` + SecondName string `json:"second_name"` + Nick string `json:"nick"` + Mail string `json:"mail"` } type Users []User +// Search for username(cid) in list of groups func (users Users) Contains(cid string) bool { for _, user := range users { if user.Cid == cid { @@ -41,17 +41,10 @@ func (user User) equals(other User) bool { return false } + // Don't check email as its not saved in every consumer atm /*if user.Mail != other.Mail { return false }*/ - /*if user.GdprEducation != other.GdprEducation { - return false - }*/ - - /* - Do not check PasswordHash nor HashFunction - */ - return true } diff --git a/src/github.com/cthit/goldapps/user_action.go b/src/github.com/cthit/goldapps/user_action.go index 6ffd6dd..34194bd 100644 --- a/src/github.com/cthit/goldapps/user_action.go +++ b/src/github.com/cthit/goldapps/user_action.go @@ -2,68 +2,103 @@ package goldapps import ( "fmt" + "bytes" ) -type UserUpdate struct { - Before User - After User -} - +// Set of action to be performed on a set of users type UserActions struct { Updates []UserUpdate Additions []User Deletions []User } +func (actions UserActions) Amount() int { + return len(actions.Additions) + len(actions.Deletions) + len(actions.Updates) +} + +// Set of actions that could not be performed with accompanying errors +type UserActionErrors struct { + Updates []UserUpdateError + Additions []UserAddOrDelError + Deletions []UserAddOrDelError +} +type UserUpdateError struct { + Action UserUpdate + Error error +} +type UserAddOrDelError struct { + Action User + Error error +} +func (actions UserActionErrors) Amount() int { + return len(actions.Additions) + len(actions.Deletions) + len(actions.Updates) +} +func (actions UserActionErrors) String() string { + builder := bytes.Buffer{} + for _,deletion := range actions.Deletions { + builder.WriteString(fmt.Sprintf("Deletion of user \"%s\" failed with error %s\n", deletion.Action.Cid, deletion.Error.Error())) + } + for _,update := range actions.Updates { + builder.WriteString(fmt.Sprintf("Update of user \"%s\" failed with error %s\n", update.Action.After.Cid, update.Error.Error())) + } + for _,addition := range actions.Additions { + builder.WriteString(fmt.Sprintf("Addition of user \"%s\" failed with error %s\n", addition.Action.Cid, addition.Error.Error())) + } + return builder.String() +} + +// Data struct representing how a user should look before and after an update +// Allows for efficient updates as application doesn't have to re-upload whole user +type UserUpdate struct { + Before User + After User +} // Commits a set of actions to a service. // Returns all actions performed and a error if not all actions could be performed for some reason. -func (actions UserActions) Commit(service UpdateService) (UserActions, error) { +func (actions UserActions) Commit(service UpdateService) UserActionErrors { - performedActions := UserActions{} + errors := UserActionErrors{} - if len(actions.Updates) > 0 { - fmt.Println("(Users) Performing updates") - } - for _, update := range actions.Updates { - err := service.UpdateUser(update) - if err != nil { - fmt.Println() - return performedActions, err + if len(actions.Deletions) > 0 { + fmt.Println("(Users) Performing deletions") + printProgress(0, len(actions.Deletions), 0) + for deletionsIndex, user := range actions.Deletions { + err := service.DeleteUser(user) + if err != nil { + // Save error + errors.Deletions = append(errors.Deletions, UserAddOrDelError{Action: user, Error: err}) + } + printProgress(deletionsIndex+1, len(actions.Deletions), len(errors.Deletions)) } - - performedActions.Updates = append(performedActions.Updates, update) - printProgress(len(performedActions.Updates), len(actions.Updates)) } - if len(actions.Additions) > 0 { - fmt.Println("(Users) Performing additions") - } - for _, user := range actions.Additions { - err := service.AddUser(user) - if err != nil { - fmt.Println() - return performedActions, err + if len(actions.Updates) > 0 { + fmt.Println("(USers) Performing updates") + printProgress(0, len(actions.Updates), 0) + for updatesIndex, update := range actions.Updates { + err := service.UpdateUser(update) + if err != nil { + // Save error + errors.Updates = append(errors.Updates, UserUpdateError{Action: update, Error: err}) + } + printProgress(updatesIndex+1, len(actions.Updates), len(errors.Updates)) } - - performedActions.Additions = append(performedActions.Additions, user) - printProgress(len(performedActions.Additions), len(actions.Additions)) } - if len(actions.Deletions) > 0 { - fmt.Println("(Users) Performing deletions") - } - for _, user := range actions.Deletions { - err := service.DeleteUser(user) - if err != nil { - fmt.Println() - return performedActions, err + if len(actions.Additions) > 0 { + fmt.Println("(Groups) Performing additions") + printProgress(0, len(actions.Additions), 0) + for additionsIndex, user := range actions.Additions { + err := service.AddUser(user) + if err != nil { + // Save error + errors.Additions = append(errors.Additions, UserAddOrDelError{Action: user, Error: err}) + } + printProgress(additionsIndex+1, len(actions.Additions), len(errors.Additions)) } - - performedActions.Deletions = append(performedActions.Deletions, user) - printProgress(len(performedActions.Deletions), len(actions.Deletions)) } - return performedActions, nil + return errors } // Determines actions required to make the "old" user list look as the "new" user list. @@ -72,12 +107,15 @@ func UserActionsRequired(old []User, new []User) UserActions { requiredActions := UserActions{} for _, newUser := range new { - exists := false for _, oldUser := range old { + // identify by Cid if newUser.Cid == oldUser.Cid { + // User exists exists = true - if !newUser.equals(oldUser) { // User exists but is modified + // check if user has to be updates + if !newUser.equals(oldUser) { + // Add User update requiredActions.Updates = append(requiredActions.Updates, UserUpdate{ Before: oldUser, After: newUser, @@ -87,25 +125,26 @@ func UserActionsRequired(old []User, new []User) UserActions { } } - if !exists { // User does not exist in old list + // Add user creation action if user doesn't exist + if !exists { requiredActions.Additions = append(requiredActions.Additions, newUser) } } for _, oldUser := range old { - - exists := false + // check if user should be removed + removed := true for _, newUser := range new { if oldUser.Cid == newUser.Cid { - exists = true + removed = false break } } - if !exists { // Old list has user but the new list doesn't + if removed { + // Add user deletion action requiredActions.Deletions = append(requiredActions.Deletions, oldUser) } - } return requiredActions diff --git a/src/github.com/cthit/goldapps/util.go b/src/github.com/cthit/goldapps/util.go index 6423653..9051b7b 100644 --- a/src/github.com/cthit/goldapps/util.go +++ b/src/github.com/cthit/goldapps/util.go @@ -5,10 +5,11 @@ import ( "fmt" ) -func printProgress(done int, total int) { +// Done does include failed too +func printProgress(done, total, failed int) { p := (done * 100) / total builder := bytes.Buffer{} - for i := 0; i < 100; i++ { + for i := 0; i <= 100; i++ { if i < p { builder.WriteByte('=') } else if i == p { @@ -16,10 +17,19 @@ func printProgress(done int, total int) { } else { builder.WriteByte(' ') } - } fmt.Printf("\rProgress: [%s] %d/%d", builder.String(), done, total) + + // Add failed counter if necessary + if failed != 0 { + fmt.Printf(" (Failed: %d)", failed) + } + + // Replace progressbar with done text if done == total { + if failed != 0 { + fmt.Printf("Done! (Failed: %d)", failed) + } fmt.Printf("\rDone\n") } } From aa5ac1a792e4add1e2508df3614add80bdbd9b36 Mon Sep 17 00:00:00 2001 From: Gurgy Date: Thu, 23 Aug 2018 20:58:08 +0200 Subject: [PATCH 46/64] Split admin service file and centralize compassions --- .../cthit/goldapps/admin/password.go | 24 ++ .../cthit/goldapps/admin/service.go | 356 +----------------- .../cthit/goldapps/admin/service_groups.go | 203 ++++++++++ .../cthit/goldapps/admin/service_users.go | 133 +++++++ src/github.com/cthit/goldapps/admin/util.go | 18 + src/github.com/cthit/goldapps/duplicates.go | 22 +- src/github.com/cthit/goldapps/group.go | 14 +- src/github.com/cthit/goldapps/group_action.go | 6 +- src/github.com/cthit/goldapps/user.go | 10 +- src/github.com/cthit/goldapps/user_action.go | 7 +- src/github.com/cthit/goldapps/util.go | 19 + 11 files changed, 430 insertions(+), 382 deletions(-) create mode 100644 src/github.com/cthit/goldapps/admin/service_groups.go create mode 100644 src/github.com/cthit/goldapps/admin/service_users.go create mode 100644 src/github.com/cthit/goldapps/admin/util.go diff --git a/src/github.com/cthit/goldapps/admin/password.go b/src/github.com/cthit/goldapps/admin/password.go index fec17b4..2537d76 100644 --- a/src/github.com/cthit/goldapps/admin/password.go +++ b/src/github.com/cthit/goldapps/admin/password.go @@ -3,8 +3,14 @@ package admin import ( "github.com/sethvargo/go-password/password" "math/rand" + "fmt" + "google.golang.org/api/gmail/v1" + "encoding/base64" ) +const passwordMailBody = "Action required! You are a member of a committee at the IT-section and have therefor been provided a google-account by the section. Login within the following week to setup two-factor-authentication or you might get locked out from your account. You can login on any google service such as gmail.google.com or drive.google.com with cid@chalmers.it and your provided password: %s" +const passwordMailSubject = "Login details for google services at chalmers.it" + func newPassword() string { numbers := rand.Intn(10) + 5 //symbols := rand.Intn(10) + 5 @@ -14,3 +20,21 @@ func newPassword() string { } return pass } + +func (s googleService) sendPassword(to string, password string) error { + + from := s.admin + "@" + s.domain + body := fmt.Sprintf(passwordMailBody, password) + + msgRaw := "From: " + from + "\r\n" + + "To: " + to + "\r\n" + + "Subject: " + passwordMailSubject + "\r\n\r\n" + + body + "\r\n" + + msg := &gmail.Message{ + Raw: base64.StdEncoding.EncodeToString([]byte(msgRaw)), + } + _, err := s.mailService.Users.Messages.Send(from, msg).Do() + + return err +} diff --git a/src/github.com/cthit/goldapps/admin/service.go b/src/github.com/cthit/goldapps/admin/service.go index 567d78b..70fab7a 100644 --- a/src/github.com/cthit/goldapps/admin/service.go +++ b/src/github.com/cthit/goldapps/admin/service.go @@ -9,19 +9,12 @@ import ( "golang.org/x/net/context" "golang.org/x/oauth2/google" - "bytes" - "encoding/base64" - "fmt" "io/ioutil" "strings" - "time" ) const googleDuplicateEntryError = "googleapi: Error 409: Entity already exists., duplicate" -const passwordMailBody = "Action required! You are a member of a committee at the IT-section and have therefor been provided a google-account by the section. Login within the following week to setup two-factor-authentication or you might get locked out from your account. You can login on any google service such as gmail.google.com or drive.google.com with cid@chalmers.it and your provided password: %s" -const passwordMailSubject = "Login details for google services at chalmers.it" - // my_customer seems to work... const googleCustomer = "my_customer" @@ -74,351 +67,4 @@ func NewGoogleService(keyPath string, adminMail string) (goldapps.UpdateService, } return gs, nil -} - -func (g googleService) sendPassword(to string, password string) error { - - from := g.admin + "@" + g.domain - body := fmt.Sprintf(passwordMailBody, password) - - msgRaw := "From: " + from + "\r\n" + - "To: " + to + "\r\n" + - "Subject: " + passwordMailSubject + "\r\n\r\n" + - body + "\r\n" - - msg := &gmail.Message{ - Raw: base64.StdEncoding.EncodeToString([]byte(msgRaw)), - } - _, err := g.mailService.Users.Messages.Send(from, msg).Do() - - return err -} - -func (s googleService) DeleteGroup(group goldapps.Group) error { - err := s.adminService.Groups.Delete(group.Email).Do() - return err -} - -func (s googleService) UpdateGroup(groupUpdate goldapps.GroupUpdate) error { - newGroup := admin.Group{ - Email: groupUpdate.Before.Email, - } - - // Add all new members - for _, member := range groupUpdate.After.Members { - exists := false - for _, existingMember := range groupUpdate.Before.Members { - if strings.ToLower(member) == strings.ToLower(existingMember) { - exists = true - break - } - } - if !exists { - _, err := s.adminService.Members.Insert(groupUpdate.Before.Email, &admin.Member{Email: member}).Do() - if err != nil { - fmt.Printf("Failed to add menber %s\n", member) - return err - } - } - } - - // Remove all old members - for _, existingMember := range groupUpdate.Before.Members { - keep := false - for _, member := range groupUpdate.After.Members { - if strings.ToLower(existingMember) == strings.ToLower(member) { - keep = true - break - } - } - if !keep { - err := s.adminService.Members.Delete(groupUpdate.Before.Email, existingMember).Do() - if err != nil { - return err - } - } - } - - // Add all new aliases - for _, alias := range groupUpdate.After.Aliases { - exists := false - for _, existingAlias := range groupUpdate.Before.Aliases { - if strings.ToLower(alias) == strings.ToLower(existingAlias) { - exists = true - break - } - } - if !exists { - _, err := s.adminService.Groups.Aliases.Insert(groupUpdate.Before.Email, &admin.Alias{Alias: alias}).Do() - if err != nil { - return err - } - } - } - - // Remove all old aliases - for _, existingAlias := range groupUpdate.Before.Aliases { - keep := false - for _, alias := range groupUpdate.After.Aliases { - if strings.ToLower(existingAlias) == strings.ToLower(alias) { - keep = true - break - } - } - if !keep { - err := s.adminService.Groups.Aliases.Delete(groupUpdate.Before.Email, existingAlias).Do() - if err != nil { - return err - } - } - } - - _, err := s.adminService.Groups.Update(groupUpdate.Before.Email, &newGroup).Do() - return err -} - -func (s googleService) AddGroup(group goldapps.Group) error { - newGroup := admin.Group{ - Email: group.Email, - } - - _, err := s.adminService.Groups.Insert(&newGroup).Do() - if err != nil { - return err - } - - time.Sleep(time.Second * 10) - - // Add members - for _, member := range group.Members { - _, err := s.adminService.Members.Insert(group.Email, &admin.Member{Email: member}).Do() - if err != nil { - return err - } - } - - // Add Aliases - for _, alias := range group.Aliases { - _, err := s.adminService.Groups.Aliases.Insert(group.Email, &admin.Alias{Alias: alias}).Do() - if err != nil { - return err - } - } - return nil -} - -func (s googleService) GetGroups() ([]goldapps.Group, error) { - - adminGroups, err := s.getGoogleGroups(googleCustomer) - if err != nil { - return nil, err - } - - groups := make([]goldapps.Group, len(adminGroups)) - for i, group := range adminGroups { - - p := (i * 100) / len(groups) - - builder := bytes.Buffer{} - for i := 0; i < 100; i++ { - if i < p { - builder.WriteByte('=') - } else if i == p { - builder.WriteByte('>') - } else { - builder.WriteByte(' ') - } - - } - - fmt.Printf("\rProgress: [%s] %d/%d", builder.String(), i+1, len(groups)) - - members, err := s.getGoogleGroupMembers(group.Email) - if err != nil { - return nil, err - } - - groups[i] = goldapps.Group{ - Email: group.Email, - Members: members, - Aliases: group.Aliases, - } - } - fmt.Printf("\rDone\n") - - return groups, nil - -} - -func (s googleService) AddUser(user goldapps.User) error { - - usr := buildGoldappsUser(user, s.domain) - - password := newPassword() - - usr.Password = password - usr.ChangePasswordAtNextLogin = true - - _, err := s.adminService.Users.Insert(usr).Do() - if err != nil { - return err - } - - err = s.sendPassword(user.Mail, password) - if err != nil { - return err - } - - // Google needs time for the addition to propagate - time.Sleep(time.Second) - - // Add alias for nick@example.ex - return s.addUserAlias(fmt.Sprintf("%s@%s", user.Cid, s.domain), fmt.Sprintf("%s@%s", user.Nick, s.domain)) -} - -func (s googleService) UpdateUser(update goldapps.UserUpdate) error { - _, err := s.adminService.Users.Update( - fmt.Sprintf("%s@%s", update.Before.Cid, s.domain), - buildGoldappsUser(update.After, s.domain), - ).Do() - if err != nil { - return err - } - - // Add alias for nick@example.ex - return s.addUserAlias(fmt.Sprintf("%s@%s", update.After.Cid, s.domain), fmt.Sprintf("%s@%s", update.After.Nick, s.domain)) -} - -func (s googleService) DeleteUser(user goldapps.User) error { - admin := fmt.Sprintf("%s@%s", s.admin, s.domain) - userId := fmt.Sprintf("%s@%s", user.Cid, s.domain) - if admin == userId { - fmt.Printf("Skipping andmin user: %s\n", admin) - } - - err := s.adminService.Users.Delete(userId).Do() - return err -} - -func (s googleService) GetUsers() ([]goldapps.User, error) { - adminUsers, err := s.getGoogleUsers(googleCustomer) - if err != nil { - return nil, err - } - users := make([]goldapps.User, len(adminUsers)-1) - - admin := fmt.Sprintf("%s@%s", s.admin, s.domain) - - i := 0 - for _, adminUser := range adminUsers { - if admin != adminUser.PrimaryEmail { // Don't list admin account - // Separating nick and firstName from (Nick / FirstName) - givenName := strings.Split(adminUser.Name.GivenName, " / ") - nick := givenName[0] - firstName := "" - if len(givenName) >= 2 { - firstName = givenName[1] - } - - // Extracting cid form (cid@example.ex) - cid := strings.Split(adminUser.PrimaryEmail, "@")[0] - - users[i] = goldapps.User{ - Cid: cid, - FirstName: firstName, - SecondName: adminUser.Name.FamilyName, - Nick: nick, - } - i++ - } - } - - return users, err -} - -func (s googleService) getGoogleGroups(customer string) ([]admin.Group, error) { - groups, err := s.adminService.Groups.List().Customer(customer).Do() - if err != nil { - return nil, err - } - - for groups.NextPageToken != "" { - newGroups, err := s.adminService.Groups.List().Customer(customer).PageToken(groups.NextPageToken).Do() - if err != nil { - return nil, err - } - - groups.Groups = append(groups.Groups, newGroups.Groups...) - groups.NextPageToken = newGroups.NextPageToken - } - - result := make([]admin.Group, len(groups.Groups)) - for i, group := range groups.Groups { - result[i] = *group - } - - return result, nil -} - -func (s googleService) getGoogleGroupMembers(email string) ([]string, error) { - members, err := s.adminService.Members.List(email).Do() - if err != nil { - return nil, err - } - - result := make([]string, len(members.Members)) - for i, member := range members.Members { - result[i] = member.Email - } - - return result, nil -} - -func (s googleService) getGoogleUsers(customer string) ([]admin.User, error) { - users, err := s.adminService.Users.List().Customer(customer).Do() - if err != nil { - return nil, err - } - - for users.NextPageToken != "" { - newUsers, err := s.adminService.Users.List().Customer(customer).PageToken(users.NextPageToken).Do() - if err != nil { - return nil, err - } - - users.Users = append(users.Users, newUsers.Users...) - users.NextPageToken = newUsers.NextPageToken - } - - result := make([]admin.User, len(users.Users)) - for i, user := range users.Users { - result[i] = *user - } - - return result, nil -} - -func (s googleService) addUserAlias(userKey string, alias string) error { - _, err := s.adminService.Users.Aliases.Insert(userKey, &admin.Alias{ - Alias: alias, - }).Do() - if err != nil { - if err.Error() == googleDuplicateEntryError { - fmt.Printf("Warning: Could not add alias for %s. It already exists. \n", alias) - } else { - return err - } - } - return nil -} - -func buildGoldappsUser(user goldapps.User, domain string) *admin.User { - return &admin.User{ - Name: &admin.UserName{ - FamilyName: user.SecondName, - GivenName: fmt.Sprintf("%s / %s", user.Nick, user.FirstName), - }, - IncludeInGlobalAddressList: true, - PrimaryEmail: fmt.Sprintf("%s@%s", user.Cid, domain), - } -} +} \ No newline at end of file diff --git a/src/github.com/cthit/goldapps/admin/service_groups.go b/src/github.com/cthit/goldapps/admin/service_groups.go new file mode 100644 index 0000000..eb4e7e1 --- /dev/null +++ b/src/github.com/cthit/goldapps/admin/service_groups.go @@ -0,0 +1,203 @@ +package admin + +import ( + "google.golang.org/api/admin/directory/v1" // Imports as admin + "github.com/cthit/goldapps" + "fmt" + "time" + "bytes" +) + +func (s googleService) DeleteGroup(group goldapps.Group) error { + err := s.adminService.Groups.Delete(group.Email).Do() + return err +} + +func (s googleService) UpdateGroup(groupUpdate goldapps.GroupUpdate) error { + newGroup := admin.Group{ + Email: groupUpdate.Before.Email, + } + + // Add all new members + for _, member := range groupUpdate.After.Members { + exists := false + for _, existingMember := range groupUpdate.Before.Members { + if goldapps.CompareEmails(member, existingMember) { + exists = true + break + } + } + if !exists { + _, err := s.adminService.Members.Insert(groupUpdate.Before.Email, &admin.Member{Email: member}).Do() + if err != nil { + fmt.Printf("Failed to add menber %s\n", member) + return err + } + } + } + + // Remove all old members + for _, existingMember := range groupUpdate.Before.Members { + keep := false + for _, member := range groupUpdate.After.Members { + if goldapps.CompareEmails(existingMember, member) { + keep = true + break + } + } + if !keep { + err := s.adminService.Members.Delete(groupUpdate.Before.Email, existingMember).Do() + if err != nil { + return err + } + } + } + + // Add all new aliases + for _, alias := range groupUpdate.After.Aliases { + exists := false + for _, existingAlias := range groupUpdate.Before.Aliases { + if goldapps.CompareEmails(alias, existingAlias) { + exists = true + break + } + } + if !exists { + _, err := s.adminService.Groups.Aliases.Insert(groupUpdate.Before.Email, &admin.Alias{Alias: alias}).Do() + if err != nil { + return err + } + } + } + + // Remove all old aliases + for _, existingAlias := range groupUpdate.Before.Aliases { + keep := false + for _, alias := range groupUpdate.After.Aliases { + if goldapps.CompareEmails(existingAlias, alias) { + keep = true + break + } + } + if !keep { + err := s.adminService.Groups.Aliases.Delete(groupUpdate.Before.Email, existingAlias).Do() + if err != nil { + return err + } + } + } + + _, err := s.adminService.Groups.Update(groupUpdate.Before.Email, &newGroup).Do() + return err +} + +func (s googleService) AddGroup(group goldapps.Group) error { + newGroup := admin.Group{ + Email: group.Email, + } + + _, err := s.adminService.Groups.Insert(&newGroup).Do() + if err != nil { + return err + } + + time.Sleep(time.Second * 10) + + // Add members + for _, member := range group.Members { + _, err := s.adminService.Members.Insert(group.Email, &admin.Member{Email: member}).Do() + if err != nil { + return err + } + } + + // Add Aliases + for _, alias := range group.Aliases { + _, err := s.adminService.Groups.Aliases.Insert(group.Email, &admin.Alias{Alias: alias}).Do() + if err != nil { + return err + } + } + return nil +} + +func (s googleService) GetGroups() ([]goldapps.Group, error) { + + adminGroups, err := s.getGoogleGroups(googleCustomer) + if err != nil { + return nil, err + } + + groups := make([]goldapps.Group, len(adminGroups)) + for i, group := range adminGroups { + + p := (i * 100) / len(groups) + + builder := bytes.Buffer{} + for i := 0; i < 100; i++ { + if i < p { + builder.WriteByte('=') + } else if i == p { + builder.WriteByte('>') + } else { + builder.WriteByte(' ') + } + + } + + fmt.Printf("\rProgress: [%s] %d/%d", builder.String(), i+1, len(groups)) + + members, err := s.getGoogleGroupMembers(group.Email) + if err != nil { + return nil, err + } + + groups[i] = goldapps.Group{ + Email: group.Email, + Members: members, + Aliases: group.Aliases, + } + } + fmt.Printf("\rDone\n") + + return groups, nil + +} + +func (s googleService) getGoogleGroups(customer string) ([]admin.Group, error) { + groups, err := s.adminService.Groups.List().Customer(customer).Do() + if err != nil { + return nil, err + } + + for groups.NextPageToken != "" { + newGroups, err := s.adminService.Groups.List().Customer(customer).PageToken(groups.NextPageToken).Do() + if err != nil { + return nil, err + } + + groups.Groups = append(groups.Groups, newGroups.Groups...) + groups.NextPageToken = newGroups.NextPageToken + } + + result := make([]admin.Group, len(groups.Groups)) + for i, group := range groups.Groups { + result[i] = *group + } + + return result, nil +} + +func (s googleService) getGoogleGroupMembers(email string) ([]string, error) { + members, err := s.adminService.Members.List(email).Do() + if err != nil { + return nil, err + } + + result := make([]string, len(members.Members)) + for i, member := range members.Members { + result[i] = member.Email + } + + return result, nil +} diff --git a/src/github.com/cthit/goldapps/admin/service_users.go b/src/github.com/cthit/goldapps/admin/service_users.go new file mode 100644 index 0000000..8574cb2 --- /dev/null +++ b/src/github.com/cthit/goldapps/admin/service_users.go @@ -0,0 +1,133 @@ +package admin + +import ( + "google.golang.org/api/admin/directory/v1" // Imports as admin + "github.com/cthit/goldapps" + "time" + "fmt" + "strings" +) + +func (s googleService) AddUser(user goldapps.User) error { + + usr := buildGoldappsUser(user, s.domain) + + password := newPassword() + + usr.Password = password + usr.ChangePasswordAtNextLogin = true + + _, err := s.adminService.Users.Insert(usr).Do() + if err != nil { + return err + } + + err = s.sendPassword(user.Mail, password) + if err != nil { + return err + } + + // Google needs time for the addition to propagate + time.Sleep(time.Second) + + // Add alias for nick@example.ex + return s.addUserAlias(fmt.Sprintf("%s@%s", user.Cid, s.domain), fmt.Sprintf("%s@%s", user.Nick, s.domain)) +} + +func (s googleService) UpdateUser(update goldapps.UserUpdate) error { + _, err := s.adminService.Users.Update( + fmt.Sprintf("%s@%s", update.Before.Cid, s.domain), + buildGoldappsUser(update.After, s.domain), + ).Do() + if err != nil { + return err + } + + // Add alias for nick@example.ex + return s.addUserAlias(fmt.Sprintf("%s@%s", update.After.Cid, s.domain), fmt.Sprintf("%s@%s", update.After.Nick, s.domain)) +} + +func (s googleService) DeleteUser(user goldapps.User) error { + admin := fmt.Sprintf("%s@%s", s.admin, s.domain) + userId := fmt.Sprintf("%s@%s", user.Cid, s.domain) + if admin == userId { + fmt.Printf("Skipping andmin user: %s\n", admin) + } + + err := s.adminService.Users.Delete(userId).Do() + return err +} + +func (s googleService) GetUsers() ([]goldapps.User, error) { + adminUsers, err := s.getGoogleUsers(googleCustomer) + if err != nil { + return nil, err + } + users := make([]goldapps.User, len(adminUsers)-1) + + admin := fmt.Sprintf("%s@%s", s.admin, s.domain) + + i := 0 + for _, adminUser := range adminUsers { + if admin != adminUser.PrimaryEmail { // Don't list admin account + // Separating nick and firstName from (Nick / FirstName) + givenName := strings.Split(adminUser.Name.GivenName, " / ") + nick := givenName[0] + firstName := "" + if len(givenName) >= 2 { + firstName = givenName[1] + } + + // Extracting cid form (cid@example.ex) + cid := strings.Split(adminUser.PrimaryEmail, "@")[0] + + users[i] = goldapps.User{ + Cid: cid, + FirstName: firstName, + SecondName: adminUser.Name.FamilyName, + Nick: nick, + } + i++ + } + } + + return users, err +} + +func (s googleService) getGoogleUsers(customer string) ([]admin.User, error) { + users, err := s.adminService.Users.List().Customer(customer).Do() + if err != nil { + return nil, err + } + + for users.NextPageToken != "" { + newUsers, err := s.adminService.Users.List().Customer(customer).PageToken(users.NextPageToken).Do() + if err != nil { + return nil, err + } + + users.Users = append(users.Users, newUsers.Users...) + users.NextPageToken = newUsers.NextPageToken + } + + result := make([]admin.User, len(users.Users)) + for i, user := range users.Users { + result[i] = *user + } + + return result, nil +} + +func (s googleService) addUserAlias(userKey string, alias string) error { + _, err := s.adminService.Users.Aliases.Insert(userKey, &admin.Alias{ + Alias: alias, + }).Do() + if err != nil { + if err.Error() == googleDuplicateEntryError { + fmt.Printf("Warning: Could not add alias for %s. It already exists. \n", alias) + } else { + return err + } + } + return nil +} diff --git a/src/github.com/cthit/goldapps/admin/util.go b/src/github.com/cthit/goldapps/admin/util.go new file mode 100644 index 0000000..228ce52 --- /dev/null +++ b/src/github.com/cthit/goldapps/admin/util.go @@ -0,0 +1,18 @@ +package admin + +import ( + "../../goldapps" + "fmt" + "google.golang.org/api/admin/directory/v1" // Imports as admin +) + +func buildGoldappsUser(user goldapps.User, domain string) *admin.User { + return &admin.User{ + Name: &admin.UserName{ + FamilyName: user.SecondName, + GivenName: fmt.Sprintf("%s / %s", user.Nick, user.FirstName), + }, + IncludeInGlobalAddressList: true, + PrimaryEmail: fmt.Sprintf("%s@%s", user.Cid, domain), + } +} \ No newline at end of file diff --git a/src/github.com/cthit/goldapps/duplicates.go b/src/github.com/cthit/goldapps/duplicates.go index b476549..b6845ef 100644 --- a/src/github.com/cthit/goldapps/duplicates.go +++ b/src/github.com/cthit/goldapps/duplicates.go @@ -10,7 +10,7 @@ func RemoveDuplicates(users Users, groups Groups) (Users, Groups) { for i, user := range users { for k := 0; k < len(groups); k++ { // Check if any cid conflicts with any group name - if strings.ToLower(user.Cid) == extractIdentifier(groups[k].Email) { + if CompareEmails(user.Cid, extractIdentifier(groups[k].Email)) { if groups[k].Expendable { groups = removeArrayGroup(groups, k) k-- // don't breaking the loop @@ -21,7 +21,7 @@ func RemoveDuplicates(users Users, groups Groups) (Users, Groups) { } } // Check if any user nick conflicts with any group name - if strings.ToLower(user.Nick) == extractIdentifier(groups[k].Email) { + if CompareEmails(user.Nick, extractIdentifier(groups[k].Email)) { if groups[k].Expendable { groups = removeArrayGroup(groups, k) k-- // don't breaking the loop @@ -33,7 +33,7 @@ func RemoveDuplicates(users Users, groups Groups) (Users, Groups) { for aliasIndex, alias := range groups[k].Aliases { // Check if any cid conflicts with any group alias - if strings.ToLower(user.Cid) == extractIdentifier(alias) { + if CompareEmails(user.Cid, extractIdentifier(alias)) { if groups[k].Expendable { groups[k] = removeAlias(groups[k], aliasIndex) } else { @@ -43,7 +43,7 @@ func RemoveDuplicates(users Users, groups Groups) (Users, Groups) { } } // Check if any Nick conflicts with any group alias - if strings.ToLower(user.Nick) == extractIdentifier(alias) { + if CompareEmails(user.Nick, extractIdentifier(alias)) { if groups[k].Expendable { groups[k] = removeAlias(groups[k], aliasIndex) } else { @@ -61,18 +61,18 @@ func RemoveDuplicates(users Users, groups Groups) (Users, Groups) { // Don't check with itself if i != j { // Compare cids - if strings.ToLower(user.Cid) == strings.ToLower(otherUser.Cid) { + if CompareEmails(user.Cid, otherUser.Cid) { // Should not be able to happen panic("two users with cid: " + user.Cid) } // Compare Nicks - if strings.ToLower(user.Nick) == strings.ToLower(otherUser.Nick) { + if CompareEmails(user.Nick, otherUser.Nick) { // Nicks are not that important users[i].Nick = "" users[j].Nick = "" } // Compare cids with nicks - if strings.ToLower(user.Cid) == strings.ToLower(otherUser.Nick) { + if CompareEmails(user.Cid, otherUser.Nick) { // Nicks are not that important users[j].Nick = "" } @@ -86,19 +86,19 @@ func RemoveDuplicates(users Users, groups Groups) (Users, Groups) { // Don't check with itself if i != j { // Compare Emails - if strings.ToLower(group.Email) == strings.ToLower(otherGroup.Email) { + if CompareEmails(group.Email, otherGroup.Email) { // Something is set up wrong panic("two groups with email: " + group.Email) } for _, alias := range group.Aliases { // Compare emails with aliases - if strings.ToLower(alias) == strings.ToLower(otherGroup.Email) { + if CompareEmails(alias, otherGroup.Email) { // Something is set up wrong panic("two groups with alias/email: " + group.Email + ", " + otherGroup.Email) } for _, otherAlias := range otherGroup.Aliases { // Compare aliases with aliases - if strings.ToLower(alias) == strings.ToLower(otherAlias) { + if CompareEmails(alias, otherAlias) { // Something is set up wrong panic("two groups with alias: " + alias) } @@ -126,5 +126,5 @@ func removeAlias(group Group, aliasIndex int) Group { } func extractIdentifier(email string) string { - return strings.ToLower(strings.Split(email, "@")[0]) + return strings.Split(email, "@")[0] } diff --git a/src/github.com/cthit/goldapps/group.go b/src/github.com/cthit/goldapps/group.go index 530f00a..90085d8 100644 --- a/src/github.com/cthit/goldapps/group.go +++ b/src/github.com/cthit/goldapps/group.go @@ -1,7 +1,5 @@ package goldapps -import "strings" - // Represents a email group. // Email is the id and main email for the group. // Members is a lost of email addresses that are members of this group. @@ -26,8 +24,12 @@ func (groups Groups) Contains(email string) bool { return false } -func (group Group) equals(other Group) bool { - if strings.ToLower(group.Email) != strings.ToLower(other.Email) { +func (group Group) Same(other Group) bool { + return CompareEmails(group.Email, other.Email) +} + +func (group Group) Equals(other Group) bool { + if !group.Same(other) { return false } @@ -37,7 +39,7 @@ func (group Group) equals(other Group) bool { for _, member := range group.Members { contains := false for _, otherMember := range other.Members { - if strings.ToLower(member) == strings.ToLower(otherMember) { + if CompareEmails(member, otherMember) { contains = true break } @@ -53,7 +55,7 @@ func (group Group) equals(other Group) bool { for _, alias := range group.Aliases { contains := false for _, otherAlias := range other.Aliases { - if strings.ToLower(alias) == strings.ToLower(otherAlias) { + if CompareEmails(alias, otherAlias) { contains = true break } diff --git a/src/github.com/cthit/goldapps/group_action.go b/src/github.com/cthit/goldapps/group_action.go index a576a07..2f44b1a 100644 --- a/src/github.com/cthit/goldapps/group_action.go +++ b/src/github.com/cthit/goldapps/group_action.go @@ -110,11 +110,11 @@ func GroupActionsRequired(old []Group, new []Group) GroupActions { exists := false for _, oldGroup := range old { // identify by Email - if newGroup.Email == oldGroup.Email { + if newGroup.Same(oldGroup) { // Groups exists exists = true // check if group has to be updates - if !newGroup.equals(oldGroup) { + if !newGroup.Equals(oldGroup) { // Add group update requiredActions.Updates = append(requiredActions.Updates, GroupUpdate{ Before: oldGroup, @@ -135,7 +135,7 @@ func GroupActionsRequired(old []Group, new []Group) GroupActions { // check if group should be removed removed := true for _, newGroup := range new { - if oldGroup.Email == newGroup.Email { + if oldGroup.Same(newGroup) { removed = false break } diff --git a/src/github.com/cthit/goldapps/user.go b/src/github.com/cthit/goldapps/user.go index 6cc4109..b816bc9 100644 --- a/src/github.com/cthit/goldapps/user.go +++ b/src/github.com/cthit/goldapps/user.go @@ -24,8 +24,12 @@ func (users Users) Contains(cid string) bool { return false } -func (user User) equals(other User) bool { - if strings.ToLower(user.Cid) != strings.ToLower(other.Cid) { +func (user User) Same(other User) bool { + return strings.ToLower(user.Cid) == strings.ToLower(other.Cid) +} + +func (user User) Equals(other User) bool { + if !user.Same(other) { return false } @@ -37,7 +41,7 @@ func (user User) equals(other User) bool { return false } - if user.Nick != other.Nick { + if SanitizeEmail(user.Nick) != SanitizeEmail(other.Nick) { // Because google uses nick for mail return false } diff --git a/src/github.com/cthit/goldapps/user_action.go b/src/github.com/cthit/goldapps/user_action.go index 34194bd..ad81d16 100644 --- a/src/github.com/cthit/goldapps/user_action.go +++ b/src/github.com/cthit/goldapps/user_action.go @@ -109,12 +109,11 @@ func UserActionsRequired(old []User, new []User) UserActions { for _, newUser := range new { exists := false for _, oldUser := range old { - // identify by Cid - if newUser.Cid == oldUser.Cid { + if newUser.Same(oldUser) { // User exists exists = true // check if user has to be updates - if !newUser.equals(oldUser) { + if !newUser.Equals(oldUser) { // Add User update requiredActions.Updates = append(requiredActions.Updates, UserUpdate{ Before: oldUser, @@ -135,7 +134,7 @@ func UserActionsRequired(old []User, new []User) UserActions { // check if user should be removed removed := true for _, newUser := range new { - if oldUser.Cid == newUser.Cid { + if oldUser.Same(newUser) { removed = false break } diff --git a/src/github.com/cthit/goldapps/util.go b/src/github.com/cthit/goldapps/util.go index 9051b7b..7726a21 100644 --- a/src/github.com/cthit/goldapps/util.go +++ b/src/github.com/cthit/goldapps/util.go @@ -3,6 +3,7 @@ package goldapps import ( "bytes" "fmt" + "strings" ) // Done does include failed too @@ -33,3 +34,21 @@ func printProgress(done, total, failed int) { fmt.Printf("\rDone\n") } } + +func CompareEmails(email, other string) bool { + return SanitizeEmail(email) == SanitizeEmail(other) +} + +func SanitizeEmail(s string) string { + s = strings.ToLower(s) + s = strings.Replace(s, "π", "pi", -1) + s = strings.Replace(s, "å", "a", -1) + s = strings.Replace(s, "ä", "a", -1) + s = strings.Replace(s, "ö", "o", -1) + s = strings.Replace(s, "ö", "o", -1) + s = strings.Replace(s, "ø", "o", -1) + s = strings.Replace(s, "æ", "ae", -1) + s = strings.Replace(s, " ", "-", -1) + s = strings.Replace(s, ".", "", -1) + return s +} From b1c0361ae032917eecd89d79bda30a8df4a47ce8 Mon Sep 17 00:00:00 2001 From: Gurgy Date: Thu, 23 Aug 2018 21:04:12 +0200 Subject: [PATCH 47/64] Restructure project to go standards structure --- {src/github.com/cthit/goldapps/admin => admin}/password.go | 0 {src/github.com/cthit/goldapps/admin => admin}/scope.go | 0 {src/github.com/cthit/goldapps/admin => admin}/service.go | 0 .../github.com/cthit/goldapps/admin => admin}/service_groups.go | 2 +- {src/github.com/cthit/goldapps/admin => admin}/service_users.go | 2 +- {src/github.com/cthit/goldapps/admin => admin}/util.go | 0 {src/github.com/cthit/goldapps/cmd => cmd}/additions.go | 0 {src/github.com/cthit/goldapps/cmd => cmd}/config.go | 0 {src/github.com/cthit/goldapps/cmd => cmd}/flags.go | 0 {src/github.com/cthit/goldapps/cmd => cmd}/main.go | 0 {src/github.com/cthit/goldapps/cmd => cmd}/services.go | 0 {src/github.com/cthit/goldapps/cmd => cmd}/util.go | 0 src/github.com/cthit/goldapps/duplicates.go => duplicates.go | 0 src/github.com/cthit/goldapps/group.go => group.go | 0 .../cthit/goldapps/group_action.go => group_action.go | 0 {src/github.com/cthit/goldapps/json => json}/service.go | 0 {src/github.com/cthit/goldapps/ldap => ldap}/service.go | 0 src/github.com/cthit/goldapps/services.go => services.go | 0 src/github.com/cthit/goldapps/user.go => user.go | 0 src/github.com/cthit/goldapps/user_action.go => user_action.go | 0 src/github.com/cthit/goldapps/util.go => util.go | 0 21 files changed, 2 insertions(+), 2 deletions(-) rename {src/github.com/cthit/goldapps/admin => admin}/password.go (100%) rename {src/github.com/cthit/goldapps/admin => admin}/scope.go (100%) rename {src/github.com/cthit/goldapps/admin => admin}/service.go (100%) rename {src/github.com/cthit/goldapps/admin => admin}/service_groups.go (99%) rename {src/github.com/cthit/goldapps/admin => admin}/service_users.go (99%) rename {src/github.com/cthit/goldapps/admin => admin}/util.go (100%) rename {src/github.com/cthit/goldapps/cmd => cmd}/additions.go (100%) rename {src/github.com/cthit/goldapps/cmd => cmd}/config.go (100%) rename {src/github.com/cthit/goldapps/cmd => cmd}/flags.go (100%) rename {src/github.com/cthit/goldapps/cmd => cmd}/main.go (100%) rename {src/github.com/cthit/goldapps/cmd => cmd}/services.go (100%) rename {src/github.com/cthit/goldapps/cmd => cmd}/util.go (100%) rename src/github.com/cthit/goldapps/duplicates.go => duplicates.go (100%) rename src/github.com/cthit/goldapps/group.go => group.go (100%) rename src/github.com/cthit/goldapps/group_action.go => group_action.go (100%) rename {src/github.com/cthit/goldapps/json => json}/service.go (100%) rename {src/github.com/cthit/goldapps/ldap => ldap}/service.go (100%) rename src/github.com/cthit/goldapps/services.go => services.go (100%) rename src/github.com/cthit/goldapps/user.go => user.go (100%) rename src/github.com/cthit/goldapps/user_action.go => user_action.go (100%) rename src/github.com/cthit/goldapps/util.go => util.go (100%) diff --git a/src/github.com/cthit/goldapps/admin/password.go b/admin/password.go similarity index 100% rename from src/github.com/cthit/goldapps/admin/password.go rename to admin/password.go diff --git a/src/github.com/cthit/goldapps/admin/scope.go b/admin/scope.go similarity index 100% rename from src/github.com/cthit/goldapps/admin/scope.go rename to admin/scope.go diff --git a/src/github.com/cthit/goldapps/admin/service.go b/admin/service.go similarity index 100% rename from src/github.com/cthit/goldapps/admin/service.go rename to admin/service.go diff --git a/src/github.com/cthit/goldapps/admin/service_groups.go b/admin/service_groups.go similarity index 99% rename from src/github.com/cthit/goldapps/admin/service_groups.go rename to admin/service_groups.go index eb4e7e1..7642cfd 100644 --- a/src/github.com/cthit/goldapps/admin/service_groups.go +++ b/admin/service_groups.go @@ -2,7 +2,7 @@ package admin import ( "google.golang.org/api/admin/directory/v1" // Imports as admin - "github.com/cthit/goldapps" + "../../goldapps" "fmt" "time" "bytes" diff --git a/src/github.com/cthit/goldapps/admin/service_users.go b/admin/service_users.go similarity index 99% rename from src/github.com/cthit/goldapps/admin/service_users.go rename to admin/service_users.go index 8574cb2..254d768 100644 --- a/src/github.com/cthit/goldapps/admin/service_users.go +++ b/admin/service_users.go @@ -2,7 +2,7 @@ package admin import ( "google.golang.org/api/admin/directory/v1" // Imports as admin - "github.com/cthit/goldapps" + "../../goldapps" "time" "fmt" "strings" diff --git a/src/github.com/cthit/goldapps/admin/util.go b/admin/util.go similarity index 100% rename from src/github.com/cthit/goldapps/admin/util.go rename to admin/util.go diff --git a/src/github.com/cthit/goldapps/cmd/additions.go b/cmd/additions.go similarity index 100% rename from src/github.com/cthit/goldapps/cmd/additions.go rename to cmd/additions.go diff --git a/src/github.com/cthit/goldapps/cmd/config.go b/cmd/config.go similarity index 100% rename from src/github.com/cthit/goldapps/cmd/config.go rename to cmd/config.go diff --git a/src/github.com/cthit/goldapps/cmd/flags.go b/cmd/flags.go similarity index 100% rename from src/github.com/cthit/goldapps/cmd/flags.go rename to cmd/flags.go diff --git a/src/github.com/cthit/goldapps/cmd/main.go b/cmd/main.go similarity index 100% rename from src/github.com/cthit/goldapps/cmd/main.go rename to cmd/main.go diff --git a/src/github.com/cthit/goldapps/cmd/services.go b/cmd/services.go similarity index 100% rename from src/github.com/cthit/goldapps/cmd/services.go rename to cmd/services.go diff --git a/src/github.com/cthit/goldapps/cmd/util.go b/cmd/util.go similarity index 100% rename from src/github.com/cthit/goldapps/cmd/util.go rename to cmd/util.go diff --git a/src/github.com/cthit/goldapps/duplicates.go b/duplicates.go similarity index 100% rename from src/github.com/cthit/goldapps/duplicates.go rename to duplicates.go diff --git a/src/github.com/cthit/goldapps/group.go b/group.go similarity index 100% rename from src/github.com/cthit/goldapps/group.go rename to group.go diff --git a/src/github.com/cthit/goldapps/group_action.go b/group_action.go similarity index 100% rename from src/github.com/cthit/goldapps/group_action.go rename to group_action.go diff --git a/src/github.com/cthit/goldapps/json/service.go b/json/service.go similarity index 100% rename from src/github.com/cthit/goldapps/json/service.go rename to json/service.go diff --git a/src/github.com/cthit/goldapps/ldap/service.go b/ldap/service.go similarity index 100% rename from src/github.com/cthit/goldapps/ldap/service.go rename to ldap/service.go diff --git a/src/github.com/cthit/goldapps/services.go b/services.go similarity index 100% rename from src/github.com/cthit/goldapps/services.go rename to services.go diff --git a/src/github.com/cthit/goldapps/user.go b/user.go similarity index 100% rename from src/github.com/cthit/goldapps/user.go rename to user.go diff --git a/src/github.com/cthit/goldapps/user_action.go b/user_action.go similarity index 100% rename from src/github.com/cthit/goldapps/user_action.go rename to user_action.go diff --git a/src/github.com/cthit/goldapps/util.go b/util.go similarity index 100% rename from src/github.com/cthit/goldapps/util.go rename to util.go From 9f060e1deaec714ca5cb96b50e314ae2b5ec5136 Mon Sep 17 00:00:00 2001 From: Gurgy Date: Thu, 23 Aug 2018 21:09:52 +0200 Subject: [PATCH 48/64] Add additions flag to readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 5cbe8b3..c2f9130 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ The following flags are available: * `-to someString`: Set the group consumer to 'gapps' or '*.json'. In case of `gapps` config value `gapps.consumer` will be used. * `-users`: Only collect and sync users * `-groups`: Only collect and sync groups +* `-additions *.json`: file with additions Notice that flags should be combined on the form `goldapps -a -b` and **NOT** on the form `goldapps -ab`. \ No newline at end of file From 8917ab57b9d7031d3873ecc71375aebb5fabd872 Mon Sep 17 00:00:00 2001 From: Gurgy Date: Tue, 4 Sep 2018 20:34:45 +0200 Subject: [PATCH 49/64] Change to global import paths --- admin/service.go | 4 ++-- admin/service_groups.go | 6 +++--- admin/service_users.go | 14 +++++++------- admin/util.go | 4 ++-- cmd/additions.go | 7 +++---- cmd/main.go | 7 +++---- cmd/services.go | 15 +++++++-------- json/service.go | 2 +- ldap/service.go | 2 +- 9 files changed, 29 insertions(+), 32 deletions(-) diff --git a/admin/service.go b/admin/service.go index 70fab7a..dc55fa5 100644 --- a/admin/service.go +++ b/admin/service.go @@ -1,7 +1,7 @@ package admin import ( - "../../goldapps" + "github.com/cthit/goldapps" "google.golang.org/api/admin/directory/v1" // Imports as admin "google.golang.org/api/gmail/v1" // Imports as gmail @@ -67,4 +67,4 @@ func NewGoogleService(keyPath string, adminMail string) (goldapps.UpdateService, } return gs, nil -} \ No newline at end of file +} diff --git a/admin/service_groups.go b/admin/service_groups.go index 7642cfd..b250e24 100644 --- a/admin/service_groups.go +++ b/admin/service_groups.go @@ -1,11 +1,11 @@ package admin import ( - "google.golang.org/api/admin/directory/v1" // Imports as admin - "../../goldapps" + "bytes" "fmt" + "github.com/cthit/goldapps" + "google.golang.org/api/admin/directory/v1" // Imports as admin "time" - "bytes" ) func (s googleService) DeleteGroup(group goldapps.Group) error { diff --git a/admin/service_users.go b/admin/service_users.go index 254d768..3a6dbc3 100644 --- a/admin/service_users.go +++ b/admin/service_users.go @@ -1,11 +1,11 @@ package admin import ( - "google.golang.org/api/admin/directory/v1" // Imports as admin - "../../goldapps" - "time" "fmt" + "github.com/cthit/goldapps" + "google.golang.org/api/admin/directory/v1" // Imports as admin "strings" + "time" ) func (s googleService) AddUser(user goldapps.User) error { @@ -82,10 +82,10 @@ func (s googleService) GetUsers() ([]goldapps.User, error) { cid := strings.Split(adminUser.PrimaryEmail, "@")[0] users[i] = goldapps.User{ - Cid: cid, - FirstName: firstName, - SecondName: adminUser.Name.FamilyName, - Nick: nick, + Cid: cid, + FirstName: firstName, + SecondName: adminUser.Name.FamilyName, + Nick: nick, } i++ } diff --git a/admin/util.go b/admin/util.go index 228ce52..32951d8 100644 --- a/admin/util.go +++ b/admin/util.go @@ -1,8 +1,8 @@ package admin import ( - "../../goldapps" "fmt" + "github.com/cthit/goldapps" "google.golang.org/api/admin/directory/v1" // Imports as admin ) @@ -15,4 +15,4 @@ func buildGoldappsUser(user goldapps.User, domain string) *admin.User { IncludeInGlobalAddressList: true, PrimaryEmail: fmt.Sprintf("%s@%s", user.Cid, domain), } -} \ No newline at end of file +} diff --git a/cmd/additions.go b/cmd/additions.go index 4ea60fc..13f3092 100644 --- a/cmd/additions.go +++ b/cmd/additions.go @@ -1,10 +1,10 @@ package main import ( - "../../goldapps" - "../json" - "regexp" "fmt" + "github.com/cthit/goldapps" + "github.com/cthit/goldapps/json" + "regexp" ) func addAdditions(providerGroups goldapps.Groups, providerUsers goldapps.Users) (goldapps.Groups, goldapps.Users) { @@ -133,4 +133,3 @@ func mergeAdditionalUsers(additionUsers goldapps.Users, providerUsers goldapps.U } return providerUsers } - diff --git a/cmd/main.go b/cmd/main.go index 6167e2d..f815c66 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -2,7 +2,7 @@ package main import ( "fmt" - "../../goldapps" + "github.com/cthit/goldapps" ) func init() { @@ -96,13 +96,13 @@ func main() { if groupErrors.Amount() == 0 { fmt.Println("All groups actions performed!") } else { - fmt.Printf("&d out of %d group actions performed\n", groupChanges.Amount()-groupErrors.Amount(), ) + fmt.Printf("&d out of %d group actions performed\n", groupChanges.Amount()-groupErrors.Amount()) fmt.Print(groupErrors.String()) } if userErrors.Amount() == 0 { fmt.Println("All groups actions performed!") } else { - fmt.Printf("&d out of %d group actions performed\n", userChanges.Amount()-userErrors.Amount(), ) + fmt.Printf("&d out of %d group actions performed\n", userChanges.Amount()-userErrors.Amount()) fmt.Print(userErrors.String()) } } @@ -228,4 +228,3 @@ func getUserChanges(proposedChanges goldapps.UserActions) goldapps.UserActions { } return proposedChanges } - diff --git a/cmd/services.go b/cmd/services.go index 807dd84..f51281f 100644 --- a/cmd/services.go +++ b/cmd/services.go @@ -1,16 +1,15 @@ package main import ( - "../ldap" - "../json" - "../admin" - "github.com/spf13/viper" - "../../goldapps" "fmt" + "github.com/cthit/goldapps" + "github.com/cthit/goldapps/admin" + "github.com/cthit/goldapps/json" + "github.com/cthit/goldapps/ldap" + "github.com/spf13/viper" "regexp" ) - func getConsumer() goldapps.UpdateService { var to string if flags.interactive { @@ -86,7 +85,7 @@ func getProvider() goldapps.CollectionService { } } -func collectGroups(service goldapps.CollectionService) (goldapps.Groups) { +func collectGroups(service goldapps.CollectionService) goldapps.Groups { groups, err := service.GetGroups() if err != nil { fmt.Println("Failed to collect groups") @@ -96,7 +95,7 @@ func collectGroups(service goldapps.CollectionService) (goldapps.Groups) { return groups } -func collectUsers(service goldapps.CollectionService) (goldapps.Users) { +func collectUsers(service goldapps.CollectionService) goldapps.Users { users, err := service.GetUsers() if err != nil { fmt.Println("Failed to collect users") diff --git a/json/service.go b/json/service.go index 5177c88..da1091a 100644 --- a/json/service.go +++ b/json/service.go @@ -1,9 +1,9 @@ package json import ( - "../../goldapps" "encoding/json" "fmt" + "github.com/cthit/goldapps" "io/ioutil" "os" ) diff --git a/ldap/service.go b/ldap/service.go index e08c7c9..b9d3ed7 100644 --- a/ldap/service.go +++ b/ldap/service.go @@ -3,8 +3,8 @@ package ldap import ( "crypto/tls" - "../../goldapps" "fmt" + "github.com/cthit/goldapps" "gopkg.in/ldap.v2" "strings" ) From 067249a51844e33678321ad02d2285620a9f4807 Mon Sep 17 00:00:00 2001 From: Gurgy Date: Tue, 4 Sep 2018 20:36:00 +0200 Subject: [PATCH 50/64] make sure to replace people without gdpr education too --- ldap/service.go | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/ldap/service.go b/ldap/service.go index b9d3ed7..95b7b4c 100644 --- a/ldap/service.go +++ b/ldap/service.go @@ -85,8 +85,12 @@ func (s ServiceLDAP) users() ([]*ldap.Entry, error) { return result.Entries, nil } -// Collect all users who are members of a committee func (s ServiceLDAP) GetUsers() ([]goldapps.User, error) { + return s.getUsers(true) +} + +// Collect all users who are members of a committee +func (s ServiceLDAP) getUsers(onlyPeopleWithGDPREducation bool) ([]goldapps.User, error) { users, err := s.users() if err != nil { return nil, err @@ -122,14 +126,14 @@ func (s ServiceLDAP) GetUsers() ([]goldapps.User, error) { for _, member := range group.GetAttributeValues("member") { for _, user := range parsePrivilegedGroupMember(member, users, groups.Entries) { if !privilegedUsers.Contains(user.GetAttributeValue("uid")) { - if user.GetAttributeValue("gdprEducated") == "TRUE" { // only add user if he's gdpr educated + if !onlyPeopleWithGDPREducation || user.GetAttributeValue("gdprEducated") == "TRUE" { // only add user if he's gdpr educated privilegedUsers = append(privilegedUsers, goldapps.User{ // TODO: Make these attribute values configurable - Cid: user.GetAttributeValue("uid"), - Nick: user.GetAttributeValue("nickname"), - FirstName: user.GetAttributeValue("givenName"), - SecondName: user.GetAttributeValue("sn"), - Mail: user.GetAttributeValue("mail"), + Cid: user.GetAttributeValue("uid"), + Nick: user.GetAttributeValue("nickname"), + FirstName: user.GetAttributeValue("givenName"), + SecondName: user.GetAttributeValue("sn"), + Mail: user.GetAttributeValue("mail"), }) } } @@ -293,14 +297,15 @@ func (s ServiceLDAP) GetGroups() ([]goldapps.Group, error) { // See above comments (only two for loops :D) // Fulhack deluxe :ok_hand: // this is fine.... + + xusers, err := s.getUsers(false) // Åh nej... + if err != nil { + return nil, err + } for _, group := range groups { if group.Type == "CommitteeDirect" { for i, member := range group.Members { replacementFound := false - xusers, err := s.GetUsers() // Åh nej... - if err != nil { - return nil, err - } for _, user := range xusers { if user.Mail == member { replacementFound = true From 4a780098b97ee2703479630f94ef4618312ef4ef Mon Sep 17 00:00:00 2001 From: Gurgy Date: Tue, 4 Sep 2018 20:41:59 +0200 Subject: [PATCH 51/64] Rearrange questions to represent the order in which changes will be committed --- cmd/main.go | 63 +++++++++++++++++++++++++++-------------------------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index f815c66..6789996 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -100,7 +100,7 @@ func main() { fmt.Print(groupErrors.String()) } if userErrors.Amount() == 0 { - fmt.Println("All groups actions performed!") + fmt.Println("All users actions performed!") } else { fmt.Printf("&d out of %d group actions performed\n", userChanges.Amount()-userErrors.Amount()) fmt.Print(userErrors.String()) @@ -116,21 +116,6 @@ func getGroupChanges(proposedChanges goldapps.GroupActions) goldapps.GroupAction len(proposedChanges.Updates), ) } else { - // Handle additions - fmt.Printf("(Groups) Additions (%d):\n", len(proposedChanges.Additions)) - if len(proposedChanges.Additions) > 0 { - for _, group := range proposedChanges.Additions { - fmt.Printf("\t%v\n", group) - } - add := askBool( - fmt.Sprintf("(Groups) Do you want to commit those %d additions?", len(proposedChanges.Additions)), - true, - ) - if !add { - proposedChanges.Additions = nil - } - } - // Handle Deletions fmt.Printf("(Groups) Deletions (%d):\n", len(proposedChanges.Deletions)) if len(proposedChanges.Deletions) > 0 { @@ -164,6 +149,21 @@ func getGroupChanges(proposedChanges goldapps.GroupActions) goldapps.GroupAction proposedChanges.Updates = nil } } + + // Handle additions + fmt.Printf("(Groups) Additions (%d):\n", len(proposedChanges.Additions)) + if len(proposedChanges.Additions) > 0 { + for _, group := range proposedChanges.Additions { + fmt.Printf("\t%v\n", group) + } + add := askBool( + fmt.Sprintf("(Groups) Do you want to commit those %d additions?", len(proposedChanges.Additions)), + true, + ) + if !add { + proposedChanges.Additions = nil + } + } } return proposedChanges } @@ -177,21 +177,6 @@ func getUserChanges(proposedChanges goldapps.UserActions) goldapps.UserActions { len(proposedChanges.Updates), ) } else { - // Handle additions - fmt.Printf("(Users) Additions (%d):\n", len(proposedChanges.Additions)) - if len(proposedChanges.Additions) > 0 { - for _, user := range proposedChanges.Additions { - fmt.Printf("\t%v\n", user) - } - add := askBool( - fmt.Sprintf("(Users) Do you want to commit those %d additions?", len(proposedChanges.Additions)), - true, - ) - if !add { - proposedChanges.Additions = nil - } - } - // Handle Deletions fmt.Printf("(Users) Deletions (%d):\n", len(proposedChanges.Deletions)) if len(proposedChanges.Deletions) > 0 { @@ -225,6 +210,22 @@ func getUserChanges(proposedChanges goldapps.UserActions) goldapps.UserActions { proposedChanges.Updates = nil } } + + // Handle additions + fmt.Printf("(Users) Additions (%d):\n", len(proposedChanges.Additions)) + if len(proposedChanges.Additions) > 0 { + for _, user := range proposedChanges.Additions { + fmt.Printf("\t%v\n", user) + } + add := askBool( + fmt.Sprintf("(Users) Do you want to commit those %d additions?", len(proposedChanges.Additions)), + true, + ) + if !add { + proposedChanges.Additions = nil + } + } + } return proposedChanges } From 35fce1944bc84a7969e02a943e9dadccfcf42ae5 Mon Sep 17 00:00:00 2001 From: Gurgy Date: Mon, 24 Sep 2018 08:56:27 +0200 Subject: [PATCH 52/64] Fix format for error information --- cmd/main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index 6789996..f984f39 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -96,13 +96,13 @@ func main() { if groupErrors.Amount() == 0 { fmt.Println("All groups actions performed!") } else { - fmt.Printf("&d out of %d group actions performed\n", groupChanges.Amount()-groupErrors.Amount()) + fmt.Printf("%d out of %d group actions performed\n", groupChanges.Amount()-groupErrors.Amount(), groupChanges.Amount()) fmt.Print(groupErrors.String()) } if userErrors.Amount() == 0 { fmt.Println("All users actions performed!") } else { - fmt.Printf("&d out of %d group actions performed\n", userChanges.Amount()-userErrors.Amount()) + fmt.Printf("%d out of %d group actions performed\n", userChanges.Amount()-userErrors.Amount(), groupChanges.Amount()) fmt.Print(userErrors.String()) } } From 5c94604234a742b52c9895f47562f37a35a41038 Mon Sep 17 00:00:00 2001 From: Gurgy Date: Mon, 24 Sep 2018 21:35:52 +0200 Subject: [PATCH 53/64] Refactor account group generation --- ldap/service.go | 73 ++++++++++++++++++++++--------------------------- 1 file changed, 33 insertions(+), 40 deletions(-) diff --git a/ldap/service.go b/ldap/service.go index 95b7b4c..e59bd87 100644 --- a/ldap/service.go +++ b/ldap/service.go @@ -86,11 +86,11 @@ func (s ServiceLDAP) users() ([]*ldap.Entry, error) { } func (s ServiceLDAP) GetUsers() ([]goldapps.User, error) { - return s.getUsers(true) + return s.getUsers() } // Collect all users who are members of a committee -func (s ServiceLDAP) getUsers(onlyPeopleWithGDPREducation bool) ([]goldapps.User, error) { +func (s ServiceLDAP) getUsers() ([]goldapps.User, error) { users, err := s.users() if err != nil { return nil, err @@ -126,9 +126,8 @@ func (s ServiceLDAP) getUsers(onlyPeopleWithGDPREducation bool) ([]goldapps.User for _, member := range group.GetAttributeValues("member") { for _, user := range parsePrivilegedGroupMember(member, users, groups.Entries) { if !privilegedUsers.Contains(user.GetAttributeValue("uid")) { - if !onlyPeopleWithGDPREducation || user.GetAttributeValue("gdprEducated") == "TRUE" { // only add user if he's gdpr educated + if user.GetAttributeValue("gdprEducated") == "TRUE" { // only add user if he's gdpr educated privilegedUsers = append(privilegedUsers, goldapps.User{ - // TODO: Make these attribute values configurable Cid: user.GetAttributeValue("uid"), Nick: user.GetAttributeValue("nickname"), FirstName: user.GetAttributeValue("givenName"), @@ -273,53 +272,47 @@ func (s ServiceLDAP) GetGroups() ([]goldapps.Group, error) { Members: treasurersGroupMembers, }) - // Dear god just please let me die - // TODO: FIXME: Refactor this, please. - for _, group := range groups { + accounts, err := s.getUsers() + if err != nil { + return nil, err + } + + for i, group := range groups { if group.Type == "Committee" { - for _, subGroup := range groups { - for _, memberEmail := range group.Members { - if subGroup.Email == memberEmail { - for i, userMail := range subGroup.Members { - for _, user := range users { - if userMail == user.GetAttributeValue("mail") { - subGroup.Members[i] = user.GetAttributeValue("uid") + "@chalmers.it" - } - } - } + for _, comitteeGroupMemberEmail := range group.Members { + + for j := range groups { + if groups[j].Email == comitteeGroupMemberEmail { + groups[j] = replaceWithAccountEmail(groups[j], accounts) } } } + } else if groups[i].Type == "CommitteeDirect" { + groups[i] = replaceWithAccountEmail(groups[i], accounts) } } - //FIXME!!! - // See above comments (only two for loops :D) - // Fulhack deluxe :ok_hand: - // this is fine.... + return groups, nil +} - xusers, err := s.getUsers(false) // Åh nej... - if err != nil { - return nil, err - } - for _, group := range groups { - if group.Type == "CommitteeDirect" { - for i, member := range group.Members { - replacementFound := false - for _, user := range xusers { - if user.Mail == member { - replacementFound = true - group.Members[i] = user.Cid + "@chalmers.it" - } - } - if !replacementFound { - return nil, fmt.Errorf("no replacement could be found for %s", member) - } +func replaceWithAccountEmail(group goldapps.Group, users goldapps.Users) goldapps.Group { + for i := 0; i < len(group.Members); i++ { + replacementFound := false + for _, user := range users { + if user.Mail == group.Members[i] { + replacementFound = true + group.Members[i] = user.Cid + "@chalmers.it" } } - } + if !replacementFound { + fmt.Printf("WARNING: no replacement could be found for %s in %s \n", group.Members[i], group.Email) - return groups, nil + //Remove member + group.Members = append(group.Members[:i], group.Members[i+1:]...) + i-- + } + } + return group } func (s ServiceLDAP) GetCustomGroups() ([]goldapps.Group, error) { From 2ac2256e3e812010e79fbee45d9bbd2757401d9e Mon Sep 17 00:00:00 2001 From: Gurgy Date: Wed, 26 Sep 2018 11:48:35 +0200 Subject: [PATCH 54/64] Add working dockerfile --- Dockerfile | 12 ++++++++++-- README.md | 13 ++++++++++++- prod.docker-compose.yaml | 14 ++++++++------ sleep_and_run.sh | 3 +++ 4 files changed, 33 insertions(+), 9 deletions(-) create mode 100755 sleep_and_run.sh diff --git a/Dockerfile b/Dockerfile index 006ae10..85f01ae 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,7 +9,7 @@ RUN apk add --update git # Copy sources RUN mkdir -p $GOPATH/src/github.com/cthit/goldapps -COPY ./src/github.com/cthit/goldapps $GOPATH/src/github.com/cthit/goldapps +COPY . $GOPATH/src/github.com/cthit/goldapps WORKDIR $GOPATH/src/github.com/cthit/goldapps/cmd # Grab dependencies @@ -25,15 +25,23 @@ RUN mkdir /app && mv $GOPATH/bin/cmd /app/goldapps FROM alpine MAINTAINER digIT +# Add standard certificates +RUN apk update && apk add ca-certificates && rm -rf /var/cache/apk/* + # Set user RUN addgroup -S app RUN adduser -S -G app -s /bin/bash app USER app:app +# Copy execution script +COPY ./sleep_and_run.sh /app/sleep_and_run.sh + # Copy binary COPY --from=buildStage /app/goldapps /app/goldapps +ENV WAIT 15s + # Set good defaults WORKDIR /app -ENTRYPOINT /app/goldapps +ENTRYPOINT ./sleep_and_run.sh CMD -dry \ No newline at end of file diff --git a/README.md b/README.md index c2f9130..8a926a1 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,18 @@ A system for syncing LDAP with gsuite and json files written in Go * use the downloaded file ## Usage -[docker image](https://hub.docker.com/r/cthit/goldapps/) + +Read setup first + +### Docker image + +`$WAIT` specifies for how long the application should wait before running. This can bes jused in conjunction with `restart: always` to make the bridge run at regular intervals. If you don't desire any waiting effect you can simply set the entrypoint to `./goldapps`. + +For some reason `entrypoint` has to be specified in the compose file or the docker run command. + +The command should be your flags for the `goldapps` command + +See `prod.docker-compose.yaml` as reference. ### Command `goldapps` diff --git a/prod.docker-compose.yaml b/prod.docker-compose.yaml index 3fcc5ac..1e30c6f 100644 --- a/prod.docker-compose.yaml +++ b/prod.docker-compose.yaml @@ -5,9 +5,11 @@ services: dockerfile: Dockerfile context: . image: goldapps:stable - command: -dry -y -# volumes: -# - some_config.toml:/app/config.toml:ro -# - google_api_key1.json:/app/gapps1.json:ro -# - google_api_key2.json:/app/gapps2.json:ro -# - data_for_json_groups:/app/data \ No newline at end of file + restart: always + entrypoint: ./sleep_and_run.sh + command: ["-from ldap", "-to gapps", "-y", "-dry"] + volumes: + - ./cmd/config.toml:/app/config.toml:ro + - ./cmd/gapps.json:/app/gapps.json:ro + environment: + - WAIT=2d \ No newline at end of file diff --git a/sleep_and_run.sh b/sleep_and_run.sh new file mode 100755 index 0000000..09161b6 --- /dev/null +++ b/sleep_and_run.sh @@ -0,0 +1,3 @@ +#!/bin/sh +echo Sleeping for "$WAIT" before running \"goldapps "$*"\" +sleep "$WAIT" && /app/goldapps $* From 2d8a0e1c085d90bdd9bcfec6c7d59fbc82bb366e Mon Sep 17 00:00:00 2001 From: Gurgy Date: Sun, 4 Nov 2018 12:32:37 +0100 Subject: [PATCH 55/64] Change commit order to make sure we dont get 'ghost groups' in google admin --- cmd/main.go | 2 +- group_action.go | 36 +++++++++++++++++++----------------- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index f984f39..071726a 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -89,8 +89,8 @@ func main() { } // Commit changes - groupErrors := groupChanges.Commit(consumer) userErrors := userChanges.Commit(consumer) + groupErrors := groupChanges.Commit(consumer) // Print result if groupErrors.Amount() == 0 { diff --git a/group_action.go b/group_action.go index 2f44b1a..d8ab559 100644 --- a/group_action.go +++ b/group_action.go @@ -1,8 +1,8 @@ package goldapps import ( - "fmt" "bytes" + "fmt" ) // Set of action, to be performed on a set of groups @@ -11,6 +11,7 @@ type GroupActions struct { Additions []Group Deletions []Group } + func (actions GroupActions) Amount() int { return len(actions.Additions) + len(actions.Deletions) + len(actions.Updates) } @@ -29,18 +30,19 @@ type GroupAddOrDelError struct { Action Group Error error } + func (actions GroupActionErrors) Amount() int { return len(actions.Additions) + len(actions.Deletions) + len(actions.Updates) } func (actions GroupActionErrors) String() string { builder := bytes.Buffer{} - for _,deletion := range actions.Deletions { + for _, deletion := range actions.Deletions { builder.WriteString(fmt.Sprintf("Deletion of group \"%s\" failed with error %s\n", deletion.Action.Email, deletion.Error.Error())) } - for _,update := range actions.Updates { + for _, update := range actions.Updates { builder.WriteString(fmt.Sprintf("Update of group \"%s\" failed with error %s\n", update.Action.After.Email, update.Error.Error())) } - for _,addition := range actions.Additions { + for _, addition := range actions.Additions { builder.WriteString(fmt.Sprintf("Addition of group \"%s\" failed with error %s\n", addition.Action.Email, addition.Error.Error())) } return builder.String() @@ -72,19 +74,6 @@ func (actions GroupActions) Commit(service UpdateService) GroupActionErrors { } } - if len(actions.Updates) > 0 { - fmt.Println("(Groups) Performing updates") - printProgress(0, len(actions.Updates), 0) - for updatesIndex, update := range actions.Updates { - err := service.UpdateGroup(update) - if err != nil { - // Save error - errors.Updates = append(errors.Updates, GroupUpdateError{Action: update, Error: err}) - } - printProgress(updatesIndex+1, len(actions.Updates), len(errors.Updates)) - } - } - if len(actions.Additions) > 0 { fmt.Println("(Groups) Performing additions") printProgress(0, len(actions.Additions), 0) @@ -98,6 +87,19 @@ func (actions GroupActions) Commit(service UpdateService) GroupActionErrors { } } + if len(actions.Updates) > 0 { + fmt.Println("(Groups) Performing updates") + printProgress(0, len(actions.Updates), 0) + for updatesIndex, update := range actions.Updates { + err := service.UpdateGroup(update) + if err != nil { + // Save error + errors.Updates = append(errors.Updates, GroupUpdateError{Action: update, Error: err}) + } + printProgress(updatesIndex+1, len(actions.Updates), len(errors.Updates)) + } + } + return errors } From 501530138a1a506b22614107a57ba3acb3d2a150 Mon Sep 17 00:00:00 2001 From: Gurgy Date: Sun, 20 Jan 2019 21:26:06 +0100 Subject: [PATCH 56/64] Sanitize email before sending to gogle --- admin/service_users.go | 8 ++++---- admin/util.go | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/admin/service_users.go b/admin/service_users.go index 3a6dbc3..0d7c786 100644 --- a/admin/service_users.go +++ b/admin/service_users.go @@ -31,12 +31,12 @@ func (s googleService) AddUser(user goldapps.User) error { time.Sleep(time.Second) // Add alias for nick@example.ex - return s.addUserAlias(fmt.Sprintf("%s@%s", user.Cid, s.domain), fmt.Sprintf("%s@%s", user.Nick, s.domain)) + return s.addUserAlias(goldapps.SanitizeEmail(fmt.Sprintf("%s@%s", user.Cid, s.domain)), goldapps.SanitizeEmail(fmt.Sprintf("%s@%s", user.Nick, s.domain))) } func (s googleService) UpdateUser(update goldapps.UserUpdate) error { _, err := s.adminService.Users.Update( - fmt.Sprintf("%s@%s", update.Before.Cid, s.domain), + goldapps.SanitizeEmail(fmt.Sprintf("%s@%s", update.Before.Cid, s.domain)), buildGoldappsUser(update.After, s.domain), ).Do() if err != nil { @@ -44,12 +44,12 @@ func (s googleService) UpdateUser(update goldapps.UserUpdate) error { } // Add alias for nick@example.ex - return s.addUserAlias(fmt.Sprintf("%s@%s", update.After.Cid, s.domain), fmt.Sprintf("%s@%s", update.After.Nick, s.domain)) + return s.addUserAlias(goldapps.SanitizeEmail(fmt.Sprintf("%s@%s", update.After.Cid, s.domain)), goldapps.SanitizeEmail(fmt.Sprintf("%s@%s", update.After.Nick, s.domain))) } func (s googleService) DeleteUser(user goldapps.User) error { admin := fmt.Sprintf("%s@%s", s.admin, s.domain) - userId := fmt.Sprintf("%s@%s", user.Cid, s.domain) + userId := goldapps.SanitizeEmail(fmt.Sprintf("%s@%s", user.Cid, s.domain)) if admin == userId { fmt.Printf("Skipping andmin user: %s\n", admin) } diff --git a/admin/util.go b/admin/util.go index 32951d8..81caffb 100644 --- a/admin/util.go +++ b/admin/util.go @@ -13,6 +13,6 @@ func buildGoldappsUser(user goldapps.User, domain string) *admin.User { GivenName: fmt.Sprintf("%s / %s", user.Nick, user.FirstName), }, IncludeInGlobalAddressList: true, - PrimaryEmail: fmt.Sprintf("%s@%s", user.Cid, domain), + PrimaryEmail: goldapps.SanitizeEmail(fmt.Sprintf("%s@%s", user.Cid, domain)), } } From 2b52173147f430bc87223ff598daf2ca322c5e4a Mon Sep 17 00:00:00 2001 From: Gurgy Date: Fri, 8 Feb 2019 09:12:19 +0100 Subject: [PATCH 57/64] Only sanitize user provided part of email This reverts commit 501530138a1a506b22614107a57ba3acb3d2a150. --- admin/service_users.go | 8 ++++---- admin/util.go | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/admin/service_users.go b/admin/service_users.go index 0d7c786..6d48c42 100644 --- a/admin/service_users.go +++ b/admin/service_users.go @@ -31,12 +31,12 @@ func (s googleService) AddUser(user goldapps.User) error { time.Sleep(time.Second) // Add alias for nick@example.ex - return s.addUserAlias(goldapps.SanitizeEmail(fmt.Sprintf("%s@%s", user.Cid, s.domain)), goldapps.SanitizeEmail(fmt.Sprintf("%s@%s", user.Nick, s.domain))) + return s.addUserAlias(fmt.Sprintf("%s@%s", goldapps.SanitizeEmail(user.Cid), s.domain), fmt.Sprintf("%s@%s", goldapps.SanitizeEmail(user.Nick), s.domain)) } func (s googleService) UpdateUser(update goldapps.UserUpdate) error { _, err := s.adminService.Users.Update( - goldapps.SanitizeEmail(fmt.Sprintf("%s@%s", update.Before.Cid, s.domain)), + fmt.Sprintf("%s@%s", goldapps.SanitizeEmail(update.Before.Cid), s.domain), buildGoldappsUser(update.After, s.domain), ).Do() if err != nil { @@ -44,12 +44,12 @@ func (s googleService) UpdateUser(update goldapps.UserUpdate) error { } // Add alias for nick@example.ex - return s.addUserAlias(goldapps.SanitizeEmail(fmt.Sprintf("%s@%s", update.After.Cid, s.domain)), goldapps.SanitizeEmail(fmt.Sprintf("%s@%s", update.After.Nick, s.domain))) + return s.addUserAlias(fmt.Sprintf("%s@%s", goldapps.SanitizeEmail(update.After.Cid), s.domain), fmt.Sprintf("%s@%s", goldapps.SanitizeEmail(update.After.Nick), s.domain)) } func (s googleService) DeleteUser(user goldapps.User) error { admin := fmt.Sprintf("%s@%s", s.admin, s.domain) - userId := goldapps.SanitizeEmail(fmt.Sprintf("%s@%s", user.Cid, s.domain)) + userId := fmt.Sprintf("%s@%s", goldapps.SanitizeEmail(user.Cid), s.domain) if admin == userId { fmt.Printf("Skipping andmin user: %s\n", admin) } diff --git a/admin/util.go b/admin/util.go index 81caffb..edfec84 100644 --- a/admin/util.go +++ b/admin/util.go @@ -13,6 +13,6 @@ func buildGoldappsUser(user goldapps.User, domain string) *admin.User { GivenName: fmt.Sprintf("%s / %s", user.Nick, user.FirstName), }, IncludeInGlobalAddressList: true, - PrimaryEmail: goldapps.SanitizeEmail(fmt.Sprintf("%s@%s", user.Cid, domain)), + PrimaryEmail: fmt.Sprintf("%s@%s", goldapps.SanitizeEmail(user.Cid), domain), } } From a1bc6836ba9f70b1b5d4e14ad7d065c19caab447 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Lev=C3=A9n?= Date: Sun, 12 May 2019 13:12:14 +0200 Subject: [PATCH 58/64] Add committee treasurers --- ldap/service.go | 75 +++++++++++++------------------------------------ 1 file changed, 19 insertions(+), 56 deletions(-) diff --git a/ldap/service.go b/ldap/service.go index e59bd87..b4dcd0d 100644 --- a/ldap/service.go +++ b/ldap/service.go @@ -245,7 +245,7 @@ func (s ServiceLDAP) GetGroups() ([]goldapps.Group, error) { } groups = append(groups, positionGroups...) - chairmenGroupMembers, err := s.getChairmenGroup() + chairmenGroupMembers, err := s.getRoleInGroups("ordf", false) if err != nil { return nil, err } @@ -254,7 +254,7 @@ func (s ServiceLDAP) GetGroups() ([]goldapps.Group, error) { Members: chairmenGroupMembers, }) - chairmenInCommitteesGroupMembers, err := s.getChairmenInCommitteesGroup() + chairmenInCommitteesGroupMembers, err := s.getRoleInGroups("ordf", true) if err != nil { return nil, err } @@ -263,7 +263,7 @@ func (s ServiceLDAP) GetGroups() ([]goldapps.Group, error) { Members: chairmenInCommitteesGroupMembers, }) - treasurersGroupMembers, err := s.getTreasurersGroup() + treasurersGroupMembers, err := s.getRoleInGroups("kassor", false) if err != nil { return nil, err } @@ -272,6 +272,15 @@ func (s ServiceLDAP) GetGroups() ([]goldapps.Group, error) { Members: treasurersGroupMembers, }) + treasurersInCommitteesGroupMembers, err := s.getRoleInGroups("kassor", true) + if err != nil { + return nil, err + } + groups = append(groups, goldapps.Group{ + Email: "kassorer.kommitteer@chalmers.it", + Members: treasurersInCommitteesGroupMembers, + }) + accounts, err := s.getUsers() if err != nil { return nil, err @@ -450,58 +459,12 @@ func (s ServiceLDAP) getPositionGroups() ([]goldapps.Group, error) { } -func (s ServiceLDAP) getChairmenGroup() ([]string, error) { - searchRequest := ldap.NewSearchRequest( - "ou=fkit,ou=groups,dc=chalmers,dc=it", // The base dn to search - ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, - "(&(objectClass=itPosition)(cn=ordf))", // The filter to apply - []string{"cn"}, // A list attributes to retrieve - nil, - ) - - result, err := s.Connection.Search(searchRequest) - if err != nil { - return nil, err - } - - var chairmenGroup []string - - for _, entry := range result.Entries { - dnSplit := strings.SplitN(entry.DN, ",", 3) - chairmenGroup = append(chairmenGroup, "ordf."+dnSplit[1][3:]+"@chalmers.it") - } - return chairmenGroup, nil -} - -func (s ServiceLDAP) getTreasurersGroup() ([]string, error) { - searchRequest := ldap.NewSearchRequest( - "ou=fkit,ou=groups,dc=chalmers,dc=it", // The base dn to search - ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, - "(&(objectClass=itPosition)(cn=kassor))", // The filter to apply - []string{"cn"}, // A list attributes to retrieve - nil, - ) - - result, err := s.Connection.Search(searchRequest) - if err != nil { - return nil, err - } - - var chairmenGroup []string - - for _, entry := range result.Entries { - dnSplit := strings.SplitN(entry.DN, ",", 3) - chairmenGroup = append(chairmenGroup, "kassor."+dnSplit[1][3:]+"@chalmers.it") - } - return chairmenGroup, nil -} - -func (s ServiceLDAP) getChairmenInCommitteesGroup() ([]string, error) { +func (s ServiceLDAP) getRoleInGroups(role string, onlyCommittees bool) ([]string, error) { searchRequest := ldap.NewSearchRequest( "ou=fkit,ou=groups,dc=chalmers,dc=it", // The base dn to search ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, - "(&(objectClass=itPosition)(cn=ordf))", // The filter to apply - []string{"cn"}, // A list attributes to retrieve + fmt.Sprintf("(&(objectClass=itPosition)(cn=%s))", role), // The filter to apply + []string{"cn"}, // A list attributes to retrieve nil, ) @@ -510,20 +473,20 @@ func (s ServiceLDAP) getChairmenInCommitteesGroup() ([]string, error) { return nil, err } - var chairmenInCommitteeGroup []string + var treasurersInCommitteeGroup []string for _, entry := range result.Entries { gtype, err := dnPositionType(s, entry.DN) if err != nil { return nil, err } - if gtype == "Committee" { + if !onlyCommittees || gtype == "Committee" { dnSplit := strings.SplitN(entry.DN, ",", 3) - chairmenInCommitteeGroup = append(chairmenInCommitteeGroup, "ordf."+dnSplit[1][3:]+"@chalmers.it") + treasurersInCommitteeGroup = append(treasurersInCommitteeGroup, fmt.Sprintf("%s.%s@chalmers.it", role, dnSplit[1][3:])) } } - return chairmenInCommitteeGroup, nil + return treasurersInCommitteeGroup, nil } func findEntry(ldapEntries []*ldap.Entry, DN string) *ldap.Entry { From deaf6fa3b34fcd3624416a165a3a913d7688b261 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Lev=C3=A9n?= Date: Sun, 12 May 2019 13:43:59 +0200 Subject: [PATCH 59/64] add additions to example compose file --- prod.docker-compose.yaml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/prod.docker-compose.yaml b/prod.docker-compose.yaml index 1e30c6f..3022090 100644 --- a/prod.docker-compose.yaml +++ b/prod.docker-compose.yaml @@ -7,9 +7,16 @@ services: image: goldapps:stable restart: always entrypoint: ./sleep_and_run.sh - command: ["-from ldap", "-to gapps", "-y", "-dry"] + command: [ + "-from ldap", + "-to gapps", + "-additions additions.json", + "-y", + "-dry" + ] volumes: - ./cmd/config.toml:/app/config.toml:ro - ./cmd/gapps.json:/app/gapps.json:ro + - ./cmd/additions.json:/app/additions.json:ro environment: - - WAIT=2d \ No newline at end of file + - WAIT=1h \ No newline at end of file From ffbcb67a4fc3f19136e3e8b47489c8c898e6f498 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Lev=C3=A9n?= Date: Sun, 12 May 2019 14:03:02 +0200 Subject: [PATCH 60/64] migrate to go modules --- Dockerfile | 10 +++--- go.mod | 13 ++++++++ go.sum | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+), 5 deletions(-) create mode 100644 go.mod create mode 100644 go.sum diff --git a/Dockerfile b/Dockerfile index 85f01ae..b6de58d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # Dockerfile for goldapps production -FROM golang:alpine AS buildStage +FROM golang:1.12-alpine AS buildStage MAINTAINER digIT # Install git @@ -8,12 +8,12 @@ RUN apk upgrade RUN apk add --update git # Copy sources -RUN mkdir -p $GOPATH/src/github.com/cthit/goldapps -COPY . $GOPATH/src/github.com/cthit/goldapps -WORKDIR $GOPATH/src/github.com/cthit/goldapps/cmd +RUN mkdir -p /goldapps +COPY . /goldapps +WORKDIR /goldapps/cmd # Grab dependencies -RUN go get -d -v ./... +#RUN go get -d -v ./... # build binary RUN go install -v diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..8b4c444 --- /dev/null +++ b/go.mod @@ -0,0 +1,13 @@ +module github.com/cthit/goldapps + +go 1.12 + +require ( + github.com/sethvargo/go-password v0.1.2 + github.com/spf13/viper v1.3.2 + golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5 + golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a + google.golang.org/api v0.5.0 + gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect + gopkg.in/ldap.v2 v2.5.1 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..ae982d9 --- /dev/null +++ b/go.sum @@ -0,0 +1,92 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0 h1:eOI3/cP2VTU6uZLDYAoic+eyzzB9YyGmJ7eIjl8rOPg= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sethvargo/go-password v0.1.2 h1:fhBF4thiPVKEZ7R6+CX46GWJiPyCyXshbeqZ7lqEeYo= +github.com/sethvargo/go-password v0.1.2/go.mod h1:qKHfdSjT26DpHQWHWWR5+X4BI45jT31dg6j4RI2TEb0= +github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5 h1:6M3SDHlHHDCx2PcQw3S4KsR170vGqDhJDOmpVd4Hjak= +golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a h1:tImsplftrFpALCYumobsd0K86vlAs/eXGFms2txfJfA= +golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +google.golang.org/api v0.5.0 h1:lj9SyhMzyoa38fgFF0oO2T6pjs5IzkLPKfVtxpyCRMM= +google.golang.org/api v0.5.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19 h1:Lj2SnHtxkRGJDqnGaSjo+CCdIieEnwVazbOXILwQemk= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d h1:TxyelI5cVkbREznMhfzycHdkp5cLA7DpE+GKjSslYhM= +gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/ldap.v2 v2.5.1 h1:wiu0okdNfjlBzg6UWvd1Hn8Y+Ux17/u/4nlk4CQr6tU= +gopkg.in/ldap.v2 v2.5.1/go.mod h1:oI0cpe/D7HRtBQl8aTg+ZmzFUAvu4lsv3eLXMLGFxWk= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= From 92d4209422dd2a2ab1ec4c0e595768e6c9a94cd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Lev=C3=A9n?= Date: Fri, 17 May 2019 18:03:51 +0200 Subject: [PATCH 61/64] change packet structure --- cmd/goldapps/main.go | 7 +++ {cmd => internal/app/cli}/additions.go | 14 ++--- cmd/main.go => internal/app/cli/bgc.go | 46 ++++++++-------- {cmd => internal/app/cli}/config.go | 2 +- {cmd => internal/app/cli}/flags.go | 4 +- {cmd => internal/app/cli}/services.go | 27 ++++----- {cmd => internal/app/cli}/util.go | 36 +++++++++++- .../pkg/actions/group_action.go | 45 +++++++-------- .../pkg/actions/user_action.go | 55 +++++++++---------- .../pkg/duplicates/duplicates.go | 29 +++++----- group.go => internal/pkg/model/group.go | 9 ++- user.go => internal/pkg/model/user.go | 11 +++- internal/pkg/model/util.go | 23 ++++++++ .../pkg/services/admin}/password.go | 0 .../pkg/services/admin}/scope.go | 0 .../pkg/services/admin}/service.go | 4 +- .../pkg/services/admin}/service_groups.go | 22 ++++---- .../pkg/services/admin}/service_users.go | 22 ++++---- .../pkg/services/admin}/util.go | 6 +- .../pkg/services/json}/service.go | 22 ++++---- .../pkg/services/ldap}/service.go | 48 ++++++++-------- internal/pkg/services/services.go | 20 +++++++ services.go | 16 ------ util.go | 54 ------------------ 24 files changed, 272 insertions(+), 250 deletions(-) create mode 100644 cmd/goldapps/main.go rename {cmd => internal/app/cli}/additions.go (86%) rename cmd/main.go => internal/app/cli/bgc.go (81%) rename {cmd => internal/app/cli}/config.go (97%) rename {cmd => internal/app/cli}/flags.go (94%) rename {cmd => internal/app/cli}/services.go (82%) rename {cmd => internal/app/cli}/util.go (50%) rename group_action.go => internal/pkg/actions/group_action.go (74%) rename user_action.go => internal/pkg/actions/user_action.go (71%) rename duplicates.go => internal/pkg/duplicates/duplicates.go (77%) rename group.go => internal/pkg/model/group.go (85%) rename user.go => internal/pkg/model/user.go (76%) create mode 100644 internal/pkg/model/util.go rename {admin => internal/pkg/services/admin}/password.go (100%) rename {admin => internal/pkg/services/admin}/scope.go (100%) rename {admin => internal/pkg/services/admin}/service.go (91%) rename {admin => internal/pkg/services/admin}/service_groups.go (86%) rename {admin => internal/pkg/services/admin}/service_users.go (75%) rename {admin => internal/pkg/services/admin}/util.go (58%) rename {json => internal/pkg/services/json}/service.go (83%) rename {ldap => internal/pkg/services/ldap}/service.go (91%) create mode 100644 internal/pkg/services/services.go delete mode 100644 services.go delete mode 100644 util.go diff --git a/cmd/goldapps/main.go b/cmd/goldapps/main.go new file mode 100644 index 0000000..b0fee8e --- /dev/null +++ b/cmd/goldapps/main.go @@ -0,0 +1,7 @@ +package main + +import "github.com/cthit/goldapps/internal/app/cli" + +func main() { + cli.Run() +} diff --git a/cmd/additions.go b/internal/app/cli/additions.go similarity index 86% rename from cmd/additions.go rename to internal/app/cli/additions.go index 13f3092..4ba6c60 100644 --- a/cmd/additions.go +++ b/internal/app/cli/additions.go @@ -1,13 +1,13 @@ -package main +package cli import ( "fmt" - "github.com/cthit/goldapps" - "github.com/cthit/goldapps/json" + "github.com/cthit/goldapps/internal/pkg/model" + "github.com/cthit/goldapps/internal/pkg/services/json" "regexp" ) -func addAdditions(providerGroups goldapps.Groups, providerUsers goldapps.Users) (goldapps.Groups, goldapps.Users) { +func addAdditions(providerGroups model.Groups, providerUsers model.Users) (model.Groups, model.Users) { fmt.Println("Collecting additions") additionUsers, additionGroups := getAdditions() if additionUsers != nil && additionGroups != nil { @@ -26,7 +26,7 @@ func addAdditions(providerGroups goldapps.Groups, providerUsers goldapps.Users) return providerGroups, providerUsers } -func getAdditions() ([]goldapps.User, []goldapps.Group) { +func getAdditions() ([]model.User, []model.Group) { var from string if flags.interactive { @@ -62,7 +62,7 @@ func getAdditions() ([]goldapps.User, []goldapps.Group) { } } -func mergeAdditionGroups(additionGroups goldapps.Groups, providerGroups goldapps.Groups) goldapps.Groups { +func mergeAdditionGroups(additionGroups model.Groups, providerGroups model.Groups) model.Groups { for _, group := range additionGroups { found := false for i, pgroup := range providerGroups { @@ -103,7 +103,7 @@ func mergeAdditionGroups(additionGroups goldapps.Groups, providerGroups goldapps } return providerGroups } -func mergeAdditionalUsers(additionUsers goldapps.Users, providerUsers goldapps.Users) goldapps.Users { +func mergeAdditionalUsers(additionUsers model.Users, providerUsers model.Users) model.Users { for _, user := range additionUsers { found := false for i, pUser := range providerUsers { diff --git a/cmd/main.go b/internal/app/cli/bgc.go similarity index 81% rename from cmd/main.go rename to internal/app/cli/bgc.go index 071726a..b06d937 100644 --- a/cmd/main.go +++ b/internal/app/cli/bgc.go @@ -1,8 +1,10 @@ -package main +package cli import ( "fmt" - "github.com/cthit/goldapps" + "github.com/cthit/goldapps/internal/pkg/actions" + "github.com/cthit/goldapps/internal/pkg/duplicates" + "github.com/cthit/goldapps/internal/pkg/model" ) func init() { @@ -16,29 +18,29 @@ func init() { fmt.Println("Loaded config.") } -func main() { +func Run() { - fmt.Println("Setting up provider") + fmt.Println("Setting up providers") provider := getProvider() - fmt.Println("Setting up consumer") + fmt.Println("Setting up services") consumer := getConsumer() // Collect users and groups - var providerUsers goldapps.Users - var consumerUsers goldapps.Users - var providerGroups goldapps.Groups - var consumerGroups goldapps.Groups + var providerUsers model.Users + var consumerUsers model.Users + var providerGroups model.Groups + var consumerGroups model.Groups if !flags.onlyUsers { - fmt.Println("Collecting groups from the provider...") + fmt.Println("Collecting groups from the providers...") providerGroups = collectGroups(provider) - fmt.Println("Collecting groups from the consumer...") + fmt.Println("Collecting groups from the services...") consumerGroups = collectGroups(consumer) } if !flags.onlyGroups { - fmt.Println("Collecting users from the provider...") + fmt.Println("Collecting users from the providers...") providerUsers = collectUsers(provider) - fmt.Println("Collecting users from the consumer...") + fmt.Println("Collecting users from the services...") consumerUsers = collectUsers(consumer) } @@ -46,19 +48,19 @@ func main() { providerGroups, providerUsers = addAdditions(providerGroups, providerUsers) // Check for and handle duplicates - providerUsers, providerGroups = goldapps.RemoveDuplicates(providerUsers, providerGroups) + providerUsers, providerGroups = duplicates.RemoveDuplicates(providerUsers, providerGroups) // Get changes to make - groupChanges := goldapps.GroupActions{} + groupChanges := actions.GroupActions{} if !flags.onlyUsers { - fmt.Println("Colculating difference between the consumer and provider groups.") - proposedGroupChanges := goldapps.GroupActionsRequired(consumerGroups, providerGroups) + fmt.Println("Colculating difference between the services and providers groups.") + proposedGroupChanges := actions.GroupActionsRequired(consumerGroups, providerGroups) groupChanges = getGroupChanges(proposedGroupChanges) } - userChanges := goldapps.UserActions{} + userChanges := actions.UserActions{} if !flags.onlyGroups { - fmt.Println("Colculating difference between the consumer and provider users.") - proposedUserChanges := goldapps.UserActionsRequired(consumerUsers, providerUsers) + fmt.Println("Colculating difference between the services and providers users.") + proposedUserChanges := actions.UserActionsRequired(consumerUsers, providerUsers) userChanges = getUserChanges(proposedUserChanges) } @@ -107,7 +109,7 @@ func main() { } } -func getGroupChanges(proposedChanges goldapps.GroupActions) goldapps.GroupActions { +func getGroupChanges(proposedChanges actions.GroupActions) actions.GroupActions { if !flags.interactive && flags.noInteraction { fmt.Printf( "(Groups) Automaticly accepting %d addition, %d deletions and %d updates\n", @@ -168,7 +170,7 @@ func getGroupChanges(proposedChanges goldapps.GroupActions) goldapps.GroupAction return proposedChanges } -func getUserChanges(proposedChanges goldapps.UserActions) goldapps.UserActions { +func getUserChanges(proposedChanges actions.UserActions) actions.UserActions { if !flags.interactive && flags.noInteraction { fmt.Printf( "(Users) Automaticly accepting %d addition, %d deletions and %d updates\n", diff --git a/cmd/config.go b/internal/app/cli/config.go similarity index 97% rename from cmd/config.go rename to internal/app/cli/config.go index f3bc89d..9085474 100644 --- a/cmd/config.go +++ b/internal/app/cli/config.go @@ -1,4 +1,4 @@ -package main +package cli import ( "github.com/spf13/viper" diff --git a/cmd/flags.go b/internal/app/cli/flags.go similarity index 94% rename from cmd/flags.go rename to internal/app/cli/flags.go index c3c11b1..ce7dbd6 100644 --- a/cmd/flags.go +++ b/internal/app/cli/flags.go @@ -1,4 +1,4 @@ -package main +package cli import "flag" @@ -18,7 +18,7 @@ var flags = flagStruct{} func loadFlags() { flag.StringVar(&flags.from, "from", "ldap", "Set the source to 'ldap', 'gapps' or '*.json'. In case of gapps config value 'gappsProvider' will be used") flag.StringVar(&flags.additions, "additions", "", "Set a json file for additional groups and users") - flag.StringVar(&flags.to, "to", "gapps", "Set the consumer to 'gapps' or '*.json'") + flag.StringVar(&flags.to, "to", "gapps", "Set the services to 'gapps' or '*.json'") flag.BoolVar(&flags.dryRun, "dry", false, "Setting this flag will cause the application to only print information and not update any groups") flag.BoolVar(&flags.noInteraction, "y", false, "Setting this flag will cause the application to not ask for any user confirmation") flag.BoolVar(&flags.interactive, "i", false, "Setting this flag will cause the application to ask the user for input in every stage ") diff --git a/cmd/services.go b/internal/app/cli/services.go similarity index 82% rename from cmd/services.go rename to internal/app/cli/services.go index f51281f..5f5a14f 100644 --- a/cmd/services.go +++ b/internal/app/cli/services.go @@ -1,19 +1,20 @@ -package main +package cli import ( "fmt" - "github.com/cthit/goldapps" - "github.com/cthit/goldapps/admin" - "github.com/cthit/goldapps/json" - "github.com/cthit/goldapps/ldap" + "github.com/cthit/goldapps/internal/pkg/model" + "github.com/cthit/goldapps/internal/pkg/services" + "github.com/cthit/goldapps/internal/pkg/services/admin" + "github.com/cthit/goldapps/internal/pkg/services/json" + "github.com/cthit/goldapps/internal/pkg/services/ldap" "github.com/spf13/viper" "regexp" ) -func getConsumer() goldapps.UpdateService { +func getConsumer() services.UpdateService { var to string if flags.interactive { - to = askString("Which consumer would you like to use, 'gapps' or '*.json?", "gapps") + to = askString("Which services would you like to use, 'gapps' or '*.json?", "gapps") } else { to = flags.to } @@ -34,7 +35,7 @@ func getConsumer() goldapps.UpdateService { consumer, _ := json.NewJsonService(to) return consumer } else { - fmt.Println("You must specify 'gapps' or '*.json' as consumer.") + fmt.Println("You must specify 'gapps' or '*.json' as services.") previous := flags.interactive flags.interactive = true defer func() { @@ -45,10 +46,10 @@ func getConsumer() goldapps.UpdateService { } } -func getProvider() goldapps.CollectionService { +func getProvider() services.CollectionService { var from string if flags.interactive { - from = askString("which provider would you like to use, 'ldap', 'gapps' or '*.json'?", "ldap") + from = askString("which providers would you like to use, 'ldap', 'gapps' or '*.json'?", "ldap") } else { from = flags.from } @@ -74,7 +75,7 @@ func getProvider() goldapps.CollectionService { provider, _ := json.NewJsonService(from) return provider } else { - fmt.Println("You must specify 'gapps', 'ldap' or '*.json' as provider.") + fmt.Println("You must specify 'gapps', 'ldap' or '*.json' as providers.") previous := flags.interactive flags.interactive = true defer func() { @@ -85,7 +86,7 @@ func getProvider() goldapps.CollectionService { } } -func collectGroups(service goldapps.CollectionService) goldapps.Groups { +func collectGroups(service services.CollectionService) model.Groups { groups, err := service.GetGroups() if err != nil { fmt.Println("Failed to collect groups") @@ -95,7 +96,7 @@ func collectGroups(service goldapps.CollectionService) goldapps.Groups { return groups } -func collectUsers(service goldapps.CollectionService) goldapps.Users { +func collectUsers(service services.CollectionService) model.Users { users, err := service.GetUsers() if err != nil { fmt.Println("Failed to collect users") diff --git a/cmd/util.go b/internal/app/cli/util.go similarity index 50% rename from cmd/util.go rename to internal/app/cli/util.go index 049741c..a8c5eb2 100644 --- a/cmd/util.go +++ b/internal/app/cli/util.go @@ -1,6 +1,9 @@ -package main +package cli -import "fmt" +import ( + "bytes" + "fmt" +) func askBool(question string, preferred bool) bool { if preferred { @@ -36,3 +39,32 @@ func askString(question string, preferred string) string { return input } } + +// Done does include failed too +func printProgress(done, total, failed int) { + p := (done * 100) / total + builder := bytes.Buffer{} + for i := 0; i <= 100; i++ { + if i < p { + builder.WriteByte('=') + } else if i == p { + builder.WriteByte('>') + } else { + builder.WriteByte(' ') + } + } + fmt.Printf("\rProgress: [%s] %d/%d", builder.String(), done, total) + + // Add failed counter if necessary + if failed != 0 { + fmt.Printf(" (Failed: %d)", failed) + } + + // Replace progressbar with done text + if done == total { + if failed != 0 { + fmt.Printf("Done! (Failed: %d)", failed) + } + fmt.Printf("\rDone\n") + } +} diff --git a/group_action.go b/internal/pkg/actions/group_action.go similarity index 74% rename from group_action.go rename to internal/pkg/actions/group_action.go index d8ab559..df5265b 100644 --- a/group_action.go +++ b/internal/pkg/actions/group_action.go @@ -1,15 +1,17 @@ -package goldapps +package actions import ( "bytes" "fmt" + "github.com/cthit/goldapps/internal/pkg/model" + "github.com/cthit/goldapps/internal/pkg/services" ) // Set of action, to be performed on a set of groups type GroupActions struct { - Updates []GroupUpdate - Additions []Group - Deletions []Group + Updates []model.GroupUpdate + Additions []model.Group + Deletions []model.Group } func (actions GroupActions) Amount() int { @@ -23,11 +25,11 @@ type GroupActionErrors struct { Deletions []GroupAddOrDelError } type GroupUpdateError struct { - Action GroupUpdate + Action model.GroupUpdate Error error } type GroupAddOrDelError struct { - Action Group + Action model.Group Error error } @@ -48,55 +50,48 @@ func (actions GroupActionErrors) String() string { return builder.String() } -// Data struct representing how a group looks not and how it should look after an update -// Allows for efficient updates as application doesn't have to re-upload whole group -type GroupUpdate struct { - Before Group - After Group -} - // Commits a set of actions to a service. // Returns all actions performed and a error if not all actions could be performed for some reason. -func (actions GroupActions) Commit(service UpdateService) GroupActionErrors { +func (actions GroupActions) Commit(service services.UpdateService) GroupActionErrors { errors := GroupActionErrors{} if len(actions.Deletions) > 0 { fmt.Println("(Groups) Performing deletions") - printProgress(0, len(actions.Deletions), 0) - for deletionsIndex, group := range actions.Deletions { + // printProgress(0, len(actions.Deletions), 0) + for _, group := range actions.Deletions { err := service.DeleteGroup(group) if err != nil { // Save error errors.Deletions = append(errors.Deletions, GroupAddOrDelError{Action: group, Error: err}) } - printProgress(deletionsIndex+1, len(actions.Deletions), len(errors.Deletions)) + // printProgress(deletionsIndex+1, len(actions.Deletions), len(errors.Deletions)) } } if len(actions.Additions) > 0 { fmt.Println("(Groups) Performing additions") - printProgress(0, len(actions.Additions), 0) - for additionsIndex, group := range actions.Additions { + // printProgress(0, len(actions.Additions), 0) + for _, group := range actions.Additions { err := service.AddGroup(group) if err != nil { // Save error errors.Additions = append(errors.Additions, GroupAddOrDelError{Action: group, Error: err}) } - printProgress(additionsIndex+1, len(actions.Additions), len(errors.Additions)) + // printProgress(additionsIndex+1, len(actions.Additions), len(errors.Additions)) } } if len(actions.Updates) > 0 { fmt.Println("(Groups) Performing updates") - printProgress(0, len(actions.Updates), 0) - for updatesIndex, update := range actions.Updates { + // printProgress(0, len(actions.Updates), 0) + for _, update := range actions.Updates { err := service.UpdateGroup(update) if err != nil { // Save error errors.Updates = append(errors.Updates, GroupUpdateError{Action: update, Error: err}) } - printProgress(updatesIndex+1, len(actions.Updates), len(errors.Updates)) + // printProgress(updatesIndex+1, len(actions.Updates), len(errors.Updates)) } } @@ -105,7 +100,7 @@ func (actions GroupActions) Commit(service UpdateService) GroupActionErrors { // Determines actions required to make the "old" group list look as the "new" group list. // Returns a list with those actions. -func GroupActionsRequired(old []Group, new []Group) GroupActions { +func GroupActionsRequired(old []model.Group, new []model.Group) GroupActions { requiredActions := GroupActions{} for _, newGroup := range new { @@ -118,7 +113,7 @@ func GroupActionsRequired(old []Group, new []Group) GroupActions { // check if group has to be updates if !newGroup.Equals(oldGroup) { // Add group update - requiredActions.Updates = append(requiredActions.Updates, GroupUpdate{ + requiredActions.Updates = append(requiredActions.Updates, model.GroupUpdate{ Before: oldGroup, After: newGroup, }) diff --git a/user_action.go b/internal/pkg/actions/user_action.go similarity index 71% rename from user_action.go rename to internal/pkg/actions/user_action.go index ad81d16..5ec8006 100644 --- a/user_action.go +++ b/internal/pkg/actions/user_action.go @@ -1,16 +1,19 @@ -package goldapps +package actions import ( - "fmt" "bytes" + "fmt" + "github.com/cthit/goldapps/internal/pkg/model" + "github.com/cthit/goldapps/internal/pkg/services" ) // Set of action to be performed on a set of users type UserActions struct { - Updates []UserUpdate - Additions []User - Deletions []User + Updates []model.UserUpdate + Additions []model.User + Deletions []model.User } + func (actions UserActions) Amount() int { return len(actions.Additions) + len(actions.Deletions) + len(actions.Updates) } @@ -22,79 +25,73 @@ type UserActionErrors struct { Deletions []UserAddOrDelError } type UserUpdateError struct { - Action UserUpdate + Action model.UserUpdate Error error } type UserAddOrDelError struct { - Action User + Action model.User Error error } + func (actions UserActionErrors) Amount() int { return len(actions.Additions) + len(actions.Deletions) + len(actions.Updates) } func (actions UserActionErrors) String() string { builder := bytes.Buffer{} - for _,deletion := range actions.Deletions { + for _, deletion := range actions.Deletions { builder.WriteString(fmt.Sprintf("Deletion of user \"%s\" failed with error %s\n", deletion.Action.Cid, deletion.Error.Error())) } - for _,update := range actions.Updates { + for _, update := range actions.Updates { builder.WriteString(fmt.Sprintf("Update of user \"%s\" failed with error %s\n", update.Action.After.Cid, update.Error.Error())) } - for _,addition := range actions.Additions { + for _, addition := range actions.Additions { builder.WriteString(fmt.Sprintf("Addition of user \"%s\" failed with error %s\n", addition.Action.Cid, addition.Error.Error())) } return builder.String() } -// Data struct representing how a user should look before and after an update -// Allows for efficient updates as application doesn't have to re-upload whole user -type UserUpdate struct { - Before User - After User -} - // Commits a set of actions to a service. // Returns all actions performed and a error if not all actions could be performed for some reason. -func (actions UserActions) Commit(service UpdateService) UserActionErrors { +func (actions UserActions) Commit(service services.UpdateService) UserActionErrors { errors := UserActionErrors{} if len(actions.Deletions) > 0 { fmt.Println("(Users) Performing deletions") - printProgress(0, len(actions.Deletions), 0) - for deletionsIndex, user := range actions.Deletions { + // printProgress(0, len(actions.Deletions), 0) + for _, user := range actions.Deletions { err := service.DeleteUser(user) if err != nil { // Save error errors.Deletions = append(errors.Deletions, UserAddOrDelError{Action: user, Error: err}) } - printProgress(deletionsIndex+1, len(actions.Deletions), len(errors.Deletions)) + // printProgress(deletionsIndex+1, len(actions.Deletions), len(errors.Deletions)) } } if len(actions.Updates) > 0 { fmt.Println("(USers) Performing updates") - printProgress(0, len(actions.Updates), 0) - for updatesIndex, update := range actions.Updates { + // printProgress(0, len(actions.Updates), 0) + for _, update := range actions.Updates { err := service.UpdateUser(update) if err != nil { // Save error errors.Updates = append(errors.Updates, UserUpdateError{Action: update, Error: err}) } - printProgress(updatesIndex+1, len(actions.Updates), len(errors.Updates)) + // printProgress(updatesIndex+1, len(actions.Updates), len(errors.Updates)) } } if len(actions.Additions) > 0 { fmt.Println("(Groups) Performing additions") - printProgress(0, len(actions.Additions), 0) - for additionsIndex, user := range actions.Additions { + // printProgress(0, len(actions.Additions), 0) + for _, user := range actions.Additions { err := service.AddUser(user) if err != nil { // Save error errors.Additions = append(errors.Additions, UserAddOrDelError{Action: user, Error: err}) } - printProgress(additionsIndex+1, len(actions.Additions), len(errors.Additions)) + // printProgress(additionsIndex+1, len(actions.Additions), len(errors.Additions)) } } @@ -103,7 +100,7 @@ func (actions UserActions) Commit(service UpdateService) UserActionErrors { // Determines actions required to make the "old" user list look as the "new" user list. // Returns a list with those actions. -func UserActionsRequired(old []User, new []User) UserActions { +func UserActionsRequired(old []model.User, new []model.User) UserActions { requiredActions := UserActions{} for _, newUser := range new { @@ -115,7 +112,7 @@ func UserActionsRequired(old []User, new []User) UserActions { // check if user has to be updates if !newUser.Equals(oldUser) { // Add User update - requiredActions.Updates = append(requiredActions.Updates, UserUpdate{ + requiredActions.Updates = append(requiredActions.Updates, model.UserUpdate{ Before: oldUser, After: newUser, }) diff --git a/duplicates.go b/internal/pkg/duplicates/duplicates.go similarity index 77% rename from duplicates.go rename to internal/pkg/duplicates/duplicates.go index b6845ef..43b0f87 100644 --- a/duplicates.go +++ b/internal/pkg/duplicates/duplicates.go @@ -1,16 +1,17 @@ -package goldapps +package duplicates import ( + "github.com/cthit/goldapps/internal/pkg/model" "strings" ) -func RemoveDuplicates(users Users, groups Groups) (Users, Groups) { +func RemoveDuplicates(users model.Users, groups model.Groups) (model.Users, model.Groups) { // Compare Users with Groups for i, user := range users { for k := 0; k < len(groups); k++ { // Check if any cid conflicts with any group name - if CompareEmails(user.Cid, extractIdentifier(groups[k].Email)) { + if model.CompareEmails(user.Cid, extractIdentifier(groups[k].Email)) { if groups[k].Expendable { groups = removeArrayGroup(groups, k) k-- // don't breaking the loop @@ -21,7 +22,7 @@ func RemoveDuplicates(users Users, groups Groups) (Users, Groups) { } } // Check if any user nick conflicts with any group name - if CompareEmails(user.Nick, extractIdentifier(groups[k].Email)) { + if model.CompareEmails(user.Nick, extractIdentifier(groups[k].Email)) { if groups[k].Expendable { groups = removeArrayGroup(groups, k) k-- // don't breaking the loop @@ -33,7 +34,7 @@ func RemoveDuplicates(users Users, groups Groups) (Users, Groups) { for aliasIndex, alias := range groups[k].Aliases { // Check if any cid conflicts with any group alias - if CompareEmails(user.Cid, extractIdentifier(alias)) { + if model.CompareEmails(user.Cid, extractIdentifier(alias)) { if groups[k].Expendable { groups[k] = removeAlias(groups[k], aliasIndex) } else { @@ -43,7 +44,7 @@ func RemoveDuplicates(users Users, groups Groups) (Users, Groups) { } } // Check if any Nick conflicts with any group alias - if CompareEmails(user.Nick, extractIdentifier(alias)) { + if model.CompareEmails(user.Nick, extractIdentifier(alias)) { if groups[k].Expendable { groups[k] = removeAlias(groups[k], aliasIndex) } else { @@ -61,18 +62,18 @@ func RemoveDuplicates(users Users, groups Groups) (Users, Groups) { // Don't check with itself if i != j { // Compare cids - if CompareEmails(user.Cid, otherUser.Cid) { + if model.CompareEmails(user.Cid, otherUser.Cid) { // Should not be able to happen panic("two users with cid: " + user.Cid) } // Compare Nicks - if CompareEmails(user.Nick, otherUser.Nick) { + if model.CompareEmails(user.Nick, otherUser.Nick) { // Nicks are not that important users[i].Nick = "" users[j].Nick = "" } // Compare cids with nicks - if CompareEmails(user.Cid, otherUser.Nick) { + if model.CompareEmails(user.Cid, otherUser.Nick) { // Nicks are not that important users[j].Nick = "" } @@ -86,19 +87,19 @@ func RemoveDuplicates(users Users, groups Groups) (Users, Groups) { // Don't check with itself if i != j { // Compare Emails - if CompareEmails(group.Email, otherGroup.Email) { + if model.CompareEmails(group.Email, otherGroup.Email) { // Something is set up wrong panic("two groups with email: " + group.Email) } for _, alias := range group.Aliases { // Compare emails with aliases - if CompareEmails(alias, otherGroup.Email) { + if model.CompareEmails(alias, otherGroup.Email) { // Something is set up wrong panic("two groups with alias/email: " + group.Email + ", " + otherGroup.Email) } for _, otherAlias := range otherGroup.Aliases { // Compare aliases with aliases - if CompareEmails(alias, otherAlias) { + if model.CompareEmails(alias, otherAlias) { // Something is set up wrong panic("two groups with alias: " + alias) } @@ -110,7 +111,7 @@ func RemoveDuplicates(users Users, groups Groups) (Users, Groups) { return users, groups } -func removeArrayGroup(s Groups, i int) Groups { +func removeArrayGroup(s model.Groups, i int) model.Groups { s[len(s)-1], s[i] = s[i], s[len(s)-1] return s[:len(s)-1] } @@ -120,7 +121,7 @@ func removeArrayString(s []string, i int) []string { return s[:len(s)-1] } -func removeAlias(group Group, aliasIndex int) Group { +func removeAlias(group model.Group, aliasIndex int) model.Group { group.Aliases = removeArrayString(group.Aliases, aliasIndex) return group } diff --git a/group.go b/internal/pkg/model/group.go similarity index 85% rename from group.go rename to internal/pkg/model/group.go index 90085d8..914cd3f 100644 --- a/group.go +++ b/internal/pkg/model/group.go @@ -1,4 +1,4 @@ -package goldapps +package model // Represents a email group. // Email is the id and main email for the group. @@ -14,6 +14,13 @@ type Group struct { type Groups []Group +// Data struct representing how a group looks not and how it should look after an update +// Allows for efficient updates as application doesn't have to re-upload whole group +type GroupUpdate struct { + Before Group + After Group +} + // Search for groupname(email) in list of groups func (groups Groups) Contains(email string) bool { for _, group := range groups { diff --git a/user.go b/internal/pkg/model/user.go similarity index 76% rename from user.go rename to internal/pkg/model/user.go index b816bc9..c923578 100644 --- a/user.go +++ b/internal/pkg/model/user.go @@ -1,4 +1,4 @@ -package goldapps +package model import ( "strings" @@ -14,6 +14,13 @@ type User struct { type Users []User +// Data struct representing how a user should look before and after an update +// Allows for efficient updates as application doesn't have to re-upload whole user +type UserUpdate struct { + Before User + After User +} + // Search for username(cid) in list of groups func (users Users) Contains(cid string) bool { for _, user := range users { @@ -45,7 +52,7 @@ func (user User) Equals(other User) bool { return false } - // Don't check email as its not saved in every consumer atm + // Don't check email as its not saved in every services atm /*if user.Mail != other.Mail { return false }*/ diff --git a/internal/pkg/model/util.go b/internal/pkg/model/util.go new file mode 100644 index 0000000..5f8ab25 --- /dev/null +++ b/internal/pkg/model/util.go @@ -0,0 +1,23 @@ +package model + +import ( + "strings" +) + +func CompareEmails(email, other string) bool { + return SanitizeEmail(email) == SanitizeEmail(other) +} + +func SanitizeEmail(s string) string { + s = strings.ToLower(s) + s = strings.Replace(s, "π", "pi", -1) + s = strings.Replace(s, "å", "a", -1) + s = strings.Replace(s, "ä", "a", -1) + s = strings.Replace(s, "ö", "o", -1) + s = strings.Replace(s, "ö", "o", -1) + s = strings.Replace(s, "ø", "o", -1) + s = strings.Replace(s, "æ", "ae", -1) + s = strings.Replace(s, " ", "-", -1) + s = strings.Replace(s, ".", "", -1) + return s +} diff --git a/admin/password.go b/internal/pkg/services/admin/password.go similarity index 100% rename from admin/password.go rename to internal/pkg/services/admin/password.go diff --git a/admin/scope.go b/internal/pkg/services/admin/scope.go similarity index 100% rename from admin/scope.go rename to internal/pkg/services/admin/scope.go diff --git a/admin/service.go b/internal/pkg/services/admin/service.go similarity index 91% rename from admin/service.go rename to internal/pkg/services/admin/service.go index dc55fa5..2222ae8 100644 --- a/admin/service.go +++ b/internal/pkg/services/admin/service.go @@ -1,7 +1,7 @@ package admin import ( - "github.com/cthit/goldapps" + "github.com/cthit/goldapps/internal/pkg/services" "google.golang.org/api/admin/directory/v1" // Imports as admin "google.golang.org/api/gmail/v1" // Imports as gmail @@ -25,7 +25,7 @@ type googleService struct { domain string } -func NewGoogleService(keyPath string, adminMail string) (goldapps.UpdateService, error) { +func NewGoogleService(keyPath string, adminMail string) (services.UpdateService, error) { jsonKey, err := ioutil.ReadFile(keyPath) if err != nil { diff --git a/admin/service_groups.go b/internal/pkg/services/admin/service_groups.go similarity index 86% rename from admin/service_groups.go rename to internal/pkg/services/admin/service_groups.go index b250e24..58bdadd 100644 --- a/admin/service_groups.go +++ b/internal/pkg/services/admin/service_groups.go @@ -3,17 +3,17 @@ package admin import ( "bytes" "fmt" - "github.com/cthit/goldapps" + "github.com/cthit/goldapps/internal/pkg/model" "google.golang.org/api/admin/directory/v1" // Imports as admin "time" ) -func (s googleService) DeleteGroup(group goldapps.Group) error { +func (s googleService) DeleteGroup(group model.Group) error { err := s.adminService.Groups.Delete(group.Email).Do() return err } -func (s googleService) UpdateGroup(groupUpdate goldapps.GroupUpdate) error { +func (s googleService) UpdateGroup(groupUpdate model.GroupUpdate) error { newGroup := admin.Group{ Email: groupUpdate.Before.Email, } @@ -22,7 +22,7 @@ func (s googleService) UpdateGroup(groupUpdate goldapps.GroupUpdate) error { for _, member := range groupUpdate.After.Members { exists := false for _, existingMember := range groupUpdate.Before.Members { - if goldapps.CompareEmails(member, existingMember) { + if model.CompareEmails(member, existingMember) { exists = true break } @@ -40,7 +40,7 @@ func (s googleService) UpdateGroup(groupUpdate goldapps.GroupUpdate) error { for _, existingMember := range groupUpdate.Before.Members { keep := false for _, member := range groupUpdate.After.Members { - if goldapps.CompareEmails(existingMember, member) { + if model.CompareEmails(existingMember, member) { keep = true break } @@ -57,7 +57,7 @@ func (s googleService) UpdateGroup(groupUpdate goldapps.GroupUpdate) error { for _, alias := range groupUpdate.After.Aliases { exists := false for _, existingAlias := range groupUpdate.Before.Aliases { - if goldapps.CompareEmails(alias, existingAlias) { + if model.CompareEmails(alias, existingAlias) { exists = true break } @@ -74,7 +74,7 @@ func (s googleService) UpdateGroup(groupUpdate goldapps.GroupUpdate) error { for _, existingAlias := range groupUpdate.Before.Aliases { keep := false for _, alias := range groupUpdate.After.Aliases { - if goldapps.CompareEmails(existingAlias, alias) { + if model.CompareEmails(existingAlias, alias) { keep = true break } @@ -91,7 +91,7 @@ func (s googleService) UpdateGroup(groupUpdate goldapps.GroupUpdate) error { return err } -func (s googleService) AddGroup(group goldapps.Group) error { +func (s googleService) AddGroup(group model.Group) error { newGroup := admin.Group{ Email: group.Email, } @@ -121,14 +121,14 @@ func (s googleService) AddGroup(group goldapps.Group) error { return nil } -func (s googleService) GetGroups() ([]goldapps.Group, error) { +func (s googleService) GetGroups() ([]model.Group, error) { adminGroups, err := s.getGoogleGroups(googleCustomer) if err != nil { return nil, err } - groups := make([]goldapps.Group, len(adminGroups)) + groups := make([]model.Group, len(adminGroups)) for i, group := range adminGroups { p := (i * 100) / len(groups) @@ -152,7 +152,7 @@ func (s googleService) GetGroups() ([]goldapps.Group, error) { return nil, err } - groups[i] = goldapps.Group{ + groups[i] = model.Group{ Email: group.Email, Members: members, Aliases: group.Aliases, diff --git a/admin/service_users.go b/internal/pkg/services/admin/service_users.go similarity index 75% rename from admin/service_users.go rename to internal/pkg/services/admin/service_users.go index 6d48c42..0cbf6b5 100644 --- a/admin/service_users.go +++ b/internal/pkg/services/admin/service_users.go @@ -2,13 +2,13 @@ package admin import ( "fmt" - "github.com/cthit/goldapps" + "github.com/cthit/goldapps/internal/pkg/model" "google.golang.org/api/admin/directory/v1" // Imports as admin "strings" "time" ) -func (s googleService) AddUser(user goldapps.User) error { +func (s googleService) AddUser(user model.User) error { usr := buildGoldappsUser(user, s.domain) @@ -31,12 +31,12 @@ func (s googleService) AddUser(user goldapps.User) error { time.Sleep(time.Second) // Add alias for nick@example.ex - return s.addUserAlias(fmt.Sprintf("%s@%s", goldapps.SanitizeEmail(user.Cid), s.domain), fmt.Sprintf("%s@%s", goldapps.SanitizeEmail(user.Nick), s.domain)) + return s.addUserAlias(fmt.Sprintf("%s@%s", model.SanitizeEmail(user.Cid), s.domain), fmt.Sprintf("%s@%s", model.SanitizeEmail(user.Nick), s.domain)) } -func (s googleService) UpdateUser(update goldapps.UserUpdate) error { +func (s googleService) UpdateUser(update model.UserUpdate) error { _, err := s.adminService.Users.Update( - fmt.Sprintf("%s@%s", goldapps.SanitizeEmail(update.Before.Cid), s.domain), + fmt.Sprintf("%s@%s", model.SanitizeEmail(update.Before.Cid), s.domain), buildGoldappsUser(update.After, s.domain), ).Do() if err != nil { @@ -44,12 +44,12 @@ func (s googleService) UpdateUser(update goldapps.UserUpdate) error { } // Add alias for nick@example.ex - return s.addUserAlias(fmt.Sprintf("%s@%s", goldapps.SanitizeEmail(update.After.Cid), s.domain), fmt.Sprintf("%s@%s", goldapps.SanitizeEmail(update.After.Nick), s.domain)) + return s.addUserAlias(fmt.Sprintf("%s@%s", model.SanitizeEmail(update.After.Cid), s.domain), fmt.Sprintf("%s@%s", model.SanitizeEmail(update.After.Nick), s.domain)) } -func (s googleService) DeleteUser(user goldapps.User) error { +func (s googleService) DeleteUser(user model.User) error { admin := fmt.Sprintf("%s@%s", s.admin, s.domain) - userId := fmt.Sprintf("%s@%s", goldapps.SanitizeEmail(user.Cid), s.domain) + userId := fmt.Sprintf("%s@%s", model.SanitizeEmail(user.Cid), s.domain) if admin == userId { fmt.Printf("Skipping andmin user: %s\n", admin) } @@ -58,12 +58,12 @@ func (s googleService) DeleteUser(user goldapps.User) error { return err } -func (s googleService) GetUsers() ([]goldapps.User, error) { +func (s googleService) GetUsers() ([]model.User, error) { adminUsers, err := s.getGoogleUsers(googleCustomer) if err != nil { return nil, err } - users := make([]goldapps.User, len(adminUsers)-1) + users := make([]model.User, len(adminUsers)-1) admin := fmt.Sprintf("%s@%s", s.admin, s.domain) @@ -81,7 +81,7 @@ func (s googleService) GetUsers() ([]goldapps.User, error) { // Extracting cid form (cid@example.ex) cid := strings.Split(adminUser.PrimaryEmail, "@")[0] - users[i] = goldapps.User{ + users[i] = model.User{ Cid: cid, FirstName: firstName, SecondName: adminUser.Name.FamilyName, diff --git a/admin/util.go b/internal/pkg/services/admin/util.go similarity index 58% rename from admin/util.go rename to internal/pkg/services/admin/util.go index edfec84..ada3ada 100644 --- a/admin/util.go +++ b/internal/pkg/services/admin/util.go @@ -2,17 +2,17 @@ package admin import ( "fmt" - "github.com/cthit/goldapps" + "github.com/cthit/goldapps/internal/pkg/model" "google.golang.org/api/admin/directory/v1" // Imports as admin ) -func buildGoldappsUser(user goldapps.User, domain string) *admin.User { +func buildGoldappsUser(user model.User, domain string) *admin.User { return &admin.User{ Name: &admin.UserName{ FamilyName: user.SecondName, GivenName: fmt.Sprintf("%s / %s", user.Nick, user.FirstName), }, IncludeInGlobalAddressList: true, - PrimaryEmail: fmt.Sprintf("%s@%s", goldapps.SanitizeEmail(user.Cid), domain), + PrimaryEmail: fmt.Sprintf("%s@%s", model.SanitizeEmail(user.Cid), domain), } } diff --git a/json/service.go b/internal/pkg/services/json/service.go similarity index 83% rename from json/service.go rename to internal/pkg/services/json/service.go index da1091a..345226c 100644 --- a/json/service.go +++ b/internal/pkg/services/json/service.go @@ -3,7 +3,7 @@ package json import ( "encoding/json" "fmt" - "github.com/cthit/goldapps" + "github.com/cthit/goldapps/internal/pkg/model" "io/ioutil" "os" ) @@ -13,11 +13,11 @@ type Service struct { } type dataObject struct { - Groups []goldapps.Group `json:"groups"` - Users []goldapps.User `json:"users"` + Groups []model.Group `json:"groups"` + Users []model.User `json:"users"` } -func (s Service) DeleteUser(user goldapps.User) error { +func (s Service) DeleteUser(user model.User) error { groups, err := s.GetGroups() if err != nil { return err @@ -39,7 +39,7 @@ func (s Service) DeleteUser(user goldapps.User) error { return fmt.Errorf("user not found %v", user) } -func (s Service) UpdateUser(update goldapps.UserUpdate) error { +func (s Service) UpdateUser(update model.UserUpdate) error { groups, err := s.GetGroups() if err != nil { return err @@ -61,7 +61,7 @@ func (s Service) UpdateUser(update goldapps.UserUpdate) error { return fmt.Errorf("user not found %v", update.Before) } -func (s Service) AddUser(user goldapps.User) error { +func (s Service) AddUser(user model.User) error { groups, err := s.GetGroups() if err != nil { return err @@ -80,7 +80,7 @@ func (s Service) AddUser(user goldapps.User) error { return err } -func (s Service) GetUsers() ([]goldapps.User, error) { +func (s Service) GetUsers() ([]model.User, error) { data, err := s.get() if err != nil { @@ -135,7 +135,7 @@ func (s Service) get() (dataObject, error) { return data, nil } -func (s Service) DeleteGroup(group goldapps.Group) error { +func (s Service) DeleteGroup(group model.Group) error { groups, err := s.GetGroups() if err != nil { return err @@ -156,7 +156,7 @@ func (s Service) DeleteGroup(group goldapps.Group) error { return fmt.Errorf("group not found %v", group) } -func (s Service) UpdateGroup(groupUpdate goldapps.GroupUpdate) error { +func (s Service) UpdateGroup(groupUpdate model.GroupUpdate) error { groups, err := s.GetGroups() if err != nil { return err @@ -178,7 +178,7 @@ func (s Service) UpdateGroup(groupUpdate goldapps.GroupUpdate) error { return fmt.Errorf("group not found %v", groupUpdate.Before) } -func (s Service) AddGroup(group goldapps.Group) error { +func (s Service) AddGroup(group model.Group) error { groups, err := s.GetGroups() if err != nil { return err @@ -197,7 +197,7 @@ func (s Service) AddGroup(group goldapps.Group) error { return err } -func (s Service) GetGroups() ([]goldapps.Group, error) { +func (s Service) GetGroups() ([]model.Group, error) { data, err := s.get() if err != nil { diff --git a/ldap/service.go b/internal/pkg/services/ldap/service.go similarity index 91% rename from ldap/service.go rename to internal/pkg/services/ldap/service.go index b4dcd0d..e62b4f1 100644 --- a/ldap/service.go +++ b/internal/pkg/services/ldap/service.go @@ -4,7 +4,7 @@ import ( "crypto/tls" "fmt" - "github.com/cthit/goldapps" + "github.com/cthit/goldapps/internal/pkg/model" "gopkg.in/ldap.v2" "strings" ) @@ -85,12 +85,12 @@ func (s ServiceLDAP) users() ([]*ldap.Entry, error) { return result.Entries, nil } -func (s ServiceLDAP) GetUsers() ([]goldapps.User, error) { +func (s ServiceLDAP) GetUsers() ([]model.User, error) { return s.getUsers() } // Collect all users who are members of a committee -func (s ServiceLDAP) getUsers() ([]goldapps.User, error) { +func (s ServiceLDAP) getUsers() ([]model.User, error) { users, err := s.users() if err != nil { return nil, err @@ -111,8 +111,8 @@ func (s ServiceLDAP) getUsers() ([]goldapps.User, error) { return nil, err } - // Create an empty goldapps.Group slice - privilegedUsers := make(goldapps.Users, 0) + // Create an empty model.Group slice + privilegedUsers := make(model.Users, 0) for _, group := range groups.Entries { // TODO: What qualified as a privileged group should be made configurable. See FIXME:s @@ -127,7 +127,7 @@ func (s ServiceLDAP) getUsers() ([]goldapps.User, error) { for _, user := range parsePrivilegedGroupMember(member, users, groups.Entries) { if !privilegedUsers.Contains(user.GetAttributeValue("uid")) { if user.GetAttributeValue("gdprEducated") == "TRUE" { // only add user if he's gdpr educated - privilegedUsers = append(privilegedUsers, goldapps.User{ + privilegedUsers = append(privilegedUsers, model.User{ Cid: user.GetAttributeValue("uid"), Nick: user.GetAttributeValue("nickname"), FirstName: user.GetAttributeValue("givenName"), @@ -168,8 +168,8 @@ func parsePrivilegedGroupMember(memberDN string, users []*ldap.Entry, groups []* } // Collects all committees from LDAP and then creates a -// goldapps.Group slice. -func (s ServiceLDAP) GetGroups() ([]goldapps.Group, error) { +// model.Group slice. +func (s ServiceLDAP) GetGroups() ([]model.Group, error) { users, err := s.users() if err != nil { return nil, err @@ -190,14 +190,14 @@ func (s ServiceLDAP) GetGroups() ([]goldapps.Group, error) { return nil, err } - // Creates an empty goldapps.Group slice - groups := make([]goldapps.Group, 0) + // Creates an empty model.Group slice + groups := make([]model.Group, 0) - // Creates a goldapps.Group with appropriate mails and members + // Creates a model.Group with appropriate mails and members for _, entry := range committees.Entries { - // Creates a goldapps.Group with it's mail - committee := goldapps.Group{ + // Creates a model.Group with it's mail + committee := model.Group{ Email: entry.GetAttributeValue("mail"), Type: entry.GetAttributeValue("type"), Members: nil, @@ -249,7 +249,7 @@ func (s ServiceLDAP) GetGroups() ([]goldapps.Group, error) { if err != nil { return nil, err } - groups = append(groups, goldapps.Group{ + groups = append(groups, model.Group{ Email: "ordforanden@chalmers.it", Members: chairmenGroupMembers, }) @@ -258,7 +258,7 @@ func (s ServiceLDAP) GetGroups() ([]goldapps.Group, error) { if err != nil { return nil, err } - groups = append(groups, goldapps.Group{ + groups = append(groups, model.Group{ Email: "ordforanden.kommitteer@chalmers.it", Members: chairmenInCommitteesGroupMembers, }) @@ -267,7 +267,7 @@ func (s ServiceLDAP) GetGroups() ([]goldapps.Group, error) { if err != nil { return nil, err } - groups = append(groups, goldapps.Group{ + groups = append(groups, model.Group{ Email: "kassorer@chalmers.it", Members: treasurersGroupMembers, }) @@ -276,7 +276,7 @@ func (s ServiceLDAP) GetGroups() ([]goldapps.Group, error) { if err != nil { return nil, err } - groups = append(groups, goldapps.Group{ + groups = append(groups, model.Group{ Email: "kassorer.kommitteer@chalmers.it", Members: treasurersInCommitteesGroupMembers, }) @@ -304,7 +304,7 @@ func (s ServiceLDAP) GetGroups() ([]goldapps.Group, error) { return groups, nil } -func replaceWithAccountEmail(group goldapps.Group, users goldapps.Users) goldapps.Group { +func replaceWithAccountEmail(group model.Group, users model.Users) model.Group { for i := 0; i < len(group.Members); i++ { replacementFound := false for _, user := range users { @@ -324,13 +324,13 @@ func replaceWithAccountEmail(group goldapps.Group, users goldapps.Users) goldapp return group } -func (s ServiceLDAP) GetCustomGroups() ([]goldapps.Group, error) { +func (s ServiceLDAP) GetCustomGroups() ([]model.Group, error) { users, err := s.users() if err != nil { return nil, err } - customGroups := make([]goldapps.Group, 0) + customGroups := make([]model.Group, 0) for _, entry := range s.CustomEntryConfigs { // Creates a search request to collect all committees from LDAP @@ -399,7 +399,7 @@ func (s ServiceLDAP) GetCustomGroups() ([]goldapps.Group, error) { } } - group := goldapps.Group{ + group := model.Group{ Email: entry.Mail, Members: members, } @@ -410,13 +410,13 @@ func (s ServiceLDAP) GetCustomGroups() ([]goldapps.Group, error) { return customGroups, nil } -func (s ServiceLDAP) getPositionGroups() ([]goldapps.Group, error) { +func (s ServiceLDAP) getPositionGroups() ([]model.Group, error) { users, err := s.users() if err != nil { return nil, err } - var positionGroups []goldapps.Group + var positionGroups []model.Group searchRequest := ldap.NewSearchRequest( "ou=fkit,ou=groups,dc=chalmers,dc=it", // The base dn to search @@ -440,7 +440,7 @@ func (s ServiceLDAP) getPositionGroups() ([]goldapps.Group, error) { return nil, err } - posGroup := goldapps.Group{ + posGroup := model.Group{ Email: fmt.Sprintf("%s.%s@chalmers.it", pos, grp), Type: groupType + "Direct", } diff --git a/internal/pkg/services/services.go b/internal/pkg/services/services.go new file mode 100644 index 0000000..a46a44f --- /dev/null +++ b/internal/pkg/services/services.go @@ -0,0 +1,20 @@ +package services + +import ( + "github.com/cthit/goldapps/internal/pkg/model" +) + +type CollectionService interface { + GetGroups() ([]model.Group, error) + GetUsers() ([]model.User, error) +} + +type UpdateService interface { + DeleteGroup(model.Group) error + UpdateGroup(model.GroupUpdate) error + AddGroup(model.Group) error + DeleteUser(model.User) error + UpdateUser(model.UserUpdate) error + AddUser(model.User) error + CollectionService +} diff --git a/services.go b/services.go deleted file mode 100644 index 2d630ca..0000000 --- a/services.go +++ /dev/null @@ -1,16 +0,0 @@ -package goldapps - -type UpdateService interface { - DeleteGroup(Group) error - UpdateGroup(GroupUpdate) error - AddGroup(Group) error - DeleteUser(User) error - UpdateUser(UserUpdate) error - AddUser(User) error - CollectionService -} - -type CollectionService interface { - GetGroups() ([]Group, error) - GetUsers() ([]User, error) -} diff --git a/util.go b/util.go deleted file mode 100644 index 7726a21..0000000 --- a/util.go +++ /dev/null @@ -1,54 +0,0 @@ -package goldapps - -import ( - "bytes" - "fmt" - "strings" -) - -// Done does include failed too -func printProgress(done, total, failed int) { - p := (done * 100) / total - builder := bytes.Buffer{} - for i := 0; i <= 100; i++ { - if i < p { - builder.WriteByte('=') - } else if i == p { - builder.WriteByte('>') - } else { - builder.WriteByte(' ') - } - } - fmt.Printf("\rProgress: [%s] %d/%d", builder.String(), done, total) - - // Add failed counter if necessary - if failed != 0 { - fmt.Printf(" (Failed: %d)", failed) - } - - // Replace progressbar with done text - if done == total { - if failed != 0 { - fmt.Printf("Done! (Failed: %d)", failed) - } - fmt.Printf("\rDone\n") - } -} - -func CompareEmails(email, other string) bool { - return SanitizeEmail(email) == SanitizeEmail(other) -} - -func SanitizeEmail(s string) string { - s = strings.ToLower(s) - s = strings.Replace(s, "π", "pi", -1) - s = strings.Replace(s, "å", "a", -1) - s = strings.Replace(s, "ä", "a", -1) - s = strings.Replace(s, "ö", "o", -1) - s = strings.Replace(s, "ö", "o", -1) - s = strings.Replace(s, "ø", "o", -1) - s = strings.Replace(s, "æ", "ae", -1) - s = strings.Replace(s, " ", "-", -1) - s = strings.Replace(s, ".", "", -1) - return s -} From 15c1d92b4d5d2685f788cc2c962cd47aa288039c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Lev=C3=A9n?= Date: Sat, 18 May 2019 08:28:07 +0200 Subject: [PATCH 62/64] remove progress printing --- internal/pkg/actions/group_action.go | 3 --- internal/pkg/actions/user_action.go | 6 ------ 2 files changed, 9 deletions(-) diff --git a/internal/pkg/actions/group_action.go b/internal/pkg/actions/group_action.go index df5265b..1f4e48f 100644 --- a/internal/pkg/actions/group_action.go +++ b/internal/pkg/actions/group_action.go @@ -65,7 +65,6 @@ func (actions GroupActions) Commit(service services.UpdateService) GroupActionEr // Save error errors.Deletions = append(errors.Deletions, GroupAddOrDelError{Action: group, Error: err}) } - // printProgress(deletionsIndex+1, len(actions.Deletions), len(errors.Deletions)) } } @@ -78,7 +77,6 @@ func (actions GroupActions) Commit(service services.UpdateService) GroupActionEr // Save error errors.Additions = append(errors.Additions, GroupAddOrDelError{Action: group, Error: err}) } - // printProgress(additionsIndex+1, len(actions.Additions), len(errors.Additions)) } } @@ -91,7 +89,6 @@ func (actions GroupActions) Commit(service services.UpdateService) GroupActionEr // Save error errors.Updates = append(errors.Updates, GroupUpdateError{Action: update, Error: err}) } - // printProgress(updatesIndex+1, len(actions.Updates), len(errors.Updates)) } } diff --git a/internal/pkg/actions/user_action.go b/internal/pkg/actions/user_action.go index 5ec8006..e17e131 100644 --- a/internal/pkg/actions/user_action.go +++ b/internal/pkg/actions/user_action.go @@ -58,40 +58,34 @@ func (actions UserActions) Commit(service services.UpdateService) UserActionErro if len(actions.Deletions) > 0 { fmt.Println("(Users) Performing deletions") - // printProgress(0, len(actions.Deletions), 0) for _, user := range actions.Deletions { err := service.DeleteUser(user) if err != nil { // Save error errors.Deletions = append(errors.Deletions, UserAddOrDelError{Action: user, Error: err}) } - // printProgress(deletionsIndex+1, len(actions.Deletions), len(errors.Deletions)) } } if len(actions.Updates) > 0 { fmt.Println("(USers) Performing updates") - // printProgress(0, len(actions.Updates), 0) for _, update := range actions.Updates { err := service.UpdateUser(update) if err != nil { // Save error errors.Updates = append(errors.Updates, UserUpdateError{Action: update, Error: err}) } - // printProgress(updatesIndex+1, len(actions.Updates), len(errors.Updates)) } } if len(actions.Additions) > 0 { fmt.Println("(Groups) Performing additions") - // printProgress(0, len(actions.Additions), 0) for _, user := range actions.Additions { err := service.AddUser(user) if err != nil { // Save error errors.Additions = append(errors.Additions, UserAddOrDelError{Action: user, Error: err}) } - // printProgress(additionsIndex+1, len(actions.Additions), len(errors.Additions)) } } From 0117182e1a4603eb88189465edcef0f204dcd4b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Lev=C3=A9n?= Date: Sun, 19 May 2019 09:56:00 +0200 Subject: [PATCH 63/64] rewrite email sanetation --- go.mod | 1 + internal/pkg/actions/user_action.go | 2 +- internal/pkg/model/util.go | 31 +++++++++++++++++++---------- internal/pkg/model/util_test.go | 15 ++++++++++++++ 4 files changed, 38 insertions(+), 11 deletions(-) create mode 100644 internal/pkg/model/util_test.go diff --git a/go.mod b/go.mod index 8b4c444..2466d6f 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/cthit/goldapps go 1.12 require ( + github.com/magiconair/properties v1.8.0 github.com/sethvargo/go-password v0.1.2 github.com/spf13/viper v1.3.2 golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5 diff --git a/internal/pkg/actions/user_action.go b/internal/pkg/actions/user_action.go index e17e131..ba4d29e 100644 --- a/internal/pkg/actions/user_action.go +++ b/internal/pkg/actions/user_action.go @@ -68,7 +68,7 @@ func (actions UserActions) Commit(service services.UpdateService) UserActionErro } if len(actions.Updates) > 0 { - fmt.Println("(USers) Performing updates") + fmt.Println("(Users) Performing updates") for _, update := range actions.Updates { err := service.UpdateUser(update) if err != nil { diff --git a/internal/pkg/model/util.go b/internal/pkg/model/util.go index 5f8ab25..f6dbdfc 100644 --- a/internal/pkg/model/util.go +++ b/internal/pkg/model/util.go @@ -1,6 +1,7 @@ package model import ( + "regexp" "strings" ) @@ -8,16 +9,26 @@ func CompareEmails(email, other string) bool { return SanitizeEmail(email) == SanitizeEmail(other) } +// Only work on the part before the @ +// You are only supposed to send in the part to the left of the @ func SanitizeEmail(s string) string { + s = strings.TrimSpace(s) s = strings.ToLower(s) - s = strings.Replace(s, "π", "pi", -1) - s = strings.Replace(s, "å", "a", -1) - s = strings.Replace(s, "ä", "a", -1) - s = strings.Replace(s, "ö", "o", -1) - s = strings.Replace(s, "ö", "o", -1) - s = strings.Replace(s, "ø", "o", -1) - s = strings.Replace(s, "æ", "ae", -1) - s = strings.Replace(s, " ", "-", -1) - s = strings.Replace(s, ".", "", -1) - return s + replacelist := map[string]string{ + "π": "pi", + "å": "a", + "ä": "a", + "ö": "o", + "ø": "o", + "æ": "ae", + " ": "-", + } + allowed := regexp.MustCompile("[a-z]|[0-9]|-") + parts := strings.Split(s, "") + for i := range parts { + if !allowed.MatchString(parts[i]) { + parts[i] = replacelist[parts[i]] + } + } + return strings.Join(parts, "") } diff --git a/internal/pkg/model/util_test.go b/internal/pkg/model/util_test.go new file mode 100644 index 0000000..f7aa51e --- /dev/null +++ b/internal/pkg/model/util_test.go @@ -0,0 +1,15 @@ +package model + +import ( + "github.com/magiconair/properties/assert" + "testing" +) + +func TestSanitizeEmail(t *testing.T) { + assert.Equal(t, SanitizeEmail("123abc"), "123abc") + assert.Equal(t, SanitizeEmail("123aBc"), "123abc") + assert.Equal(t, SanitizeEmail("123 abc"), "123-abc") + assert.Equal(t, SanitizeEmail("123-abc"), "123-abc") + assert.Equal(t, SanitizeEmail("123*abc"), "123abc") + assert.Equal(t, SanitizeEmail("123öabc"), "123oabc") +} From 5a55c0913f4d48c6713826f833ca58b7a49f8e08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Lev=C3=A9n?= Date: Thu, 23 May 2019 20:45:29 +0200 Subject: [PATCH 64/64] Update config files --- Dockerfile | 4 +-- chalmers.it.config.toml | 64 ---------------------------------------- example.config.toml | 50 ++++++++++++++++++------------- prod.docker-compose.yaml | 6 ++-- 4 files changed, 35 insertions(+), 89 deletions(-) delete mode 100644 chalmers.it.config.toml diff --git a/Dockerfile b/Dockerfile index b6de58d..aef30af 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,14 +10,14 @@ RUN apk add --update git # Copy sources RUN mkdir -p /goldapps COPY . /goldapps -WORKDIR /goldapps/cmd +WORKDIR /goldapps/cmd/goldapps # Grab dependencies #RUN go get -d -v ./... # build binary RUN go install -v -RUN mkdir /app && mv $GOPATH/bin/cmd /app/goldapps +RUN mkdir /app && mv $GOPATH/bin/goldapps /app/goldapps ########################## # PRODUCTION STAGE # diff --git a/chalmers.it.config.toml b/chalmers.it.config.toml deleted file mode 100644 index d134f80..0000000 --- a/chalmers.it.config.toml +++ /dev/null @@ -1,64 +0,0 @@ -[gapps.consumer] - servicekeyfile = "gapps.json" - adminaccount = "admin@chalmers.it" - -[gapps.provider] - servicekeyfile = "gapps.json" - adminaccount = "admin@chalmers.it" - -[ldap] - url = "ldap.chalmers.it:636" - servername = "chalmers.it" - user = "cn=SERVICE_ACCOUNT_GOES_HERE,dc=chalmers,dc=it" - password = "PASSWORD_GOES_HERE" - custom = ["chairman", "chairmen.fkit", "chairmen.committees", "treasurers", "phadderchef", "fkit"] - -[ldap.groups] - basedn = "ou=fkit,ou=groups,dc=chalmers,dc=it" - filter = "(|(objectClass=itGroup)(objectClass=itPosition))" - attributes = ["cn", "displayName", "mail", "member", "type"] - -[ldap.users] - basedn = "ou=people,dc=chalmers,dc=it" - filter = "(&(objectClass=chalmersstudent))" - attributes = ["uid", "givenName", "sn", "nickname", "mail", "gdprEducated"] - -#### CUSTOM FILTERS #### -[ldap.fkit] - mail = "fkit@chalmers.it" - basedn = "ou=fkit,ou=groups,dc=chalmers,dc=it" - filter = "(&(objectClass=itGroup))" - parent_filter = "(&(ou=%childRDN%))" - attributes = ["cn", "displayName", "mail"] - -[ldap.chairman] - mail = "ordforande@chalmers.it" - basedn = "ou=styrit,ou=fkit,ou=groups,dc=chalmers,dc=it" - filter = "(&(objectClass=itPosition)(cn=ordf))" - attributes = ["cn", "displayName", "mail"] - -[ldap.chairmen.fkit] - mail = "ordforanden@chalmers.it" - basedn = "ou=fkit,ou=groups,dc=chalmers,dc=it" - filter = "(&(objectClass=itPosition)(cn=ordf))" - attributes = ["cn", "displayName", "mail"] - -[ldap.chairmen.committees] - mail = "ordforanden.kommiteer@chalmers.it" - basedn = "ou=fkit,ou=groups,dc=chalmers,dc=it" - filter = "(&(objectClass=itPosition)(cn=ordf))" - parent_filter = "(&(objectClass=itGroup)(type=Committee))" - attributes = ["cn", "displayName", "mail", "type"] - -[ldap.treasurers] - mail = "kassorer@chalmers.it" - basedn = "ou=fkit,ou=groups,dc=chalmers,dc=it" - filter = "(&(objectClass=itPosition)(cn=kassor))" - attributes = ["cn", "displayName", "mail"] - -[ldap.phadderchef] - mail = "phadderchef@chalmers.it" - basedn = "ou=nollkit,ou=fkit,ou=groups,dc=chalmers,dc=it" - filter = "(&(objectClass=itPosition)(cn=phadderchef))" - attributes = ["cn", "displayName", "mail"] -#### ============== #### diff --git a/example.config.toml b/example.config.toml index 9c0f9de..8212381 100644 --- a/example.config.toml +++ b/example.config.toml @@ -1,31 +1,41 @@ [gapps.consumer] - servicekeyfile = "gapps1.json" - adminaccount = "admin@example1.ex" + servicekeyfile = "gapps.json" + adminaccount = "admin@mydomain.ex" [gapps.provider] - servicekeyfile = "gapps2.json" - adminaccount = "admin@example2.ex" + servicekeyfile = "gapps.json" + adminaccount = "admin@mydomain.ex" [ldap] - url = "ldap.example.ex:999" - servername = "example.ex" - user = "cn=god,dc=example,dc=ex" - password = "secret" - custom = ["my_custom_filter"] + url = "ldap.mydomain.ex:636" + servername = "mydomain.ex" + user = "cn=admin,dc=mydomain,dc=ex" + password = "PASSWORD" + custom = ["fkit", "kit"] [ldap.groups] - basedn = "ou=some,ou=groups,dc=example,dc=ex" - filter = "(&(objectClass=Group))" - attributes = ["cn", "displayName", "mail", "member"] + basedn = "ou=groups,dc=mydomain,dc=ex" + filter = "(|(objectClass=itGroup)(objectClass=itPosition))" + attibutes = ["cn", "displayName", "mail", "member"] [ldap.users] - basedn = "ou=people,dc=example,dc=ex" - filter = "(&(objectClass=Group))" - attributes = ["uid", "mail"] + basedn = "ou=people,dc=mydomain,dc=ex" + filter = "(&(objectClass=chalmersstudent))" + attibutes = ["uid", "mail"] -[ldap.my_custom_filter] - mail = "custom@example.ex" - basedn = "ou=groups,dc=chalmers,dc=it" - filter = "(&(objectClass=Group))" +#### CUSTOM FILTERS #### +[ldap.fkit] + mail = "fkit@mydomain.ex" + basedn = "ou=fkit,ou=groups,dc=mydomain,dc=ex" + filter = "(&(objectClass=itGroup))" parent_filter = "(&(ou=%childRDN%))" - attributes = ["cn", "displayName", "mail"] + attibutes = ["cn", "displayName", "mail"] + + +[ldap.kit] + mail = "kit@mydomain.ex" + basedn = "ou=fkit,ou=groups,dc=mydomain,dc=ex" + filter = "(&(objectClass=itGroup)(type=Committee))" + parent_filter = "(&(ou=%childRDN%))" + attibutes = ["cn", "displayName", "mail"] +#### ============== #### diff --git a/prod.docker-compose.yaml b/prod.docker-compose.yaml index 3022090..7e02d04 100644 --- a/prod.docker-compose.yaml +++ b/prod.docker-compose.yaml @@ -15,8 +15,8 @@ services: "-dry" ] volumes: - - ./cmd/config.toml:/app/config.toml:ro - - ./cmd/gapps.json:/app/gapps.json:ro - - ./cmd/additions.json:/app/additions.json:ro + - ./config.toml:/app/config.toml:ro + - ./gapps.json:/app/gapps.json:ro + - ./additions.json:/app/additions.json:ro environment: - WAIT=1h \ No newline at end of file