Add support for extensions and critical options on the identity

function.
This commit is contained in:
Mariano Cano 2020-07-30 17:24:05 -07:00
parent 8ff8d90f8c
commit 02379d494b
4 changed files with 105 additions and 2 deletions

View file

@ -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 {

View file

@ -326,7 +326,14 @@ func (b *base) AuthorizeSSHRekey(ctx context.Context, token string) (*ssh.Certif
// Identity is the type representing an externally supplied identity that is used // Identity is the type representing an externally supplied identity that is used
// 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.

View file

@ -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 = `{

View file

@ -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{}