Skip to content

Commit 4a067dc

Browse files
Merge pull request hashicorp#488 from lawliet89/vault-oidc
Add resources for Vault Identity Tokens
2 parents e24a6c3 + 5c5bee3 commit 4a067dc

16 files changed

Lines changed: 1890 additions & 0 deletions

go.sum

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me
160160
github.com/go-test/deep v1.0.1 h1:UQhStjbkDClarlmv0am7OXXO4/GaPdCGiUiMTvi28sg=
161161
github.com/go-test/deep v1.0.1/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
162162
github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
163+
github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw=
163164
github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
164165
github.com/gocql/gocql v0.0.0-20190402132108-0e1d5de854df/go.mod h1:4Fw1eo5iaEhDUs8XyuhSVCVy52Jq3L+/3GJgYkwc+/0=
165166
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=

util/util.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,3 +141,32 @@ func ShortDur(d time.Duration) string {
141141
}
142142
return s
143143
}
144+
145+
func SliceHasElement(list []interface{}, search interface{}) (bool, int) {
146+
for i, ele := range list {
147+
if reflect.DeepEqual(ele, search) {
148+
return true, i
149+
}
150+
}
151+
return false, -1
152+
}
153+
154+
func SliceAppendIfMissing(list []interface{}, search interface{}) []interface{} {
155+
if found, _ := SliceHasElement(list, search); !found {
156+
return append(list, search)
157+
}
158+
159+
return list
160+
}
161+
162+
// Warning: Slice order will be modified
163+
func SliceRemoveIfPresent(list []interface{}, search interface{}) []interface{} {
164+
if found, index := SliceHasElement(list, search); found {
165+
// Set the index we found to be the last item
166+
list[index] = list[len(list)-1]
167+
// Return slice sans last item
168+
return list[:len(list)-1]
169+
}
170+
171+
return list
172+
}

util/util_test.go

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,15 @@ package util
22

33
import (
44
"fmt"
5+
"reflect"
56
"testing"
67
)
78

