forked from TrueCloudLab/certificates
Add support for extensions and critical options on the identity
function.
This commit is contained in:
parent
8ff8d90f8c
commit
02379d494b
4 changed files with 105 additions and 2 deletions
|
@ -384,6 +384,14 @@ func (o *OIDC) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption
|
||||||
if v, err := unsafeParseSigned(token); err == nil {
|
if v, err := unsafeParseSigned(token); err == nil {
|
||||||
data.SetToken(v)
|
data.SetToken(v)
|
||||||
}
|
}
|
||||||
|
// Add custom extensions added in the identity function.
|
||||||
|
for k, v := range iden.Permissions.Extensions {
|
||||||
|
data.AddExtension(k, v)
|
||||||
|
}
|
||||||
|
// Add custom critical options added in the identity function.
|
||||||
|
for k, v := range iden.Permissions.CriticalOptions {
|
||||||
|
data.AddCriticalOption(k, v)
|
||||||
|
}
|
||||||
|
|
||||||
templateOptions, err := TemplateSSHOptions(o.SSHOptions, data)
|
templateOptions, err := TemplateSSHOptions(o.SSHOptions, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -327,6 +327,13 @@ func (b *base) AuthorizeSSHRekey(ctx context.Context, token string) (*ssh.Certif
|
||||||
// by provisioners to populate certificate fields.
|
// by provisioners to populate certificate fields.
|
||||||
type Identity struct {
|
type Identity struct {
|
||||||
Usernames []string `json:"usernames"`
|
Usernames []string `json:"usernames"`
|
||||||
|
Permissions `json:"permissions"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Permissions defines extra extensions and critical options to grant to an SSH certificate.
|
||||||
|
type Permissions struct {
|
||||||
|
Extensions map[string]string `json:"extensions"`
|
||||||
|
CriticalOptions map[string]string `json:"criticalOptions"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetIdentityFunc is a function that returns an identity.
|
// GetIdentityFunc is a function that returns an identity.
|
||||||
|
|
|
@ -5,6 +5,7 @@ const (
|
||||||
KeyIDKey = "KeyID"
|
KeyIDKey = "KeyID"
|
||||||
PrincipalsKey = "Principals"
|
PrincipalsKey = "Principals"
|
||||||
ExtensionsKey = "Extensions"
|
ExtensionsKey = "Extensions"
|
||||||
|
CriticalOptionsKey = "CriticalOptions"
|
||||||
TokenKey = "Token"
|
TokenKey = "Token"
|
||||||
InsecureKey = "Insecure"
|
InsecureKey = "Insecure"
|
||||||
UserKey = "User"
|
UserKey = "User"
|
||||||
|
@ -70,6 +71,17 @@ func (t TemplateData) AddExtension(key, value string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddCriticalOption adds one critical option to the templates data.
|
||||||
|
func (t TemplateData) AddCriticalOption(key, value string) {
|
||||||
|
if m, ok := t[CriticalOptionsKey].(map[string]interface{}); ok {
|
||||||
|
m[key] = value
|
||||||
|
} else {
|
||||||
|
t[CriticalOptionsKey] = map[string]interface{}{
|
||||||
|
key: value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Set sets a key-value pair in the template data.
|
// Set sets a key-value pair in the template data.
|
||||||
func (t TemplateData) Set(key string, v interface{}) {
|
func (t TemplateData) Set(key string, v interface{}) {
|
||||||
t[key] = v
|
t[key] = v
|
||||||
|
@ -104,6 +116,12 @@ func (t TemplateData) SetExtensions(e map[string]interface{}) {
|
||||||
t.Set(ExtensionsKey, e)
|
t.Set(ExtensionsKey, e)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetCriticalOptions sets the certificate critical options in the template
|
||||||
|
// data.
|
||||||
|
func (t TemplateData) SetCriticalOptions(o map[string]interface{}) {
|
||||||
|
t.Set(CriticalOptionsKey, o)
|
||||||
|
}
|
||||||
|
|
||||||
// SetToken sets the given token in the template data.
|
// SetToken sets the given token in the template data.
|
||||||
func (t TemplateData) SetToken(v interface{}) {
|
func (t TemplateData) SetToken(v interface{}) {
|
||||||
t.Set(TokenKey, v)
|
t.Set(TokenKey, v)
|
||||||
|
@ -126,7 +144,8 @@ const DefaultCertificate = `{
|
||||||
"type": "{{ .Type }}",
|
"type": "{{ .Type }}",
|
||||||
"keyId": "{{ .KeyID }}",
|
"keyId": "{{ .KeyID }}",
|
||||||
"principals": {{ toJson .Principals }},
|
"principals": {{ toJson .Principals }},
|
||||||
"extensions": {{ toJson .Extensions }}
|
"extensions": {{ toJson .Extensions }},
|
||||||
|
"criticalOptions": {{ toJson .CriticalOptions }}
|
||||||
}`
|
}`
|
||||||
|
|
||||||
const DefaultIIDCertificate = `{
|
const DefaultIIDCertificate = `{
|
||||||
|
|
|
@ -157,6 +157,46 @@ func TestTemplateData_AddExtension(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTemplateData_AddCriticalOption(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
key string
|
||||||
|
value string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
t TemplateData
|
||||||
|
args args
|
||||||
|
want TemplateData
|
||||||
|
}{
|
||||||
|
{"empty", TemplateData{}, args{"key", "value"}, TemplateData{
|
||||||
|
CriticalOptionsKey: map[string]interface{}{"key": "value"},
|
||||||
|
}},
|
||||||
|
{"overwrite", TemplateData{
|
||||||
|
CriticalOptionsKey: map[string]interface{}{"key": "value"},
|
||||||
|
}, args{"key", "value"}, TemplateData{
|
||||||
|
CriticalOptionsKey: map[string]interface{}{
|
||||||
|
"key": "value",
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
{"add", TemplateData{
|
||||||
|
CriticalOptionsKey: map[string]interface{}{"foo": "bar"},
|
||||||
|
}, args{"key", "value"}, TemplateData{
|
||||||
|
CriticalOptionsKey: map[string]interface{}{
|
||||||
|
"key": "value",
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
tt.t.AddCriticalOption(tt.args.key, tt.args.value)
|
||||||
|
if !reflect.DeepEqual(tt.t, tt.want) {
|
||||||
|
t.Errorf("AddCriticalOption() = %v, want %v", tt.t, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestTemplateData_Set(t *testing.T) {
|
func TestTemplateData_Set(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
key string
|
key string
|
||||||
|
@ -325,6 +365,35 @@ func TestTemplateData_SetExtensions(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTemplateData_SetCriticalOptions(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
e map[string]interface{}
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
t TemplateData
|
||||||
|
args args
|
||||||
|
want TemplateData
|
||||||
|
}{
|
||||||
|
{"ok", TemplateData{}, args{map[string]interface{}{"foo": "bar"}}, TemplateData{
|
||||||
|
CriticalOptionsKey: map[string]interface{}{"foo": "bar"},
|
||||||
|
}},
|
||||||
|
{"overwrite", TemplateData{
|
||||||
|
CriticalOptionsKey: map[string]interface{}{"foo": "bar"},
|
||||||
|
}, args{map[string]interface{}{"key": "value"}}, TemplateData{
|
||||||
|
CriticalOptionsKey: map[string]interface{}{"key": "value"},
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
tt.t.SetCriticalOptions(tt.args.e)
|
||||||
|
if !reflect.DeepEqual(tt.t, tt.want) {
|
||||||
|
t.Errorf("SetCriticalOptions() = %v, want %v", tt.t, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestTemplateData_SetToken(t *testing.T) {
|
func TestTemplateData_SetToken(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
v interface{}
|
v interface{}
|
||||||
|
|
Loading…
Reference in a new issue