pull/599/head
max furman 3 years ago
parent d8d5d7332b
commit 9bfb1c2e7b

@ -32,24 +32,24 @@ func (p adminSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
// Collection is a memory map of admins.
type Collection struct {
byID *sync.Map
bySubProv *sync.Map
byProv *sync.Map
sorted adminSlice
provisioners *provisioner.Collection
count int
countByProvisioner map[string]int
byID *sync.Map
bySubProv *sync.Map
byProv *sync.Map
sorted adminSlice
provisioners *provisioner.Collection
superCount int
superCountByProvisioner map[string]int
}
// NewCollection initializes a collection of provisioners. The given list of
// audiences are the audiences used by the JWT provisioner.
func NewCollection(provisioners *provisioner.Collection) *Collection {
return &Collection{
byID: new(sync.Map),
byProv: new(sync.Map),
bySubProv: new(sync.Map),
countByProvisioner: map[string]int{},
provisioners: provisioners,
byID: new(sync.Map),
byProv: new(sync.Map),
bySubProv: new(sync.Map),
superCountByProvisioner: map[string]int{},
provisioners: provisioners,
}
}
@ -106,12 +106,12 @@ func (c *Collection) Store(adm *Admin) error {
if admins, ok := c.LoadByProvisioner(provName); ok {
c.byProv.Store(provName, append(admins, adm))
c.countByProvisioner[provName]++
c.superCountByProvisioner[provName]++
} else {
c.byProv.Store(provName, []*Admin{adm})
c.countByProvisioner[provName] = 1
c.superCountByProvisioner[provName] = 1
}
c.count++
c.superCount++
// Store sorted admins.
// Use the first 4 bytes (32bit) of the sum to insert the order
@ -131,14 +131,14 @@ func (c *Collection) Store(adm *Admin) error {
return nil
}
// Count returns the total number of admins.
func (c *Collection) Count() int {
return c.count
// SuperCount returns the total number of admins.
func (c *Collection) SuperCount() int {
return c.superCount
}
// CountByProvisioner returns the total number of admins.
func (c *Collection) CountByProvisioner(provName string) int {
if cnt, ok := c.countByProvisioner[provName]; ok {
// SuperCountByProvisioner returns the total number of admins.
func (c *Collection) SuperCountByProvisioner(provName string) int {
if cnt, ok := c.superCountByProvisioner[provName]; ok {
return cnt
}
return 0

@ -120,8 +120,8 @@ func (h *Handler) CreateAdmin(w http.ResponseWriter, r *http.Request) {
func (h *Handler) DeleteAdmin(w http.ResponseWriter, r *http.Request) {
id := chi.URLParam(r, "id")
if h.auth.GetAdminCollection().Count() == 1 {
api.WriteError(w, mgmt.NewError(mgmt.ErrorBadRequestType, "cannot remove last admin"))
if h.auth.GetAdminCollection().SuperCount() == 1 {
api.WriteError(w, mgmt.NewError(mgmt.ErrorBadRequestType, "cannot remove the last super admin"))
return
}

@ -101,34 +101,17 @@ func (h *Handler) GetProvisioners(w http.ResponseWriter, r *http.Request) {
func (h *Handler) CreateProvisioner(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var body CreateProvisionerRequest
if err := api.ReadJSON(r.Body, &body); err != nil {
api.WriteError(w, err)
return
}
if err := body.Validate(h.auth.GetProvisionerCollection()); err != nil {
var prov = new(mgmt.Provisioner)
if err := api.ReadJSON(r.Body, prov); err != nil {
api.WriteError(w, err)
return
}
details, err := mgmt.UnmarshalProvisionerDetails(body.Details)
if err != nil {
api.WriteError(w, mgmt.WrapErrorISE(err, "error unmarshaling provisioner details"))
return
}
// TODO: validate
// TODO: fix this
prov.Claims = mgmt.NewDefaultClaims()
claims := mgmt.NewDefaultClaims()
prov := &mgmt.Provisioner{
Type: body.Type,
Name: body.Name,
Claims: claims,
Details: details,
X509Template: body.X509Template,
X509TemplateData: body.X509TemplateData,
SSHTemplate: body.SSHTemplate,
SSHTemplateData: body.SSHTemplateData,
}
if err := h.db.CreateProvisioner(ctx, prov); err != nil {
api.WriteError(w, err)
return
@ -144,21 +127,20 @@ func (h *Handler) CreateProvisioner(w http.ResponseWriter, r *http.Request) {
func (h *Handler) DeleteProvisioner(w http.ResponseWriter, r *http.Request) {
name := chi.URLParam(r, "name")
p, ok := h.auth.GetProvisionerCollection().LoadByName(name)
if !ok {
api.WriteError(w, mgmt.NewError(mgmt.ErrorNotFoundType, "provisioner %s not found", name))
return
}
c := h.auth.GetAdminCollection()
fmt.Printf("c.Count() = %+v\n", c.Count())
fmt.Printf("c.CountByProvisioner() = %+v\n", c.CountByProvisioner(name))
if c.Count() == c.CountByProvisioner(name) {
if c.SuperCount() == c.SuperCountByProvisioner(name) {
api.WriteError(w, mgmt.NewError(mgmt.ErrorBadRequestType,
"cannot remove provisioner %s because no admins will remain", name))
"cannot remove provisioner %s because no super admins will remain", name))
return
}
ctx := r.Context()
p, ok := h.auth.GetProvisionerCollection().LoadByName(name)
if !ok {
api.WriteError(w, mgmt.NewError(mgmt.ErrorNotFoundType, "provisioner %s not found", name))
return
}
prov, err := h.db.GetProvisioner(ctx, p.GetID())
if err != nil {
api.WriteError(w, mgmt.WrapErrorISE(err, "error loading provisioner %s from db", name))

@ -12,15 +12,6 @@ import (
type ProvisionerOption func(*ProvisionerCtx)
type ProvisionerCtx struct {
JWK *jose.JSONWebKey
JWE *jose.JSONWebEncryption
X509Template, SSHTemplate string
X509TemplateData, SSHTemplateData []byte
Claims *Claims
Password string
}
type ProvisionerType string
var (
@ -35,29 +26,6 @@ var (
ProvisionerTypeX5C = ProvisionerType("X5C")
)
func NewProvisionerCtx(opts ...ProvisionerOption) *ProvisionerCtx {
pc := &ProvisionerCtx{
Claims: NewDefaultClaims(),
}
for _, o := range opts {
o(pc)
}
return pc
}
func WithJWK(jwk *jose.JSONWebKey, jwe *jose.JSONWebEncryption) func(*ProvisionerCtx) {
return func(ctx *ProvisionerCtx) {
ctx.JWK = jwk
ctx.JWE = jwe
}
}
func WithPassword(pass string) func(*ProvisionerCtx) {
return func(ctx *ProvisionerCtx) {
ctx.Password = pass
}
}
type unmarshalProvisioner struct {
ID string `json:"-"`
AuthorityID string `json:"-"`
@ -157,6 +125,38 @@ func CreateProvisioner(ctx context.Context, db DB, typ, name string, opts ...Pro
return p, nil
}
type ProvisionerCtx struct {
JWK *jose.JSONWebKey
JWE *jose.JSONWebEncryption
X509Template, SSHTemplate string
X509TemplateData, SSHTemplateData []byte
Claims *Claims
Password string
}
func NewProvisionerCtx(opts ...ProvisionerOption) *ProvisionerCtx {
pc := &ProvisionerCtx{
Claims: NewDefaultClaims(),
}
for _, o := range opts {
o(pc)
}
return pc
}
func WithJWK(jwk *jose.JSONWebKey, jwe *jose.JSONWebEncryption) func(*ProvisionerCtx) {
return func(ctx *ProvisionerCtx) {
ctx.JWK = jwk
ctx.JWE = jwe
}
}
func WithPassword(pass string) func(*ProvisionerCtx) {
return func(ctx *ProvisionerCtx) {
ctx.Password = pass
}
}
// ProvisionerDetails is the interface implemented by all provisioner details
// attributes.
type ProvisionerDetails interface {
@ -172,37 +172,61 @@ type ProvisionerDetailsJWK struct {
// ProvisionerDetailsOIDC represents the values required by a OIDC provisioner.
type ProvisionerDetailsOIDC struct {
Type ProvisionerType `json:"type"`
Type ProvisionerType `json:"type"`
ClientID string `json:"clientID"`
ClientSecret string `json:"clientSecret"`
ConfigurationEndpoint string `json:"configurationEndpoint"`
Admins []string `json:"admins"`
Domains []string `json:"domains"`
Groups []string `json:"groups"`
ListenAddress string `json:"listenAddress"`
TenantID string `json:"tenantID"`
}
// ProvisionerDetailsGCP represents the values required by a GCP provisioner.
type ProvisionerDetailsGCP struct {
Type ProvisionerType `json:"type"`
Type ProvisionerType `json:"type"`
ServiceAccounts []string `json:"serviceAccounts"`
ProjectIDs []string `json:"projectIDs"`
DisableCustomSANs bool `json:"disableCustomSANs"`
DisableTrustOnFirstUse bool `json:"disableTrustOnFirstUse"`
InstanceAge string `json:"instanceAge"`
}
// ProvisionerDetailsAWS represents the values required by a AWS provisioner.
type ProvisionerDetailsAWS struct {
Type ProvisionerType `json:"type"`
Type ProvisionerType `json:"type"`
Accounts []string `json:"accounts"`
DisableCustomSANs bool `json:"disableCustomSANs"`
DisableTrustOnFirstUse bool `json:"disableTrustOnFirstUse"`
InstanceAge string `json:"instanceAge"`
}
// ProvisionerDetailsAzure represents the values required by a Azure provisioner.
type ProvisionerDetailsAzure struct {
Type ProvisionerType `json:"type"`
Type ProvisionerType `json:"type"`
ResourceGroups []string `json:"resourceGroups"`
Audience string `json:"audience"`
DisableCustomSANs bool `json:"disableCustomSANs"`
DisableTrustOnFirstUse bool `json:"disableTrustOnFirstUse"`
}
// ProvisionerDetailsACME represents the values required by a ACME provisioner.
type ProvisionerDetailsACME struct {
Type ProvisionerType `json:"type"`
Type ProvisionerType `json:"type"`
ForceCN bool `json:"forceCN"`
}
// ProvisionerDetailsX5C represents the values required by a X5C provisioner.
type ProvisionerDetailsX5C struct {
Type ProvisionerType `json:"type"`
Type ProvisionerType `json:"type"`
Roots []byte `json:"roots"`
}
// ProvisionerDetailsK8SSA represents the values required by a K8SSA provisioner.
type ProvisionerDetailsK8SSA struct {
Type ProvisionerType `json:"type"`
Type ProvisionerType `json:"type"`
PublicKeys []byte `json:"publicKeys"`
}
// ProvisionerDetailsSSHPOP represents the values required by a SSHPOP provisioner.
@ -285,6 +309,42 @@ func createJWKDetails(pc *ProvisionerCtx) (*ProvisionerDetailsJWK, error) {
}, nil
}
func createACMEDetails(pc *ProvisionerCtx) (*ProvisionerDetailsJWK, error) {
var err error
if pc.JWK != nil && pc.JWE == nil {
return nil, NewErrorISE("JWE is required with JWK for createJWKProvisioner")
}
if pc.JWE != nil && pc.JWK == nil {
return nil, NewErrorISE("JWK is required with JWE for createJWKProvisioner")
}
if pc.JWK == nil && pc.JWE == nil {
// Create a new JWK w/ encrypted private key.
if pc.Password == "" {
return nil, NewErrorISE("password is required to provisioner with new keys")
}
pc.JWK, pc.JWE, err = jose.GenerateDefaultKeyPair([]byte(pc.Password))
if err != nil {
return nil, WrapErrorISE(err, "error generating JWK key pair")
}
}
jwkPubBytes, err := pc.JWK.MarshalJSON()
if err != nil {
return nil, WrapErrorISE(err, "error marshaling JWK")
}
jwePrivStr, err := pc.JWE.CompactSerialize()
if err != nil {
return nil, WrapErrorISE(err, "error serializing JWE")
}
return &ProvisionerDetailsJWK{
Type: ProvisionerTypeJWK,
PublicKey: jwkPubBytes,
PrivateKey: jwePrivStr,
}, nil
}
// ToCertificates converts the landlord provisioner type to the open source
// provisioner type.
func (p *Provisioner) ToCertificates() (provisioner.Interface, error) {
@ -461,6 +521,7 @@ type detailsType struct {
Type ProvisionerType
}
// UnmarshalProvisionerDetails unmarshals bytes into the proper details type.
func UnmarshalProvisionerDetails(data json.RawMessage) (ProvisionerDetails, error) {
dt := new(detailsType)
if err := json.Unmarshal(data, dt); err != nil {

@ -315,9 +315,9 @@ retry:
}
// CreateProvisioner performs the POST /admin/provisioners request to the CA.
func (c *AdminClient) CreateProvisioner(req *mgmtAPI.CreateProvisionerRequest) (*mgmt.Provisioner, error) {
func (c *AdminClient) CreateProvisioner(prov *mgmt.Provisioner) (*mgmt.Provisioner, error) {
var retried bool
body, err := json.Marshal(req)
body, err := json.Marshal(prov)
if err != nil {
return nil, errs.Wrap(http.StatusInternalServerError, err, "error marshaling request")
}
@ -334,11 +334,11 @@ retry:
}
return nil, readAdminError(resp.Body)
}
var prov = new(mgmt.Provisioner)
if err := readJSON(resp.Body, prov); err != nil {
var nuProv = new(mgmt.Provisioner)
if err := readJSON(resp.Body, nuProv); err != nil {
return nil, errors.Wrapf(err, "error reading %s", u)
}
return prov, nil
return nuProv, nil
}
// UpdateProvisioner performs the PUT /admin/provisioners/{id} request to the CA.

Loading…
Cancel
Save