9+
type testingStruct struct {
10+
foobar bool
11+
list []string
12+
}
13+
814
func TestExpiredTokenError(t *testing.T) {
915
if ok := IsExpiredTokenErr(fmt.Errorf("error: invalid accessor custom_accesor_value")); !ok {
1016
t.Errorf("Should be expired")
@@ -19,3 +25,124 @@ func TestExpiredTokenError(t *testing.T) {
1925
t.Errorf("Shouldn't be expired")
2026
}
2127
}
28+
29+
func TestSliceHasElement_scalar(t *testing.T) {
30+
slice := []interface{}{1, 2, 3, 4, 5}
31+
32+
found, index := SliceHasElement(slice, 2)
33+
if !found && index != 1 {
34+
t.Errorf("Slice should find element")
35+
}
36+
37+
found, index = SliceHasElement(slice, 10)
38+
if found && index != -1 {
39+
t.Errorf("Slice should not find element")
40+
}
41+
}
42+
43+
func TestSliceHasElement_struct(t *testing.T) {
44+
slice := []interface{}{
45+
testingStruct{foobar: false, list: []string{"hello", "world"}},
46+
testingStruct{foobar: true, list: []string{"best", "line", "on", "the", "citadel"}},
47+
testingStruct{foobar: true, list: []string{"I", "gotta", "go"}},
48+
}
49+
50+
found, index := SliceHasElement(slice, testingStruct{foobar: true, list: []string{"I", "gotta", "go"}})
51+
if !found && index != 1 {
52+
t.Errorf("Slice should find element")
53+
}
54+
55+
found, index = SliceHasElement(slice, testingStruct{foobar: false, list: []string{}})
56+
if found && index != -1 {
57+
t.Errorf("Slice should not find element")
58+
}
59+
60+
found, index = SliceHasElement(slice, 10)
61+
if found && index != -1 {
62+
t.Errorf("Slice should not find element")
63+
}
64+
}
65+
66+
func TestSliceAppendIfMissing_scalar(t *testing.T) {
67+
slice := []interface{}{1, 2, 3, 4, 5}
68+
expectedAppend := []interface{}{1, 2, 3, 4, 5, 6}
69+
70+
append := SliceAppendIfMissing(slice, 3)
71+
if !reflect.DeepEqual(slice, append) {
72+
t.Errorf("Slice should not be appended")
73+
}
74+
75+
append = SliceAppendIfMissing(slice, 6)
76+
if !reflect.DeepEqual(expectedAppend, append) {
77+
t.Errorf("Slice should be appended")
78+
}
79+
}
80+
81+
func TestSliceAppendIfMissing_struct(t *testing.T) {
82+
slice := []interface{}{
83+
testingStruct{foobar: false, list: []string{"hello", "world"}},
84+
testingStruct{foobar: true, list: []string{"best", "line", "on", "the", "citadel"}},
85+
}
86+
expectedAppend := []interface{}{
87+
testingStruct{foobar: false, list: []string{"hello", "world"}},
88+
testingStruct{foobar: true, list: []string{"best", "line", "on", "the", "citadel"}},
89+
testingStruct{foobar: true, list: []string{"I", "gotta", "go"}},
90+
}
91+
92+
append := SliceAppendIfMissing(slice, testingStruct{foobar: false, list: []string{"hello", "world"}})
93+
if !reflect.DeepEqual(slice, append) {
94+
t.Errorf("Slice should not be appended")
95+
}
96+
97+
append = SliceAppendIfMissing(slice, testingStruct{foobar: true, list: []string{"I", "gotta", "go"}})
98+
if !reflect.DeepEqual(expectedAppend, append) {
99+
t.Errorf("Slice should be appended")
100+
}
101+
}
102+
103+
func TestSliceRemoveIfPresent_scalar(t *testing.T) {
104+
slice := []interface{}{1, 2, 3, 4, 5}
105+
expected := []interface{}{1, 2, 5, 4}
106+
107+
removed := SliceRemoveIfPresent(slice, 10)
108+
if !reflect.DeepEqual(slice, removed) {
109+
t.Errorf("Slice should not be modified")
110+
}
111+
112+
removed = SliceRemoveIfPresent(slice, 3)
113+
if !reflect.DeepEqual(expected, removed) {
114+
t.Errorf("Slice should be modified")
115+
}
116+
117+
empty := make([]interface{}, 0)
118+
if len(SliceRemoveIfPresent(empty, 0)) != 0 {
119+
t.Errorf("Slice should be empty")
120+
}
121+
122+
single := []interface{}{1}
123+
if len(SliceRemoveIfPresent(single, 1)) != 0 {
124+
t.Errorf("Slice should be empty")
125+
}
126+
}
127+
128+
func TestSliceRemoveIfPresent_struct(t *testing.T) {
129+
slice := []interface{}{
130+
testingStruct{foobar: false, list: []string{"hello", "world"}},
131+
testingStruct{foobar: true, list: []string{"best", "line", "on", "the", "citadel"}},
132+
testingStruct{foobar: true, list: []string{"I", "gotta", "go"}},
133+
}
134+
expected := []interface{}{
135+
testingStruct{foobar: true, list: []string{"I", "gotta", "go"}},
136+
testingStruct{foobar: true, list: []string{"best", "line", "on", "the", "citadel"}},
137+
}
138+
139+
removed := SliceRemoveIfPresent(slice, testingStruct{foobar: false, list: []string{}})
140+
if !reflect.DeepEqual(slice, removed) {
141+
t.Errorf("Slice should not be modified")
142+
}
143+
144+
removed = SliceRemoveIfPresent(slice, testingStruct{foobar: false, list: []string{"hello", "world"}})
145+
if !reflect.DeepEqual(expected, removed) {
146+
t.Errorf("Slice should be modified")
147+
}
148+
}

vault/provider.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,22 @@ var (
412412
Resource: identityGroupPoliciesResource(),
413413
PathInventory: []string{"/identity/lookup/group"},
414414
},
415+
"vault_identity_oidc": {
416+
Resource: identityOidc(),
417+
PathInventory: []string{"/identity/oidc/config"},
418+
},
419+
"vault_identity_oidc_key": {
420+
Resource: identityOidcKey(),
421+
PathInventory: []string{"/identity/oidc/key/{name}"},
422+
},
423+
"vault_identity_oidc_key_allowed_client_id": {
424+
Resource: identityOidcKeyAllowedClientId(),
425+
PathInventory: []string{"/identity/oidc/key/{key_name}"},
426+
},
427+
"vault_identity_oidc_role": {
428+
Resource: identityOidcRole(),
429+
PathInventory: []string{"/identity/oidc/role/{name}"},
430+
},
415431
"vault_rabbitmq_secret_backend": {
416432
Resource: rabbitmqSecretBackendResource(),
417433
PathInventory: []string{

vault/resource_identity_oidc.go

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
package vault
2+
3+
import (
4+
"fmt"
5+
"log"
6+
7+
"github.com/hashicorp/terraform/helper/schema"
8+
"github.com/hashicorp/vault/api"
9+
)
10+
11+
const identityOidcPathTemplate = "identity/oidc/config"
12+
13+
func identityOidc() *schema.Resource {
14+
return &schema.Resource{
15+
Create: identityOidcCreate,
16+
Update: identityOidcUpdate,
17+
Read: identityOidcRead,
18+
Delete: identityOidcDelete,
19+
Exists: identityOidcExists,
20+
21+
Schema: map[string]*schema.Schema{
22+
"issuer": {
23+
Type: schema.TypeString,
24+
Description: "Issuer URL to be used in the iss claim of the token. If not set, Vault's api_addr will be used. The issuer is a case sensitive URL using the https scheme that contains scheme, host, and optionally, port number and path components, but no query or fragment components.",
25+
Optional: true,
26+
Computed: true,
27+
},
28+
},
29+
}
30+
}
31+
32+
func identityOidcUpdateFields(d *schema.ResourceData, data map[string]interface{}) {
33+
data["issuer"] = d.Get("issuer").(string)
34+
}
35+
36+
func identityOidcCreate(d *schema.ResourceData, meta interface{}) error {
37+
client := meta.(*api.Client)
38+
path := identityOidcPathTemplate
39+
40+
data := make(map[string]interface{})
41+
addr := client.Address()
42+
43+
identityOidcUpdateFields(d, data)
44+
45+
_, err := client.Logical().Write(path, data)
46+
47+
if err != nil {
48+
return fmt.Errorf("error writing IdentityOidc %s: %s", addr, err)
49+
}
50+
log.Printf("[DEBUG] Wrote IdentityOidc to %s", addr)
51+
52+
d.SetId(addr)
53+
54+
return identityOidcRead(d, meta)
55+
}
56+
57+
func identityOidcUpdate(d *schema.ResourceData, meta interface{}) error {
58+
client := meta.(*api.Client)
59+
path := identityOidcPathTemplate
60+
addr := d.Id()
61+
62+
log.Printf("[DEBUG] Updating IdentityOidc for %s", addr)
63+
64+
data := map[string]interface{}{}
65+
66+
identityOidcUpdateFields(d, data)
67+
68+
_, err := client.Logical().Write(path, data)
69+
70+
if err != nil {
71+
return fmt.Errorf("error updating IdentityOidc for %s: %s", addr, err)
72+
}
73+
log.Printf("[DEBUG] Updated IdentityOidc for %q", addr)
74+
75+
return identityOidcRead(d, meta)
76+
}
77+
78+
func identityOidcRead(d *schema.ResourceData, meta interface{}) error {
79+
client := meta.(*api.Client)
80+
path := identityOidcPathTemplate
81+
addr := d.Id()
82+
83+
log.Printf("[DEBUG] Reading IdentityOidc for %s", addr)
84+
resp, err := client.Logical().Read(path)
85+
if err != nil {
86+
return fmt.Errorf("error reading IdentityOidc for %s: %s", addr, err)
87+
}
88+
log.Printf("[DEBUG] Read IdentityOidc for %s", addr)
89+
if resp == nil {
90+
log.Printf("[WARN] IdentityOidc %s not found, removing from state", addr)
91+
d.SetId("")
92+
return nil
93+
}
94+
95+
for _, k := range []string{"issuer"} {
96+
if err := d.Set(k, resp.Data[k]); err != nil {
97+
return fmt.Errorf("error setting state key \"%s\" on IdentityOidc %q: %s", k, addr, err)
98+
}
99+
}
100+
return nil
101+
}
102+
103+
func identityOidcDelete(d *schema.ResourceData, meta interface{}) error {
104+
client := meta.(*api.Client)
105+
addr := d.Id()
106+
path := identityOidcPathTemplate
107+
108+
log.Printf("[DEBUG] Reseting IdentityOidc for %q back to defaults", addr)
109+
110+
d.Set("issuer", "")
111+
data := map[string]interface{}{}
112+
identityOidcUpdateFields(d, data)
113+
114+
_, err := client.Logical().Write(path, data)
115+
if err != nil {
116+
return fmt.Errorf("error resetting IdentityOidc %s, %s", addr, err)
117+
}
118+
log.Printf("[DEBUG] Finished resetting IdentityOidc for %q", addr)
119+
120+
return nil
121+
}
122+
123+
func identityOidcExists(d *schema.ResourceData, meta interface{}) (bool, error) {
124+
client := meta.(*api.Client)
125+
addr := d.Id()
126+
path := identityOidcPathTemplate
127+
128+
log.Printf("[DEBUG] Checking if IdentityOidc for %q is set", addr)
129+
resp, err := client.Logical().Read(path)
130+
if err != nil {
131+
return true, fmt.Errorf("error checking if IdentityOidc for %q is set: %s", addr, err)
132+
}
133+
log.Printf("[DEBUG] Checked if IdentityOidc for %q is set", addr)
134+
135+
return resp.Data["issuer"].(string) != "", nil
136+
}

0 commit comments

Comments
 (0)