176 lines
5.3 KiB
Go
176 lines
5.3 KiB
Go
|
|
// Package role defines the tenant membership roles and the single permission
|
||
|
|
// matrix that authorizes console and management behavior.
|
||
|
|
//
|
||
|
|
// Roles are stored in memberships.role. Authorization decisions must derive
|
||
|
|
// from Grants or Allows so that SQL role checks and Go checks cannot drift
|
||
|
|
// apart.
|
||
|
|
package role
|
||
|
|
|
||
|
|
// Role is a tenant membership role.
|
||
|
|
type Role string
|
||
|
|
|
||
|
|
const (
|
||
|
|
// Owner has full control of a tenant, including tenant-level settings.
|
||
|
|
Owner Role = "owner"
|
||
|
|
// Admin manages applications, credentials, contracts, and members.
|
||
|
|
Admin Role = "admin"
|
||
|
|
// Developer manages applications, credentials, and contracts.
|
||
|
|
Developer Role = "developer"
|
||
|
|
// Operator issues and revokes credentials and imports contracts.
|
||
|
|
Operator Role = "operator"
|
||
|
|
// Viewer reads the console without changing anything.
|
||
|
|
Viewer Role = "viewer"
|
||
|
|
// User is a self-service member. It carries no console access and exists so
|
||
|
|
// that end users of client applications still hold a tenant role that
|
||
|
|
// applications can read through /connect/userinfo.
|
||
|
|
User Role = "user"
|
||
|
|
)
|
||
|
|
|
||
|
|
// Permission is a capability granted by a role.
|
||
|
|
type Permission string
|
||
|
|
|
||
|
|
const (
|
||
|
|
ReadConsole Permission = "console.read"
|
||
|
|
ReadUsers Permission = "users.read"
|
||
|
|
ManageApplications Permission = "applications.manage"
|
||
|
|
ManageKeys Permission = "keys.manage"
|
||
|
|
ManageContracts Permission = "contracts.manage"
|
||
|
|
ManageUsers Permission = "users.manage"
|
||
|
|
ManageTenants Permission = "tenants.manage"
|
||
|
|
EditProfile Permission = "profile.edit"
|
||
|
|
)
|
||
|
|
|
||
|
|
// catalog lists every role in descending order of privilege.
|
||
|
|
var catalog = []Role{Owner, Admin, Developer, Operator, Viewer, User}
|
||
|
|
|
||
|
|
// grants is the permission matrix. ReadConsole is deliberately absent for User
|
||
|
|
// so that self-service members are routed to the profile screen instead of the
|
||
|
|
// management console.
|
||
|
|
var grants = map[Role]map[Permission]bool{
|
||
|
|
Owner: {
|
||
|
|
ReadConsole: true, ReadUsers: true, ManageApplications: true, ManageKeys: true,
|
||
|
|
ManageContracts: true, ManageUsers: true, ManageTenants: true, EditProfile: true,
|
||
|
|
},
|
||
|
|
Admin: {
|
||
|
|
ReadConsole: true, ReadUsers: true, ManageApplications: true, ManageKeys: true,
|
||
|
|
ManageContracts: true, ManageUsers: true, EditProfile: true,
|
||
|
|
},
|
||
|
|
Developer: {
|
||
|
|
ReadConsole: true, ReadUsers: true, ManageApplications: true, ManageKeys: true,
|
||
|
|
ManageContracts: true, EditProfile: true,
|
||
|
|
},
|
||
|
|
Operator: {
|
||
|
|
ReadConsole: true, ReadUsers: true, ManageKeys: true, ManageContracts: true, EditProfile: true,
|
||
|
|
},
|
||
|
|
Viewer: {
|
||
|
|
ReadConsole: true, ReadUsers: true, EditProfile: true,
|
||
|
|
},
|
||
|
|
User: {
|
||
|
|
EditProfile: true,
|
||
|
|
},
|
||
|
|
}
|
||
|
|
|
||
|
|
var ordered = []Permission{
|
||
|
|
ReadConsole, ReadUsers, ManageApplications, ManageKeys,
|
||
|
|
ManageContracts, ManageUsers, ManageTenants, EditProfile,
|
||
|
|
}
|
||
|
|
|
||
|
|
// Entry describes a role for the console and for API clients.
|
||
|
|
type Entry struct {
|
||
|
|
Value string `json:"value"`
|
||
|
|
Label string `json:"label"`
|
||
|
|
Permissions []string `json:"permissions"`
|
||
|
|
}
|
||
|
|
|
||
|
|
var labels = map[Role]string{
|
||
|
|
Owner: "Owner",
|
||
|
|
Admin: "Administrator",
|
||
|
|
Developer: "Developer",
|
||
|
|
Operator: "Operator",
|
||
|
|
Viewer: "Viewer",
|
||
|
|
User: "User",
|
||
|
|
}
|
||
|
|
|
||
|
|
// All returns every role ordered from most to least privileged.
|
||
|
|
func All() []Role {
|
||
|
|
result := make([]Role, len(catalog))
|
||
|
|
copy(result, catalog)
|
||
|
|
return result
|
||
|
|
}
|
||
|
|
|
||
|
|
// Catalog returns every role with its label and permissions so that the console
|
||
|
|
// and API clients never hardcode the role list.
|
||
|
|
func Catalog() []Entry {
|
||
|
|
result := make([]Entry, 0, len(catalog))
|
||
|
|
for _, item := range catalog {
|
||
|
|
result = append(result, Entry{Value: string(item), Label: labels[item], Permissions: item.Permissions()})
|
||
|
|
}
|
||
|
|
return result
|
||
|
|
}
|
||
|
|
|
||
|
|
// Parse converts a stored or submitted value into a Role.
|
||
|
|
func Parse(value string) (Role, bool) {
|
||
|
|
candidate := Role(value)
|
||
|
|
if _, known := grants[candidate]; !known {
|
||
|
|
return "", false
|
||
|
|
}
|
||
|
|
return candidate, true
|
||
|
|
}
|
||
|
|
|
||
|
|
// Valid reports whether the value names a role.
|
||
|
|
func (r Role) Valid() bool {
|
||
|
|
_, known := grants[r]
|
||
|
|
return known
|
||
|
|
}
|
||
|
|
|
||
|
|
// Allows reports whether the role grants the permission.
|
||
|
|
func (r Role) Allows(permission Permission) bool {
|
||
|
|
return grants[r][permission]
|
||
|
|
}
|
||
|
|
|
||
|
|
// Permissions returns the role's permissions in a stable order.
|
||
|
|
func (r Role) Permissions() []string {
|
||
|
|
result := make([]string, 0, len(ordered))
|
||
|
|
for _, permission := range ordered {
|
||
|
|
if r.Allows(permission) {
|
||
|
|
result = append(result, string(permission))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return result
|
||
|
|
}
|
||
|
|
|
||
|
|
// Grants returns the roles that grant a permission, for use as a SQL parameter
|
||
|
|
// so that role checks in queries stay aligned with this matrix.
|
||
|
|
func Grants(permission Permission) []string {
|
||
|
|
result := make([]string, 0, len(catalog))
|
||
|
|
for _, item := range catalog {
|
||
|
|
if item.Allows(permission) {
|
||
|
|
result = append(result, string(item))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return result
|
||
|
|
}
|
||
|
|
|
||
|
|
// Grantable returns the roles a tenant administrator may hand to a new member.
|
||
|
|
// Ownership is not grantable during an invitation because it is established by
|
||
|
|
// creating or claiming a tenant.
|
||
|
|
func Grantable() []Role {
|
||
|
|
result := make([]Role, 0, len(catalog)-1)
|
||
|
|
for _, item := range catalog {
|
||
|
|
if item != Owner {
|
||
|
|
result = append(result, item)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return result
|
||
|
|
}
|
||
|
|
|
||
|
|
// GrantableValues returns Grantable as strings for a request payload.
|
||
|
|
func GrantableValues() []string {
|
||
|
|
values := Grantable()
|
||
|
|
result := make([]string, len(values))
|
||
|
|
for index, item := range values {
|
||
|
|
result[index] = string(item)
|
||
|
|
}
|
||
|
|
return result
|
||
|
|
}
|