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

Commit

Permalink
Add RBAC descriptions (#1405)
Browse files Browse the repository at this point in the history
  • Loading branch information
sethvargo authored Dec 17, 2020
1 parent 29542f9 commit b2ea015
Show file tree
Hide file tree
Showing 9 changed files with 96 additions and 125 deletions.
9 changes: 7 additions & 2 deletions cmd/server/assets/users/_form.html
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,18 @@ <h6>Permissions</h6>
</small>
{{end}}
{{range $name, $permission := $permissions}}
<div class="custom-control custom-checkbox py-1">
<div class="custom-control custom-checkbox py-3">
<input type="checkbox" name="permissions" id="permission-{{$permission.Value}}"
class="custom-control-input" value="{{$permission.Value}}"
{{checkedIf ($userMembership.Can $permission)}}
{{disabledIf (eq $currentMembership.UserID $userMembership.UserID)}}
{{readonlyIf (eq $currentMembership.UserID $userMembership.UserID)}}>
<label class="custom-control-label" for="permission-{{$permission.Value}}">{{$name}}</label>
<label class="custom-control-label w-100" for="permission-{{$permission.Value}}">
{{$name}}
<small class="form-text text-muted">
Can {{$permission.Description}}.
</small>
</label>
</div>
{{end}}
</div>
Expand Down
7 changes: 5 additions & 2 deletions cmd/server/assets/users/show.html
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,18 @@ <h6 class="card-title">Permissions</h6>
<div class="mb-3 mt-n2">
<ul class="list-unstyled">
{{range $name, $permission := $permissions}}
<li class="small">
<li class="small py-1">
{{if $userMembership.Can $permission}}
<span class="sr-only">{{$user.Name}} can {{$name}}</span>
<span class="oi oi-circle-check text-success mr-1" aria-hidden="true"></span>
{{else}}
<span class="sr-only">{{$user.Name}} cannot {{$name}}</span>
<span class="oi oi-circle-x text-muted mr-1" aria-hidden="true"></span>
{{end}}
{{$name}}
{{$name}} -
<span class="text-muted">
{{$permission.Description}}
</span>
</li>
{{end}}
</ul>
Expand Down
2 changes: 1 addition & 1 deletion pkg/controller/user/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,6 @@ func (c *Controller) renderNew(ctx context.Context, w http.ResponseWriter) {
m.Title("New user")
m["user"] = &database.User{}
m["userMembership"] = &database.Membership{}
m["permissions"] = rbac.PermissionMap()
m["permissions"] = rbac.NamePermissionMap
c.h.RenderHTML(w, "users/new", m)
}
2 changes: 1 addition & 1 deletion pkg/controller/user/show.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,6 @@ func (c *Controller) renderShow(ctx context.Context, w http.ResponseWriter, user
m.Title("User: %s", user.Name)
m["user"] = user
m["userMembership"] = membership
m["permissions"] = rbac.PermissionMap()
m["permissions"] = rbac.NamePermissionMap
c.h.RenderHTML(w, "users/show", m)
}
2 changes: 1 addition & 1 deletion pkg/controller/user/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,6 @@ func (c *Controller) renderEdit(ctx context.Context, w http.ResponseWriter, user
m.Title("Edit user: %s", user.Name)
m["user"] = user
m["userMembership"] = membership
m["permissions"] = rbac.PermissionMap()
m["permissions"] = rbac.NamePermissionMap
c.h.RenderHTML(w, "users/edit", m)
}
29 changes: 10 additions & 19 deletions pkg/controller/user/update_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func TestUpdate(t *testing.T) {
taskCtx, done := context.WithTimeout(browserCtx, 30*time.Second)
defer done()

for _, permission := range rbac.PermissionMap() {
for _, permission := range rbac.NamePermissionMap {
permission := permission
target := fmt.Sprintf(`input#permission-%d`, permission)

Expand All @@ -84,15 +84,6 @@ func TestUpdate(t *testing.T) {
); err != nil {
t.Fatal(err)
}

membership, err := user.FindMembership(harness.Database, realm.ID)
if err != nil {
t.Fatal(err)
}

if membership.Can(permission) {
t.Errorf("expected %s to be removed", permission)
}
}

// Assert the user has no permissions left
Expand All @@ -105,7 +96,7 @@ func TestUpdate(t *testing.T) {
}

// Now add permissions back
for _, permission := range rbac.PermissionMap() {
for _, permission := range rbac.NamePermissionMap {
permission := permission
target := fmt.Sprintf(`input#permission-%d`, permission)

Expand All @@ -128,14 +119,14 @@ func TestUpdate(t *testing.T) {
); err != nil {
t.Fatal(err)
}
}

membership, err := user.FindMembership(harness.Database, realm.ID)
if err != nil {
t.Fatal(err)
}

if !membership.Can(permission) {
t.Errorf("expected %s to be added", permission)
}
// Permissions should be back
membership, err = user.FindMembership(harness.Database, realm.ID)
if err != nil {
t.Fatal(err)
}
if got, want := int64(membership.Permissions), int64(32766); got != want {
t.Errorf("expected %v to be %v", got, want)
}
}
101 changes: 68 additions & 33 deletions pkg/rbac/rbac.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.

//go:generate stringer -output=rbac_gen.go -type=Permission

// Package rbac implements authorization.
package rbac

Expand All @@ -23,8 +21,39 @@ import (
"sort"
)

// Can returns true if the given resource has permission to perform ALL of the
// provided permissions.
// PermissionsMap is the list of permissions mapped to their name and
// description.
var (
PermissionMap = map[Permission][2]string{
AuditRead: {"AuditRead", "read event and audit logs"},
APIKeyRead: {"APIKeyRead", "view information about API keys, including statistics"},
APIKeyWrite: {"APIKeyWrite", "create, update, and delete API keys"},
CodeIssue: {"CodeIssue", "issue codes"},
CodeBulkIssue: {"CodeBulkIssue", "issue codes in bulk, if bulk issue is enabled on the realm"},
CodeRead: {"CodeRead", "lookup code status"},
CodeExpire: {"CodeExpire", "expire codes"},
SettingsRead: {"SettingsRead", "read realm settings"},
SettingsWrite: {"SettingsWrite", "update realm settings"},
StatsRead: {"StatsRead", "view realm statistics"},
MobileAppRead: {"MobileAppRead", "view mobile app information"},
MobileAppWrite: {"MobileAppWrite", "create, update, and delete mobile apps"},
UserRead: {"UserRead", "view user information"},
UserWrite: {"UserWrite", "create, update, and delete users"},
}

// NamePermissionMap is the map of permission names to their value.
NamePermissionMap map[string]Permission
)

func init() {
NamePermissionMap = make(map[string]Permission, len(PermissionMap))
for k, v := range PermissionMap {
NamePermissionMap[v[0]] = k
}
}

// Can returns true if the given resource has permission to perform the provided
// permissions.
func Can(given Permission, target Permission) bool {
return int64(given)&int64(target) != 0
}
Expand All @@ -46,23 +75,13 @@ func CompileAndAuthorize(actorPermission Permission, toUpdate []Permission) (Per
return permission, nil
}

// PermissionMap is a map of permissions to their names. It requires the
// stringer generation.
func PermissionMap() map[string]Permission {
m := make(map[string]Permission, len(_Permission_map)+2)
for k, v := range _Permission_map {
m[v] = k
}
return m
}

// PermissionNames returns the list of permissions included in the given
// permission.
func PermissionNames(p Permission) []string {
names := make([]string, 0, len(_Permission_map))
for v, k := range _Permission_map {
names := make([]string, 0, len(PermissionMap))
for v, k := range PermissionMap {
if Can(p, v) {
names = append(names, k)
names = append(names, k[0])
}
}
sort.Strings(names)
Expand All @@ -73,41 +92,57 @@ func PermissionNames(p Permission) []string {
// because most database systems lack unsigned integer types.
type Permission int64

// String implements stringer.
func (p Permission) String() string {
if v, ok := PermissionMap[p]; ok {
return v[0]
}
return fmt.Sprintf("Permission(%d)", int64(p))
}

// Value returns the permissions value as an integer for sql drivers.
func (p Permission) Value() (driver.Value, error) {
return int64(p), nil
}

// Description returns the description
func (p Permission) Description() (string, error) {
if v, ok := PermissionMap[p]; ok {
return v[1], nil
}
return "", fmt.Errorf("missing description for %s", p)
}

const (
_ Permission = 1 << iota

// Audit
AuditRead
AuditRead = 1 << iota

// API keys
APIKeyRead
APIKeyWrite
APIKeyRead = 1 << iota
APIKeyWrite = 1 << iota

// Codes
CodeIssue
CodeBulkIssue
CodeRead
CodeExpire
CodeIssue = 1 << iota
CodeBulkIssue = 1 << iota
CodeRead = 1 << iota
CodeExpire = 1 << iota

// Realm settings
SettingsRead
SettingsWrite
SettingsRead = 1 << iota
SettingsWrite = 1 << iota

// Realm statistics
StatsRead
StatsRead = 1 << iota

// Mobile apps
MobileAppRead
MobileAppWrite
MobileAppRead = 1 << iota
MobileAppWrite = 1 << iota

// Users
UserRead
UserWrite
UserRead = 1 << iota
UserWrite = 1 << iota
)

// --
Expand All @@ -116,10 +151,10 @@ const (

const (
// LegacyRealmUser is a quick reference to the old "user" permissions.
LegacyRealmUser = CodeIssue | CodeBulkIssue | CodeRead | CodeExpire
LegacyRealmUser Permission = CodeIssue | CodeBulkIssue | CodeRead | CodeExpire

// LegacyRealmAdmin is a quick reference to the old "realm admin" permissions.
LegacyRealmAdmin = AuditRead |
LegacyRealmAdmin Permission = AuditRead |
APIKeyRead | APIKeyWrite |
CodeIssue | CodeBulkIssue | CodeRead | CodeExpire |
SettingsRead | SettingsWrite |
Expand Down
65 changes: 0 additions & 65 deletions pkg/rbac/rbac_gen.go

This file was deleted.

4 changes: 3 additions & 1 deletion pkg/render/renderer_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,9 @@ func templateFuncs() htmltemplate.FuncMap {
"t": translate,
"passwordSentinel": pwdSentinel,

"rbac": rbac.PermissionMap,
"rbac": func() map[string]rbac.Permission {
return rbac.NamePermissionMap
},
}
}

Expand Down

0 comments on commit b2ea015

Please sign in to comment.