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
|
||||
|
||||
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.
|
||||
type Admin struct {
|
||||
ID string `json:"id"`
|
||||
AuthorityID string `json:"-"`
|
||||
ProvisionerID string `json:"provisionerID"`
|
||||
Name string `json:"name"`
|
||||
IsSuperAdmin bool `json:"isSuperAdmin"`
|
||||
Status StatusType `json:"status"`
|
||||
ID string `json:"id"`
|
||||
AuthorityID string `json:"-"`
|
||||
ProvisionerID string `json:"provisionerID"`
|
||||
Subject string `json:"subject"`
|
||||
ProvisionerName string `json:"provisionerName"`
|
||||
ProvisionerType string `json:"provisionerType"`
|
||||
Type AdminType `json:"type"`
|
||||
Status StatusType `json:"status"`
|
||||
}
|
||||
|
||||
// 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{
|
||||
Name: name,
|
||||
ProvisionerID: provID,
|
||||
IsSuperAdmin: isSuperAdmin,
|
||||
Status: StatusActive,
|
||||
Subject: sub,
|
||||
ProvisionerName: provName,
|
||||
Type: typ,
|
||||
Status: StatusActive,
|
||||
}
|
||||
if err := db.CreateAdmin(ctx, adm); err != nil {
|
||||
return nil, WrapErrorISE(err, "error creating admin")
|
||||
}
|
||||
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