Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support OIDC auth backends #398

Merged
merged 6 commits into from
May 7, 2019
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion vault/resource_auth_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ func authBackendResource() *schema.Resource {
Type: schema.TypeString,
ForceNew: true,
Optional: true,
Description: "Speficies whether to show this mount in the UI-specific listing endpoint",
Description: "Specifies whether to show this mount in the UI-specific listing endpoint",
},

"local": {
Expand Down
69 changes: 62 additions & 7 deletions vault/resource_jwt_auth_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@ import (
"log"

"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/helper/validation"
"github.com/hashicorp/vault/api"
)

var jwtAuthType = "jwt"

func jwtAuthBackendResource() *schema.Resource {
return &schema.Resource{
Create: jwtAuthBackendWrite,
Expand All @@ -29,6 +28,15 @@ func jwtAuthBackendResource() *schema.Resource {
ValidateFunc: validateNoTrailingSlash,
},

"type": {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be role_type to correspond with this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure... This type belongs to auth backend not to a role.

Type: schema.TypeString,
Optional: true,
ForceNew: true,
Description: "Type of backend. Can be either 'jwt' or 'oidc'",
Default: "jwt",
ValidateFunc: validation.StringInSlice([]string{"jwt", "oidc"}, false),
},

"description": {
Type: schema.TypeString,
Required: false,
Expand All @@ -43,6 +51,19 @@ func jwtAuthBackendResource() *schema.Resource {
Description: "The OIDC Discovery URL, without any .well-known component (base path)",
},

"oidc_client_id": {
Type: schema.TypeString,
Optional: true,
Description: "Client ID used for OIDC",
},

"oidc_client_secret": {
Type: schema.TypeString,
Optional: true,
Sensitive: true,
Description: "Client Secret used for OIDC",
},

"oidc_discovery_ca_pem": {
Type: schema.TypeString,
Optional: true,
Expand All @@ -69,19 +90,26 @@ func jwtAuthBackendResource() *schema.Resource {
Description: "A list of supported signing algorithms. Defaults to [RS256]",
},

"default_role": {
Type: schema.TypeString,
Optional: true,
Description: "The default role to use if none is provided during login",
},

"accessor": {
Type: schema.TypeString,
Computed: true,
Description: "The accessor of the JWT auth backend",
},
"tune": authMountTuneSchema(),
},
}
}

func jwtAuthBackendWrite(d *schema.ResourceData, meta interface{}) error {
client := meta.(*api.Client)

authType := jwtAuthType
authType := d.Get("type").(string)
desc := d.Get("description").(string)
path := getJwtPath(d)

Expand Down Expand Up @@ -146,7 +174,10 @@ func jwtAuthBackendRead(d *schema.ResourceData, meta interface{}) error {

d.Set("accessor", backend.Accessor)
d.Set("oidc_discovery_ca_pem", config.Data["oidc_discovery_ca_pem"])
d.Set("oidc_client_id", config.Data["oidc_client_id"])
d.Set("oidc_client_secret", config.Data["oidc_client_secret"])
d.Set("bound_issuer", config.Data["bound_issuer"])
d.Set("default_role", config.Data["default_role"])
d.Set("oidc_discovery_url", config.Data["oidc_discovery_url"])
d.Set("jwt_validation_pubkeys", config.Data["jwt_validation_pubkeys"])
d.Set("jwt_supported_algs", config.Data["jwt_supported_algs"])
Expand All @@ -168,7 +199,6 @@ func jwtAuthBackendUpdate(d *schema.ResourceData, meta interface{}) error {

oidcDiscoveryUrl, oidcDiscoveryUrlExists := d.GetOk("oidc_discovery_url")
jwtValidationPubKeys, jwtValidationPubKeysExists := d.GetOk("jwt_validation_pubkeys")
jwtSupportedAlgs, jwtSupportedAlgsExists := d.GetOk("jwt_supported_algs")

if oidcDiscoveryUrlExists == jwtValidationPubKeysExists {
return errors.New("exactly one of oidc_discovery_url and jwt_validation_pubkeys should be provided")
Expand All @@ -182,15 +212,40 @@ func jwtAuthBackendUpdate(d *schema.ResourceData, meta interface{}) error {
configuration["jwt_validation_pubkeys"] = jwtValidationPubKeys
}

if jwtSupportedAlgsExists {
configuration["jwt_supported_algs"] = jwtSupportedAlgs
if v, ok := d.GetOkExists("jwt_supported_algs"); ok {
configuration["jwt_supported_algs"] = v
}
if v, ok := d.GetOkExists("oidc_client_id"); ok {
configuration["oidc_client_id"] = v.(string)
}
if v, ok := d.GetOkExists("oidc_client_secret"); ok {
configuration["oidc_client_secret"] = v.(string)
}
if v, ok := d.GetOkExists("default_role"); ok {
configuration["default_role"] = v.(string)
}

_, err := client.Logical().Write(jwtConfigEndpoint(path), configuration)
if err != nil {
return fmt.Errorf("error updating configuration to Vault for path %s: %s", path, err)
}

if d.HasChange("tune") {
log.Printf("[INFO] JWT/OIDC Auth '%q' tune configuration changed", d.Id())
if raw, ok := d.GetOk("tune"); ok {
backendType := d.Get("type")
log.Printf("[DEBUG] Writing %s auth tune to '%q'", backendType, path)

err := authMountTune(client, "auth/"+path, raw)
if err != nil {
return nil
}

log.Printf("[INFO] Written %s auth tune to '%q'", backendType, path)
d.SetPartial("tune")
}
}

return jwtAuthBackendRead(d, meta)
}

Expand All @@ -208,7 +263,7 @@ func getJwtAuthBackendIfPresent(client *api.Client, path string) (*api.AuthMount

for authBackendPath, auth := range auths {

if auth.Type == jwtAuthType && authBackendPath == configuredPath {
if authBackendPath == configuredPath {
return auth, nil
}
}
Expand Down
66 changes: 66 additions & 0 deletions vault/resource_jwt_auth_backend_role.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ func jwtAuthBackendRoleResource() *schema.Resource {
},
Description: "Policies to be set on tokens issued using this role.",
},
"allowed_redirect_uris": {
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
Description: "The list of allowed values for redirect_uri during OIDC logins.",
},
"ttl": {
Type: schema.TypeInt,
Optional: true,
Expand Down Expand Up @@ -98,6 +106,24 @@ func jwtAuthBackendRoleResource() *schema.Resource {
Type: schema.TypeString,
},
},
"oidc_scopes": {
Type: schema.TypeSet,
Optional: true,
Description: "List of OIDC scopes to be used with an OIDC role. The standard scope \"openid\" is automatically included and need not be specified",
sergeytrasko marked this conversation as resolved.
Show resolved Hide resolved
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"bound_claims": {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wondering if that should be plural or singular

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using plurals to be in line with Vault's documentation - https://www.vaultproject.io/api/auth/jwt/index.html

Type: schema.TypeMap,
Optional: true,
Description: "Map of claims/values to match against. The expected value may be a single string or a list of strings.",
},
"claim_mappings": {
Type: schema.TypeMap,
Optional: true,
Description: "Map of claims (keys) to be copied to specified metadata fields (values).",
},
"groups_claim": {
Type: schema.TypeString,
Optional: true,
Expand Down Expand Up @@ -196,6 +222,13 @@ func jwtAuthBackendRoleRead(d *schema.ResourceData, meta interface{}) error {
if err != nil {
return fmt.Errorf("error setting policies in state: %s", err)
}
if resp.Data["allowed_redirect_uris"] != nil {
allowedRedirectUris := util.JsonStringArrayToStringArray(resp.Data["allowed_redirect_uris"].([]interface{}))
err = d.Set("allowed_redirect_uris", allowedRedirectUris)
if err != nil {
return fmt.Errorf("error setting allowed_redirect_uris in state: %s", err)
}
}

tokenTTL, err := resp.Data["ttl"].(json.Number).Int64()
if err != nil {
Expand Down Expand Up @@ -234,6 +267,24 @@ func jwtAuthBackendRoleRead(d *schema.ResourceData, meta interface{}) error {
d.Set("bound_cidrs", make([]string, 0))
}

if resp.Data["oidc_scopes"] != nil {
cidrs := util.JsonStringArrayToStringArray(resp.Data["oidc_scopes"].([]interface{}))
err = d.Set("oidc_scopes", cidrs)
if err != nil {
return fmt.Errorf("error setting oidc_scopes in state: %s", err)
}
} else {
d.Set("oidc_scopes", make([]string, 0))
}

if resp.Data["bound_claims"] != nil {
d.Set("bound_claims", resp.Data["bound_claims"])
}

if resp.Data["claim_mappings"] != nil {
d.Set("claim_mappings", resp.Data["claim_mappings"])
}

d.Set("groups_claim", resp.Data["groups_claim"].(string))
if resp.Data["groups_claim_delimiter_pattern"] != nil {
d.Set("groups_claim_delimiter_pattern", resp.Data["groups_claim_delimiter_pattern"].(string))
Expand Down Expand Up @@ -343,6 +394,9 @@ func jwtAuthBackendRoleDataToWrite(d *schema.ResourceData) map[string]interface{
if dataList := util.TerraformSetToStringArray(d.Get("policies")); len(dataList) > 0 {
data["policies"] = dataList
}
if dataList := util.TerraformSetToStringArray(d.Get("allowed_redirect_uris")); len(dataList) > 0 {
data["allowed_redirect_uris"] = dataList
}

if v, ok := d.GetOk("role_type"); ok {
data["role_type"] = v.(string)
Expand All @@ -368,6 +422,18 @@ func jwtAuthBackendRoleDataToWrite(d *schema.ResourceData) map[string]interface{
data["bound_cidrs"] = dataList
}

if dataList := util.TerraformSetToStringArray(d.Get("oidc_scopes")); len(dataList) > 0 {
data["oidc_scopes"] = dataList
}

if v, ok := d.GetOk("bound_claims"); ok {
data["bound_claims"] = v
}

if v, ok := d.GetOk("claim_mappings"); ok {
data["claim_mappings"] = v
}

if v, ok := d.GetOkExists("groups_claim"); ok {
data["groups_claim"] = v.(string)
}
Expand Down
Loading