wip
parent
4d48072746
commit
5d09d04d14
@ -1,12 +0,0 @@
|
|||||||
package authority
|
|
||||||
|
|
||||||
// Admin is the type definining Authority admins. Admins can update Authority
|
|
||||||
// configuration, provisioners, and even other admins.
|
|
||||||
type Admin struct {
|
|
||||||
ID string `json:"-"`
|
|
||||||
AuthorityID string `json:"-"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Provisioner string `json:"provisioner"`
|
|
||||||
IsSuperAdmin bool `json:"isSuperAdmin"`
|
|
||||||
IsDeleted bool `json:"isDeleted"`
|
|
||||||
}
|
|
@ -0,0 +1,15 @@
|
|||||||
|
package admin
|
||||||
|
|
||||||
|
// Type specifies the type of administrator privileges the admin has.
|
||||||
|
type Type string
|
||||||
|
|
||||||
|
// Admin type.
|
||||||
|
type Admin struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
AuthorityID string `json:"-"`
|
||||||
|
Subject string `json:"subject"`
|
||||||
|
ProvisionerName string `json:"provisionerName"`
|
||||||
|
ProvisionerType string `json:"provisionerType"`
|
||||||
|
ProvisionerID string `json:"provisionerID"`
|
||||||
|
Type Type `json:"type"`
|
||||||
|
}
|
@ -0,0 +1,173 @@
|
|||||||
|
package admin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha1"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"go.step.sm/crypto/jose"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DefaultProvisionersLimit is the default limit for listing provisioners.
|
||||||
|
const DefaultProvisionersLimit = 20
|
||||||
|
|
||||||
|
// DefaultProvisionersMax is the maximum limit for listing provisioners.
|
||||||
|
const DefaultProvisionersMax = 100
|
||||||
|
|
||||||
|
/*
|
||||||
|
type uidProvisioner struct {
|
||||||
|
provisioner Interface
|
||||||
|
uid string
|
||||||
|
}
|
||||||
|
|
||||||
|
type provisionerSlice []uidProvisioner
|
||||||
|
|
||||||
|
func (p provisionerSlice) Len() int { return len(p) }
|
||||||
|
func (p provisionerSlice) Less(i, j int) bool { return p[i].uid < p[j].uid }
|
||||||
|
func (p provisionerSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
||||||
|
*/
|
||||||
|
|
||||||
|
// loadByTokenPayload is a payload used to extract the id used to load the
|
||||||
|
// provisioner.
|
||||||
|
type loadByTokenPayload struct {
|
||||||
|
jose.Claims
|
||||||
|
AuthorizedParty string `json:"azp"` // OIDC client id
|
||||||
|
TenantID string `json:"tid"` // Microsoft Azure tenant id
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collection is a memory map of admins.
|
||||||
|
type Collection struct {
|
||||||
|
byID *sync.Map
|
||||||
|
bySubProv *sync.Map
|
||||||
|
byProv *sync.Map
|
||||||
|
count int
|
||||||
|
countByProvisioner map[string]int
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCollection initializes a collection of provisioners. The given list of
|
||||||
|
// audiences are the audiences used by the JWT provisioner.
|
||||||
|
func NewCollection() *Collection {
|
||||||
|
return &Collection{
|
||||||
|
byID: new(sync.Map),
|
||||||
|
byProv: new(sync.Map),
|
||||||
|
bySubProv: new(sync.Map),
|
||||||
|
countByProvisioner: map[string]int{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadByID a admin by the ID.
|
||||||
|
func (c *Collection) LoadByID(id string) (*Admin, bool) {
|
||||||
|
return loadAdmin(c.byID, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func subProvNameHash(sub, provName string) string {
|
||||||
|
subHash := sha1.Sum([]byte(sub))
|
||||||
|
provNameHash := sha1.Sum([]byte(provName))
|
||||||
|
_res := sha1.Sum(append(subHash[:], provNameHash[:]...))
|
||||||
|
return string(_res[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadBySubProv a admin by the subject and provisioner name.
|
||||||
|
func (c *Collection) LoadBySubProv(sub, provName string) (*Admin, bool) {
|
||||||
|
return loadAdmin(c.bySubProv, subProvNameHash(sub, provName))
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadByProvisioner a admin by the subject and provisioner name.
|
||||||
|
func (c *Collection) LoadByProvisioner(provName string) ([]*Admin, bool) {
|
||||||
|
a, ok := c.byProv.Load(provName)
|
||||||
|
if !ok {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
admins, ok := a.([]*Admin)
|
||||||
|
if !ok {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
return admins, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store adds an admin to the collection and enforces the uniqueness of
|
||||||
|
// admin IDs and amdin subject <-> provisioner name combos.
|
||||||
|
func (c *Collection) Store(adm *Admin) error {
|
||||||
|
provName := adm.ProvisionerName
|
||||||
|
// Store admin always in byID. ID must be unique.
|
||||||
|
if _, loaded := c.byID.LoadOrStore(adm.ID, adm); loaded {
|
||||||
|
return errors.New("cannot add multiple admins with the same id")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store admin alwasy in bySubProv. Subject <-> ProvisionerName must be unique.
|
||||||
|
if _, loaded := c.bySubProv.LoadOrStore(subProvNameHash(adm.Subject, provName), adm); loaded {
|
||||||
|
c.byID.Delete(adm.ID)
|
||||||
|
return errors.New("cannot add multiple admins with the same subject and provisioner")
|
||||||
|
}
|
||||||
|
|
||||||
|
if admins, ok := c.LoadByProvisioner(provName); ok {
|
||||||
|
c.byProv.Store(provName, append(admins, adm))
|
||||||
|
c.countByProvisioner[provName]++
|
||||||
|
} else {
|
||||||
|
c.byProv.Store(provName, []*Admin{adm})
|
||||||
|
c.countByProvisioner[provName] = 1
|
||||||
|
}
|
||||||
|
c.count++
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count returns the total number of admins.
|
||||||
|
func (c *Collection) Count() int {
|
||||||
|
return c.count
|
||||||
|
}
|
||||||
|
|
||||||
|
// CountByProvisioner returns the total number of admins.
|
||||||
|
func (c *Collection) CountByProvisioner(provName string) int {
|
||||||
|
if cnt, ok := c.countByProvisioner[provName]; ok {
|
||||||
|
return cnt
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Find implements pagination on a list of sorted provisioners.
|
||||||
|
func (c *Collection) Find(cursor string, limit int) (List, string) {
|
||||||
|
switch {
|
||||||
|
case limit <= 0:
|
||||||
|
limit = DefaultProvisionersLimit
|
||||||
|
case limit > DefaultProvisionersMax:
|
||||||
|
limit = DefaultProvisionersMax
|
||||||
|
}
|
||||||
|
|
||||||
|
n := c.sorted.Len()
|
||||||
|
cursor = fmt.Sprintf("%040s", cursor)
|
||||||
|
i := sort.Search(n, func(i int) bool { return c.sorted[i].uid >= cursor })
|
||||||
|
|
||||||
|
slice := List{}
|
||||||
|
for ; i < n && len(slice) < limit; i++ {
|
||||||
|
slice = append(slice, c.sorted[i].provisioner)
|
||||||
|
}
|
||||||
|
|
||||||
|
if i < n {
|
||||||
|
return slice, strings.TrimLeft(c.sorted[i].uid, "0")
|
||||||
|
}
|
||||||
|
return slice, ""
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
func loadAdmin(m *sync.Map, key string) (*Admin, bool) {
|
||||||
|
a, ok := m.Load(key)
|
||||||
|
if !ok {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
adm, ok := a.(*Admin)
|
||||||
|
if !ok {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
return adm, true
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
// provisionerSum returns the SHA1 of the provisioners ID. From this we will
|
||||||
|
// create the unique and sorted id.
|
||||||
|
func provisionerSum(p Interface) []byte {
|
||||||
|
sum := sha1.Sum([]byte(p.GetID()))
|
||||||
|
return sum[:]
|
||||||
|
}
|
||||||
|
*/
|
@ -1,27 +1,55 @@
|
|||||||
package mgmt
|
package mgmt
|
||||||
|
|
||||||
import "context"
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/smallstep/certificates/authority/admin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AdminType specifies the type of the admin. e.g. SUPER_ADMIN, REGULAR
|
||||||
|
type AdminType string
|
||||||
|
|
||||||
|
var (
|
||||||
|
// AdminTypeSuper superadmin
|
||||||
|
AdminTypeSuper = AdminType("SUPER_ADMIN")
|
||||||
|
// AdminTypeRegular regular
|
||||||
|
AdminTypeRegular = AdminType("REGULAR")
|
||||||
|
)
|
||||||
|
|
||||||
// Admin type.
|
// Admin type.
|
||||||
type Admin struct {
|
type Admin struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
AuthorityID string `json:"-"`
|
AuthorityID string `json:"-"`
|
||||||
ProvisionerID string `json:"provisionerID"`
|
ProvisionerID string `json:"provisionerID"`
|
||||||
Name string `json:"name"`
|
Subject string `json:"subject"`
|
||||||
IsSuperAdmin bool `json:"isSuperAdmin"`
|
ProvisionerName string `json:"provisionerName"`
|
||||||
Status StatusType `json:"status"`
|
ProvisionerType string `json:"provisionerType"`
|
||||||
|
Type AdminType `json:"type"`
|
||||||
|
Status StatusType `json:"status"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateAdmin builds and stores an admin type in the DB.
|
// CreateAdmin builds and stores an admin type in the DB.
|
||||||
func CreateAdmin(ctx context.Context, db DB, name string, provID string, isSuperAdmin bool) (*Admin, error) {
|
func CreateAdmin(ctx context.Context, db DB, provName, sub string, typ AdminType) (*Admin, error) {
|
||||||
adm := &Admin{
|
adm := &Admin{
|
||||||
Name: name,
|
Subject: sub,
|
||||||
ProvisionerID: provID,
|
ProvisionerName: provName,
|
||||||
IsSuperAdmin: isSuperAdmin,
|
Type: typ,
|
||||||
Status: StatusActive,
|
Status: StatusActive,
|
||||||
}
|
}
|
||||||
if err := db.CreateAdmin(ctx, adm); err != nil {
|
if err := db.CreateAdmin(ctx, adm); err != nil {
|
||||||
return nil, WrapErrorISE(err, "error creating admin")
|
return nil, WrapErrorISE(err, "error creating admin")
|
||||||
}
|
}
|
||||||
return adm, nil
|
return adm, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToCertificates converts an Admin to the Admin type expected by the authority.
|
||||||
|
func (adm *Admin) ToCertificates() (*admin.Admin, error) {
|
||||||
|
return &admin.Admin{
|
||||||
|
ID: adm.ID,
|
||||||
|
Subject: adm.Subject,
|
||||||
|
ProvisionerID: adm.ProvisionerID,
|
||||||
|
ProvisionerName: adm.ProvisionerName,
|
||||||
|
ProvisionerType: adm.ProvisionerType,
|
||||||
|
Type: admin.Type(adm.Type),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue