diff --git a/README.md b/README.md index 8a926a1..86a2c2f 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,36 @@ -# GoldApps +# GoldApps + A system for syncing LDAP with gsuite and json files written in Go ### Features -* Migrate groups (from -> to): - * ldap -> gapps - * ldap -> json - * gapps -> json - * gapps -> gapps - * json -> gapps - * (json -> json) + +Producers +- LDAP +- JSON +- Gamma (1.0) +- Auth (Gamma 2.0) + +Consumers +- GApps +- JSON + +## Dummy setup + +Create the following files: + +- `config.toml` - Copy from example.config.toml +- `gapps.json` - Containing `{}` +- `additions.json` - Containing `{}` +- `gamma.json` - Containing `{}` ## Setup -* Copy example.config.toml to config.toml and edit -* Grabb gapps.json and place in working directory - * go to [Google developer console](https://console.developers.google.com) - * go to credentials - * create new service account för this app - * use the downloaded file + +- Copy example.config.toml to config.toml and edit +- Grabb gapps.json and place in working directory + - go to [Google developer console](https://console.developers.google.com) + - go to credentials + - create new service account för this app + - use the downloaded file ## Usage @@ -30,19 +44,17 @@ For some reason `entrypoint` has to be specified in the compose file or the dock The command should be your flags for the `goldapps` command -See `prod.docker-compose.yaml` as reference. - ### Command `goldapps` The following flags are available: -* `-y`: No interacting from the user required. -* `-i`: Ask the user about everything... -* `-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 -* `-additions *.json`: file with additions +- `-y`: No interacting from the user required. +- `-i`: Ask the user about everything... +- `-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 +- `-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 +Notice that flags should be combined on the form `goldapps -a -b` and **NOT** on the form `goldapps -ab`. diff --git a/docker-compose.yml b/docker-compose.yml index c506b3e..8890e96 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,6 +6,8 @@ services: POSTGRES_USER: user POSTGRES_DB: postgres POSTGRES_PASSWORD: password + networks: + - gamma-internal gamma-frontend: image: cthit/gamma-frontend:development @@ -13,6 +15,8 @@ services: HTTP_PROXY: http://gamma-backend:8081 ports: - 3000:3000 + networks: + - gamma-internal gamma-backend: # Starta med cthit/gamma-backend:development för att få in mock data @@ -43,6 +47,16 @@ services: - ./mock.json:/mock/mock.json ports: - 8081:8081 + networks: + - gamma + - gamma-internal redis: image: redis:5.0 + networks: + - gamma-internal + +networks: + gamma: + external: True + gamma-internal: \ No newline at end of file diff --git a/example.config.toml b/example.config.toml index 1049d72..c7ecf6d 100644 --- a/example.config.toml +++ b/example.config.toml @@ -1,48 +1,52 @@ [gapps.consumer] - servicekeyfile = "gapps.json" - adminaccount = "admin@mydomain.ex" +servicekeyfile = "gapps.json" +adminaccount = "admin@mydomain.ex" [gapps.provider] - servicekeyfile = "gapps.json" - adminaccount = "admin@mydomain.ex" +servicekeyfile = "gapps.json" +adminaccount = "admin@mydomain.ex" [gamma.provider] - apiKey = "key" - url = "http://localhost:8081" +apiKey = "key" +url = "http://gamma-backend:8081" + +[auth.provider] +apiKey = "key" +url = "http://gamma-mock:8081" [additions] file = "additions.json" [ldap] - url = "ldap.mydomain.ex:636" - servername = "mydomain.ex" - user = "cn=admin,dc=mydomain,dc=ex" - password = "PASSWORD" - custom = ["fkit", "kit"] +url = "ldap.mydomain.ex:636" +servername = "mydomain.ex" +user = "cn=admin,dc=mydomain,dc=ex" +password = "PASSWORD" +custom = ["fkit", "kit"] [ldap.groups] - basedn = "ou=groups,dc=mydomain,dc=ex" - filter = "(|(objectClass=itGroup)(objectClass=itPosition))" - attibutes = ["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=mydomain,dc=ex" - filter = "(&(objectClass=chalmersstudent))" - attibutes = ["uid", "mail"] +basedn = "ou=people,dc=mydomain,dc=ex" +filter = "(&(objectClass=chalmersstudent))" +attibutes = ["uid", "mail"] #### CUSTOM FILTERS #### [ldap.fkit] - mail = "fkit@mydomain.ex" - basedn = "ou=fkit,ou=groups,dc=mydomain,dc=ex" - filter = "(&(objectClass=itGroup))" - parent_filter = "(&(ou=%childRDN%))" - attibutes = ["cn", "displayName", "mail"] +mail = "fkit@mydomain.ex" +basedn = "ou=fkit,ou=groups,dc=mydomain,dc=ex" +filter = "(&(objectClass=itGroup))" +parent_filter = "(&(ou=%childRDN%))" +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"] +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/internal/app/cli/services.go b/internal/app/cli/services.go index cf83755..1cd924d 100644 --- a/internal/app/cli/services.go +++ b/internal/app/cli/services.go @@ -7,6 +7,7 @@ import ( "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/auth" "github.com/cthit/goldapps/internal/pkg/services/gamma" "github.com/cthit/goldapps/internal/pkg/services/json" "github.com/cthit/goldapps/internal/pkg/services/ldap" @@ -80,7 +81,12 @@ func getProvider() services.CollectionService { panic(err) } return provider - + case "auth": + provider, _ := auth.CreateAuthService( + viper.GetString("auth.provider.apiKey"), + viper.GetString("auth.provider.url"), + ) + return provider default: isJson, _ := regexp.MatchString(`.+\.json$`, from) if isJson { diff --git a/internal/app/web/services.go b/internal/app/web/services.go index ec463a0..f4657f7 100644 --- a/internal/app/web/services.go +++ b/internal/app/web/services.go @@ -11,6 +11,7 @@ import ( "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/auth" "github.com/cthit/goldapps/internal/pkg/services/gamma" "github.com/cthit/goldapps/internal/pkg/services/json" "github.com/spf13/viper" @@ -48,6 +49,10 @@ func getProvider(fromJson string) (services.CollectionService, error) { provider, err = gamma.CreateGammaService( viper.GetString("gamma.provider.apiKey"), viper.GetString("gamma.provider.url")) + } else if fromJson == "auth" { + provider, _ = auth.CreateAuthService( + viper.GetString("auth.provider.apiKey"), + viper.GetString("auth.provider.url")) } else if isJson { provider, err = json.NewJsonService(fromJson) } else { diff --git a/internal/pkg/services/auth/models.go b/internal/pkg/services/auth/models.go new file mode 100644 index 0000000..6e71b0b --- /dev/null +++ b/internal/pkg/services/auth/models.go @@ -0,0 +1,70 @@ +package auth + +import "github.com/cthit/goldapps/internal/pkg/model" + +type AuthSuperGroup struct { + Name string `json:"name"` + PrettyName string `json:"prettyName"` + Type string `json:"type"` + Members []struct { + Post struct { + PostID string `json:"postId"` + SvText string `json:"svText"` + EnText string `json:"enText"` + EmailPrefix string `json:"emailPrefix"` + } `json:"post"` + User AuthUser `json:"user"` + } `json:"members"` +} + +type AuthUser struct { + Email string `json:"email"` + Cid string `json:"cid"` + FirstName string `json:"firstName"` + LastName string `json:"lastName"` + Nick string `json:"nick"` +} + +type AuthSuperGroups []AuthSuperGroup + +type AuthUsers []AuthUser + +func (user AuthUser) ToUser() model.Users { + return model.Users{ + model.User{ + Cid: user.Cid, + FirstName: user.FirstName, + SecondName: user.LastName, + Nick: user.Nick, + Mail: user.Email, + }, + } +} + +func (users AuthUsers) ToUsers() model.Users { + usersList := model.Users{} + for _, user := range users { + usersList = append(usersList, user.ToUser()...) + } + return usersList +} + +func (superGroup AuthSuperGroup) ToGroup() model.Group { + group := model.Group{ + Email: superGroup.Name + "@chalmers.it", + Type: superGroup.Type, + Aliases: []string{}, + } + for _, member := range superGroup.Members { + group.Members = append(group.Members, member.User.Email) + } + return group +} + +func (superGroups AuthSuperGroups) ToGroups() model.Groups { + groupsList := model.Groups{} + for _, superGroup := range superGroups { + groupsList = append(groupsList, superGroup.ToGroup()) + } + return groupsList +} diff --git a/internal/pkg/services/auth/service.go b/internal/pkg/services/auth/service.go new file mode 100644 index 0000000..c3c337f --- /dev/null +++ b/internal/pkg/services/auth/service.go @@ -0,0 +1,81 @@ +package auth + +import ( + "encoding/json" + "fmt" + "io" + "log" + "net/http" + + "github.com/cthit/goldapps/internal/pkg/model" +) + +type AuthService struct { + apiKey string + url string +} + +// Creates a auth service which has the url to auth and the pre-shared key +func CreateAuthService(apiKey string, url string) (AuthService, error) { + return AuthService{ + apiKey: apiKey, + url: url, + }, nil +} + +// Executes a generic get request with api key +func request(s *AuthService, endpoint string, response interface{}) error { + req, err := http.NewRequest("GET", fmt.Sprintf("%s%s", s.url, endpoint), nil) + if err != nil { + log.Println(err) + return err + } + + req.Header.Set("Authorization", fmt.Sprintf("pre-shared %s", s.apiKey)) + + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + log.Println(err) + return err + } + fmt.Printf("Request sent to: %s [key: %s] status %d\n", endpoint, s.apiKey, resp.StatusCode) + + body, err := io.ReadAll(resp.Body) + if err != nil { + log.Println(err) + return err + } + + err = json.Unmarshal(body, &response) + if err != nil { + log.Println(err) + return err + } + + return nil +} + +func (s AuthService) GetGroups() ([]model.Group, error) { + var groups AuthSuperGroups + + err := request(&s, "/api/account-scaffold/v1/supergroups", &groups) + if err != nil { + log.Println(err) + return nil, err + } + + return groups.ToGroups(), nil +} + +func (s AuthService) GetUsers() ([]model.User, error) { + var users AuthUsers + + err := request(&s, "/api/account-scaffold/v1/users", &users) + if err != nil { + log.Println(err) + return nil, err + } + + return users.ToUsers(), nil +} diff --git a/prod.docker-compose.yaml b/prod.docker-compose.yaml index 500ee21..da6db75 100644 --- a/prod.docker-compose.yaml +++ b/prod.docker-compose.yaml @@ -5,28 +5,23 @@ services: dockerfile: Dockerfile context: . image: goldapps:stable - network_mode: host # Easier since gamma is running with a different docker-compose - command: [ - "-from ldap", - "-to gapps", - "-additions additions.json", - "-y", - "-dry" - ] volumes: - ./config.toml:/app/config.toml:ro - ./gapps.json:/app/gapps.json:ro - ./additions.json:/app/additions.json:ro + - ./gamma.json:/app/gamma.json:ro + networks: + - gamma web: build: dockerfile: Dockerfile.web context: . - network_mode: host # Easier since gamma is running with a different docker-compose volumes: - ./config.toml:/app/config.toml:ro - ./gapps.json:/app/gapps.json:ro - ./additions.json:/app/additions.json:ro + - ./gamma.json:/app/gamma.json:ro environment: GIN_MODE: debug SESSION_SECRET: secret @@ -36,4 +31,12 @@ services: # Gamma client info GAMMA_CLIENT_ID: id GAMMA_CLIENT_SECRET: key - GAMMA_REDIRECT_URL: http://localhost:3001/callback + GAMMA_REDIRECT_URL: http://localhost:8080/api/authenticate + networks: + - gamma + ports: + - 8080:8080 + +networks: + gamma: + external: True \ No newline at end of file