Skip to content
This repository has been archived by the owner on Jul 12, 2023. It is now read-only.

Commit

Permalink
Allow system admins to remove themselves from a realm (#561)
Browse files Browse the repository at this point in the history
  • Loading branch information
sethvargo authored Sep 17, 2020
1 parent 9c40008 commit 2dffa19
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 23 deletions.
3 changes: 2 additions & 1 deletion cmd/server/assets/users/_form.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{{define "users/_form"}}

{{$currentRealm := .currentRealm}} {{$user := .user}}
{{$currentRealm := .currentRealm}}
{{$user := .user}}

{{if $user.ID}}
<form method="POST" action="/users/{{$user.ID}}">
Expand Down
9 changes: 8 additions & 1 deletion cmd/server/assets/users/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,20 @@ <h1>Users</h1>
{{end}}
</td>
<td class="text-center">
{{- /* cannot delete yourself */ -}}
{{if not (eq .ID $currentUser.ID)}}
{{- /* cannot delete yourself */ -}}
<a href="/users/{{.ID}}" class="d-block text-danger" data-method="DELETE"
data-confirm="Are you sure you want to remove '{{.Name}}'?" data-toggle="tooltip"
title="Remove this user">
<span class="oi oi-trash" aria-hidden="true"></span>
</a>
{{else if $currentUser.Admin}}
{{- /* system admins can remove themselves */ -}}
<a href="/users/{{.ID}}" class="d-block text-danger" data-method="DELETE"
data-confirm="Are you sure you want to leave {{.Name}}?" data-toggle="tooltip"
title="Leave this realm">
<span class="oi oi-account-logout" aria-hidden="true"></span>
</a>
{{end}}
</td>
</tr>
Expand Down
26 changes: 5 additions & 21 deletions pkg/controller/user/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,8 @@ import (
"context"
"net/http"

"firebase.google.com/go/auth"
"github.com/google/exposure-notifications-verification-server/pkg/controller"
"github.com/google/exposure-notifications-verification-server/pkg/database"
"github.com/sethvargo/go-password/password"
)

func (c *Controller) HandleCreate() http.Handler {
Expand Down Expand Up @@ -98,27 +96,13 @@ func (c *Controller) HandleCreate() http.Handler {
return
}

if _, err := c.client.GetUserByEmail(ctx, user.Email); auth.IsUserNotFound(err) {
pwd, err := password.Generate(24, 8, 8, false, true)
if err != nil {
flash.Alert("Failed to generate password for '%v'", form.Email)
c.renderNew(ctx, w, user, false)
return
}

fbUser := &auth.UserToCreate{}
fbUser.Email(user.Email).DisplayName(user.Name).Password(pwd)
if _, err = c.client.CreateUser(ctx, fbUser); err != nil {
flash.Alert("Error creating user '%v'", form.Email)
c.renderNew(ctx, w, user, false)
return
}

c.renderNew(ctx, w, user, true)
return
created, err := user.CreateFirebaseUser(ctx, c.client)
if err != nil {
flash.Alert("Failed to create user: %v", err)
c.renderNew(ctx, w, user, false)
}

c.renderNew(ctx, w, user, false)
c.renderNew(ctx, w, user, created)
})
}

Expand Down
22 changes: 22 additions & 0 deletions pkg/controller/user/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ func (c *Controller) HandleDelete() http.Handler {
return
}

currentUser := controller.UserFromContext(ctx)
if realm == nil {
controller.MissingUser(w, r, c.h)
return
}

user, err := realm.FindUser(c.db, vars["id"])
if err != nil {
if database.IsNotFound(err) {
Expand All @@ -51,6 +57,13 @@ func (c *Controller) HandleDelete() http.Handler {
return
}

// Do not allow users to remove themselves unless they are admins.
if user.ID == currentUser.ID && !currentUser.Admin {
flash.Error("Failed to remove user from realm: cannot remove self")
http.Redirect(w, r, "/users", http.StatusSeeOther)
return
}

user.RemoveRealm(realm)

if err := c.db.SaveUser(user); err != nil {
Expand All @@ -59,6 +72,15 @@ func (c *Controller) HandleDelete() http.Handler {
return
}

// If the user removed themselves from a realm, clear it from the session to
// avoid a weird redirect.
if user.ID == currentUser.ID {
flash.Alert("Successfully removed you from the realm")
controller.ClearSessionRealm(session)
http.Redirect(w, r, "/home", http.StatusSeeOther)
return
}

flash.Alert("Successfully removed user %v from realm", user.Email)
http.Redirect(w, r, "/users", http.StatusSeeOther)
})
Expand Down
31 changes: 31 additions & 0 deletions pkg/database/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@
package database

import (
"context"
"fmt"
"strings"
"time"

"firebase.google.com/go/auth"
"github.com/jinzhu/gorm"
"github.com/sethvargo/go-password/password"
)

// User represents a user of the system
Expand Down Expand Up @@ -177,6 +180,34 @@ func (db *Database) TouchUserRevokeCheck(u *User) error {
Error
}

// CreateFirebaseUser creates the associated Firebase user for this database
// user. It does nothing if the firebase user already exists. If the firebase
// user does not exist, it generates a random password. The returned boolean
// indicates if the user was created.
func (u *User) CreateFirebaseUser(ctx context.Context, fbAuth *auth.Client) (bool, error) {
if _, err := fbAuth.GetUserByEmail(ctx, u.Email); err != nil {
if !auth.IsUserNotFound(err) {
return false, fmt.Errorf("failed lookup firebase user: %w", err)
}

pwd, err := password.Generate(24, 8, 8, false, true)
if err != nil {
return false, fmt.Errorf("failed to generate password: %w", err)
}

fbUser := &auth.UserToCreate{}
fbUser = fbUser.Email(u.Email)
fbUser = fbUser.Password(pwd)
fbUser = fbUser.DisplayName(u.Name)
if _, err := fbAuth.CreateUser(ctx, fbUser); err != nil {
return false, fmt.Errorf("failed to create firebase user: %w", err)
}
return true, nil
}

return false, nil
}

// SaveUser updates the user in the database.
func (db *Database) SaveUser(u *User) error {
db.db.Model(u).Association("Realms").Replace(u.Realms)
Expand Down

0 comments on commit 2dffa19

Please sign in to comment.