Welcome: Create new users via Memberships API
Change-Id: Iaa12b3438340a5ca4c4fdb2157d1e8f064d56139
diff --git a/charts/welcome/templates/install.yaml b/charts/welcome/templates/install.yaml
index dfbac29..81898d4 100644
--- a/charts/welcome/templates/install.yaml
+++ b/charts/welcome/templates/install.yaml
@@ -111,7 +111,7 @@
- --port=8080
- --create-account-addr={{ .Values.createAccountAddr }}
- --login-addr={{ .Values.loginAddr }}
- - --memberships-init-addr={{ .Values.membershipsInitAddr }}
+ - --memberships-addr={{ .Values.membershipsAddr }}
volumeMounts:
- name: ssh-key
readOnly: true
diff --git a/charts/welcome/values.yaml b/charts/welcome/values.yaml
index 95ac8ec..a0b42b1 100644
--- a/charts/welcome/values.yaml
+++ b/charts/welcome/values.yaml
@@ -6,7 +6,7 @@
sshPrivateKey: key
createAccountAddr: http://api.core-auth.svc.cluster.local/identities
loginAddr: https://accounts-ui.example.com
-membershipsInitAddr: http://memberships.example.svc.cluster.local/api/ini
+membershipsAddr: http://memberships.example.svc.cluster.local
ingress:
className: pcloud-ingress-public
domain: welcome.example.com
diff --git a/core/auth/memberships/main.go b/core/auth/memberships/main.go
index 8ef0848..0e3f2da 100644
--- a/core/auth/memberships/main.go
+++ b/core/auth/memberships/main.go
@@ -33,7 +33,7 @@
type Store interface {
// Initializes store with admin user and their groups.
- Init(owner string, groups []string) error
+ Init(user, email string, groups []string) error
CreateGroup(owner string, group Group) error
AddChildGroup(parent, child string) error
AddOwnerGroup(owned_group, owner_group string) error
@@ -137,7 +137,7 @@
return &SQLiteStore{db: db}, nil
}
-func (s *SQLiteStore) Init(owner string, groups []string) error {
+func (s *SQLiteStore) Init(user, email string, groups []string) error {
tx, err := s.db.Begin()
if err != nil {
return err
@@ -151,17 +151,21 @@
if count != 0 {
return fmt.Errorf("Store already initialised")
}
+ query := `INSERT INTO users (username, email) VALUES (?, ?)`
+ if _, err := tx.Exec(query, user, email); err != nil {
+ return err
+ }
for _, g := range groups {
- query := `INSERT INTO groups (name, description) VALUES (?, '')`
+ query = `INSERT INTO groups (name, description) VALUES (?, '')`
if _, err := tx.Exec(query, g); err != nil {
return err
}
query = `INSERT INTO owners (username, group_name) VALUES (?, ?)`
- if _, err := tx.Exec(query, owner, g); err != nil {
+ if _, err := tx.Exec(query, user, g); err != nil {
return err
}
query = `INSERT INTO user_to_group (username, group_name) VALUES (?, ?)`
- if _, err := tx.Exec(query, owner, g); err != nil {
+ if _, err := tx.Exec(query, user, g); err != nil {
return err
}
}
@@ -1188,7 +1192,8 @@
}
type initRequest struct {
- Owner string `json:"owner"`
+ User string `json:"user"`
+ Email string `json:"email"`
Groups []string `json:"groups"`
}
@@ -1198,7 +1203,7 @@
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
- if err := s.store.Init(req.Owner, req.Groups); err != nil {
+ if err := s.store.Init(req.User, req.Email, req.Groups); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
@@ -1285,34 +1290,30 @@
}
}
+type createUserRequest struct {
+ User string `json:"user"`
+ Email string `json:"email"`
+}
+
func (s *Server) apiCreateUser(w http.ResponseWriter, r *http.Request) {
defer s.pingAllSyncAddresses()
- selfAddress := r.FormValue("selfAddress")
- if selfAddress != "" {
- s.addSyncAddress(selfAddress)
- }
- if err := r.ParseForm(); err != nil {
+ var req createUserRequest
+ if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "Invalid request body", http.StatusBadRequest)
return
}
- username := r.FormValue("username")
- email := r.FormValue("email")
- if username == "" {
+ if req.User == "" {
http.Error(w, "Username cannot be empty", http.StatusBadRequest)
return
}
- if email == "" {
+ if req.Email == "" {
http.Error(w, "Email cannot be empty", http.StatusBadRequest)
return
}
- username = strings.ToLower(username)
- email = strings.ToLower(email)
- err := s.store.CreateUser(username, email)
- if err != nil {
+ if err := s.store.CreateUser(strings.ToLower(req.User), strings.ToLower(req.Email)); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
- w.WriteHeader(http.StatusOK)
}
func (s *Server) pingAllSyncAddresses() {
diff --git a/core/auth/memberships/store_test.go b/core/auth/memberships/store_test.go
index 4e9ef19..bc2e9c6 100644
--- a/core/auth/memberships/store_test.go
+++ b/core/auth/memberships/store_test.go
@@ -23,7 +23,7 @@
if err != nil {
t.Fatal(err)
}
- if err := store.Init("admin", []string{"admin", "all"}); err != nil {
+ if err := store.Init("admin", "admin@admin", []string{"admin", "all"}); err != nil {
t.Fatal(err)
}
groups, err := store.GetGroupsOwnedBy("admin")
@@ -60,7 +60,7 @@
if err != nil {
t.Fatal(err)
}
- err = store.Init("admin", []string{"admin", "all"})
+ err = store.Init("admin", "admin", []string{"admin", "all"})
if err == nil {
t.Fatal("initialisation did not fail")
} else if err.Error() != "Store already initialised" {
diff --git a/core/installer/cmd/welcome.go b/core/installer/cmd/welcome.go
index db1ce4a..30ed48f 100644
--- a/core/installer/cmd/welcome.go
+++ b/core/installer/cmd/welcome.go
@@ -11,12 +11,12 @@
)
var welcomeFlags struct {
- repo string
- sshKey string
- port int
- createAccountAddr string
- loginAddr string
- membershipsInitAddr string
+ repo string
+ sshKey string
+ port int
+ createAccountAddr string
+ loginAddr string
+ membershipsAddr string
}
func welcomeCmd() *cobra.Command {
@@ -55,8 +55,8 @@
"",
)
cmd.Flags().StringVar(
- &welcomeFlags.membershipsInitAddr,
- "memberships-init-addr",
+ &welcomeFlags.membershipsAddr,
+ "memberships-addr",
"",
"",
)
@@ -95,7 +95,7 @@
installer.NewGitHelmFetcher(),
welcomeFlags.createAccountAddr,
welcomeFlags.loginAddr,
- welcomeFlags.membershipsInitAddr,
+ welcomeFlags.membershipsAddr,
)
s.Start()
return nil
diff --git a/core/installer/tasks/infra.go b/core/installer/tasks/infra.go
index f855adf..eb4840f 100644
--- a/core/installer/tasks/infra.go
+++ b/core/installer/tasks/infra.go
@@ -78,6 +78,7 @@
type firstAccount struct {
Created bool `json:"created"`
+ Domain string `json:"domain"`
Groups []string `json:"groups"`
}
@@ -88,7 +89,7 @@
return err
}
return r.Do(func(r soft.RepoFS) (string, error) {
- fa := firstAccount{false, initGroups}
+ fa := firstAccount{false, env.Domain, initGroups}
if err := soft.WriteYaml(r, "first-account.yaml", fa); err != nil {
return "", err
}
diff --git a/core/installer/values-tmpl/welcome.cue b/core/installer/values-tmpl/welcome.cue
index 55f4e14..3e385ab 100644
--- a/core/installer/values-tmpl/welcome.cue
+++ b/core/installer/values-tmpl/welcome.cue
@@ -37,7 +37,7 @@
sshPrivateKey: base64.Encode(null, input.sshPrivateKey)
createAccountAddr: "http://api.\(global.namespacePrefix)core-auth.svc.cluster.local/identities"
loginAddr: "https://launcher.\(networks.public.domain)"
- membershipsInitAddr: "http://memberships-api.\(global.namespacePrefix)core-auth-memberships.svc.cluster.local/api/init"
+ membershipsAddr: "http://memberships-api.\(global.namespacePrefix)core-auth-memberships.svc.cluster.local"
ingress: {
className: input.network.ingressClass
domain: "welcome.\(input.network.domain)"
diff --git a/core/installer/welcome/welcome.go b/core/installer/welcome/welcome.go
index 80bbb64..85d7f61 100644
--- a/core/installer/welcome/welcome.go
+++ b/core/installer/welcome/welcome.go
@@ -28,13 +28,13 @@
var staticAssets embed.FS
type Server struct {
- port int
- repo soft.RepoIO
- nsCreator installer.NamespaceCreator
- hf installer.HelmFetcher
- createAccountAddr string
- loginAddr string
- membershipsInitAddr string
+ port int
+ repo soft.RepoIO
+ nsCreator installer.NamespaceCreator
+ hf installer.HelmFetcher
+ createAccountAddr string
+ loginAddr string
+ membershipsAddr string
}
func NewServer(
@@ -44,7 +44,7 @@
hf installer.HelmFetcher,
createAccountAddr string,
loginAddr string,
- membershipsInitAddr string,
+ membershipsAddr string,
) *Server {
return &Server{
port,
@@ -53,7 +53,7 @@
hf,
createAccountAddr,
loginAddr,
- membershipsInitAddr,
+ membershipsAddr,
}
}
@@ -205,7 +205,7 @@
return
}
}
- if err := s.initMemberships(req.Username); err != nil {
+ if err := s.createUser(req.Username); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
@@ -245,40 +245,66 @@
renderRegistrationSuccess(w, s.loginAddr)
}
-type firstaccount struct {
+type firstAccount struct {
Created bool `json:"created"`
+ Domain string `json:"domain"`
Groups []string `json:"groups"`
}
type initRequest struct {
- Owner string `json:"owner"`
+ User string `json:"user"`
+ Email string `json:"email"`
Groups []string `json:"groups"`
}
-func (s *Server) initMemberships(username string) error {
+type createUserRequest struct {
+ User string `json:"user"`
+ Email string `json:"email"`
+}
+
+func (s *Server) createUser(username string) error {
return s.repo.Do(func(r soft.RepoFS) (string, error) {
- var fa firstaccount
+ var fa firstAccount
if err := soft.ReadYaml(r, "first-account.yaml", &fa); err != nil {
return "", err
}
+ var resp *http.Response
+ var err error
if fa.Created {
- return "", nil
+ req := createUserRequest{username, fmt.Sprintf("%s@%s", username, fa.Domain)}
+ var buf bytes.Buffer
+ if err := json.NewEncoder(&buf).Encode(req); err != nil {
+ return "", err
+ }
+ resp, err = http.Post(
+ fmt.Sprintf("%s/api/users", s.membershipsAddr),
+ "applications/json",
+ &buf,
+ )
+ } else {
+ req := initRequest{username, fmt.Sprintf("%s@%s", username, fa.Domain), fa.Groups}
+ var buf bytes.Buffer
+ if err := json.NewEncoder(&buf).Encode(req); err != nil {
+ return "", err
+ }
+ resp, err = http.Post(
+ fmt.Sprintf("%s/api/init", s.membershipsAddr),
+ "applications/json",
+ &buf,
+ )
+ fa.Created = true
+ if err := soft.WriteYaml(r, "first-account.yaml", fa); err != nil {
+ return "", err
+ }
}
- req := initRequest{username, fa.Groups}
- var buf bytes.Buffer
- if err := json.NewEncoder(&buf).Encode(req); err != nil {
- return "", err
- }
- resp, err := http.Post(s.membershipsInitAddr, "applications/json", &buf)
if err != nil {
return "", err
}
defer resp.Body.Close()
fmt.Printf("Memberships resp: %d", resp.StatusCode)
io.Copy(os.Stdout, resp.Body)
- fa.Created = true
- if err := soft.WriteYaml(r, "first-account.yaml", fa); err != nil {
- return "", err
+ if resp.StatusCode != http.StatusOK {
+ return "", fmt.Errorf("memberships error")
}
return "initialized groups for first account", nil
})