Skip to content

Commit df5bc11

Browse files
committed
Initialize Open Source Project tencentcloud-vault
1 parent 4bd1b2f commit df5bc11

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+2614
-2510
lines changed

LICENSE

-373
This file was deleted.

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ EXTERNAL_TOOLS=\
77
BUILD_TAGS?=${TOOL}
88
GOFMT_FILES?=$$(find . -name '*.go' | grep -v vendor)
99

10-
# bin generates the releasable binaries for this plugin
10+
# bin generates the releaseable binaries for this plugin
1111
bin: fmtcheck generate
1212
@CGO_ENABLED=0 BUILD_TAGS='$(BUILD_TAGS)' sh -c "'$(CURDIR)/scripts/build.sh'"
1313

README.md

+5-4
Original file line numberDiff line numberDiff line change
@@ -98,10 +98,11 @@ To run the acceptance tests, you will need a TencentCloud account.
9898
To run the acceptance tests, invoke `make test-acc`:
9999

100100
```sh
101-
$ export VAULT_ACC_TEST_ACCESS_KEY=YOU ACCESS KEY
101+
$ export VAULT_ACC_TEST_SECRET_ID=YOU
102102
$ export VAULT_ACC_TEST_SECRET_KEY=YOU SECRET KEY
103-
$ export VAULT_ACC_TEST_SECRET_TOKEN=YOU SECRET TOKEN (if you run as a CAM role, VAULT_ACC_TEST_SECRET_TOKEN is required)
104-
$ export VAULT_ACC_TEST_REGION=YOU REGION
103+
$ export VAULT_ACC_TEST_TOKEN=YOU SECRET TOKEN (if you run as a CAM role, VAULT_ACC_TEST_TOKEN is required)
104+
$ export CLIENT_CONFIG_TEST_SECRET_ID=CLIENT CONFIG SECRET ID
105+
$ export CLIENT_CONFIG_TEST_SECRET_KEY=CLIENT CONFIG SECRET KEY
105106
$ make test-acc
106107
```
107108

@@ -121,4 +122,4 @@ You can also specify a `TESTARGS` variable to filter tests like so:
121122

122123
```sh
123124
$ make test TESTARGS='--run=TestConfig'
124-
```
125+
```

arn.go

+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package vault_plugin_auth_tencentcloud
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"strings"
7+
)
8+
9+
const (
10+
arnRoleType arnType = iota // roleName
11+
arnAssumedRoleType // assumed-role
12+
)
13+
14+
const (
15+
roleName = "roleName"
16+
assumedRole = "assumed-role"
17+
)
18+
19+
type arnType int
20+
21+
// toString
22+
func (t arnType) String() string {
23+
switch t {
24+
case arnRoleType:
25+
return roleName
26+
case arnAssumedRoleType:
27+
return assumedRole
28+
default:
29+
return ""
30+
}
31+
}
32+
33+
type arn struct {
34+
Uin string
35+
RoleName string
36+
RoleId string
37+
Full string
38+
Type arnType
39+
}
40+
41+
// check member
42+
func (a *arn) IsMemberOf(possibleParent *arn) bool {
43+
if possibleParent.Type != arnRoleType && possibleParent.Type != arnAssumedRoleType {
44+
return false
45+
}
46+
if possibleParent.Uin != a.Uin {
47+
return false
48+
}
49+
if possibleParent.RoleName != a.RoleName {
50+
return false
51+
}
52+
return true
53+
}
54+
55+
func parseARN(a string) (*arn, error) {
56+
// camArn should look like one of the following:
57+
// 1. qcs::cam::uin/<uin>:roleName/<RoleName>
58+
// 2. qcs::sts:<uin>:assumed-role/<RoleId>
59+
// if we get something like 2, then we want to transform that back to what
60+
// most people would expect, which is qcs::cam::uin/<uin>:roleName/<RoleName>
61+
if a == "" {
62+
return nil, fmt.Errorf("no arn provided")
63+
}
64+
parsed := &arn{
65+
Full: a,
66+
}
67+
outerFields := strings.Split(a, ":")
68+
if len(outerFields) != 6 && len(outerFields) != 5 {
69+
return nil, fmt.Errorf("unrecognized arn: contains %d colon-separated fields, expected 6 or 5", len(outerFields))
70+
}
71+
if outerFields[0] != "qcs" {
72+
return nil, errors.New(`unrecognized arn: does not begin with "qcs:"`)
73+
}
74+
if outerFields[2] != "cam" && outerFields[2] != "sts" {
75+
return nil, fmt.Errorf("unrecognized service: %v, not cam or sts", outerFields[2])
76+
}
77+
if outerFields[2] == "cam" {
78+
uinFields := strings.Split(outerFields[4], "/")
79+
if len(uinFields) < 2 {
80+
return nil, fmt.Errorf("unrecognized arn: %q contains fewer than 2 slash-separated uinFields", outerFields[4])
81+
}
82+
parsed.Uin = uinFields[1]
83+
roleFiles := strings.Split(outerFields[5], "/")
84+
if len(roleFiles) == 2 {
85+
parsed.Type = arnRoleType
86+
if roleFiles[0] == roleName {
87+
parsed.RoleName = roleFiles[1]
88+
} else {
89+
return nil, errors.New("the caller's arn does not match the role's arn")
90+
}
91+
} else {
92+
return nil, fmt.Errorf("unrecognized arn: %q contains fewer than 2 slash-separated roleFiles", outerFields[4])
93+
}
94+
} else if outerFields[2] == "sts" {
95+
parsed.Uin = outerFields[3]
96+
roleFiles := strings.Split(outerFields[4], "/")
97+
if len(roleFiles) == 2 {
98+
parsed.Type = arnAssumedRoleType
99+
if roleFiles[0] == assumedRole {
100+
parsed.RoleId = roleFiles[1]
101+
} else {
102+
return nil, errors.New("the caller's arn does not match the role's arn")
103+
}
104+
} else {
105+
return nil, fmt.Errorf("unrecognized arn: %q contains fewer than 2 slash-separated roleFiles", outerFields[4])
106+
}
107+
}
108+
return parsed, nil
109+
}
110+
111+
// toString
112+
func (a *arn) String() string {
113+
return a.Full
114+
}

arn_test.go

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package vault_plugin_auth_tencentcloud
2+
3+
import (
4+
"testing"
5+
)
6+
7+
func TestParseRoleArn(t *testing.T) {
8+
// qcs::cam::uin/100021543***:roleName/****
9+
arn := "qcs::cam::uin/1000215438890:roleName/elk"
10+
result, err := parseARN(arn)
11+
if err != nil {
12+
t.Fatal(err)
13+
}
14+
if result.Uin != "1000215438890" {
15+
t.Fatalf("got %s but expected %s", result.Uin, "1000215438890")
16+
}
17+
if result.Type != arnRoleType {
18+
t.Fatalf("got %d but expected %d", result.Type, arnRoleType)
19+
}
20+
if result.RoleName != "elk" {
21+
t.Fatalf("got %s but wanted %s", result.RoleName, "elk")
22+
}
23+
if result.RoleId != "" {
24+
t.Fatalf("got %s but wanted %s", result.RoleId, "")
25+
}
26+
}
27+
28+
func TestParseAssumedRoleArn(t *testing.T) {
29+
// qcs::sts:1000262***:assumed-role/461168601842741***
30+
arn := "qcs::sts:1000215438890:assumed-role/4611686018427418890"
31+
result, err := parseARN(arn)
32+
if err != nil {
33+
panic(err)
34+
}
35+
if result.Uin != "1000215438890" {
36+
t.Fatalf("got %s but expected %s", result.Uin, "1000215438890")
37+
}
38+
if result.Type != arnAssumedRoleType {
39+
t.Fatalf("got %d but expected %d", result.Type, arnAssumedRoleType)
40+
}
41+
if result.RoleName != "" {
42+
t.Fatalf("got %s but wanted %s", result.RoleName, "")
43+
}
44+
if result.RoleId != "4611686018427418890" {
45+
t.Fatalf("got %s but wanted %s", result.RoleId, "4611686018427418890")
46+
}
47+
}
48+
49+
func TestParseEmpty(t *testing.T) {
50+
arn := ""
51+
_, err := parseARN(arn)
52+
if err == nil {
53+
t.Fatal("expected an err")
54+
}
55+
}
+13-22
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,31 @@
1-
package plugin
1+
package vault_plugin_auth_tencentcloud
22

33
import (
44
"context"
55
"net/http"
6-
"os"
7-
"strings"
86

9-
"github.com/hashicorp/vault-plugin-auth-tencentcloud/sdk"
7+
"github.com/hashicorp/go-cleanhttp"
108
"github.com/hashicorp/vault/sdk/framework"
119
"github.com/hashicorp/vault/sdk/logical"
1210
)
1311

12+
// Factory
1413
func Factory(ctx context.Context, conf *logical.BackendConfig) (logical.Backend, error) {
15-
debug := conf.Logger.IsDebug()
16-
17-
if !debug {
18-
env := strings.ToLower(os.Getenv("VAULT_LOG_LEVEL"))
19-
debug = env == "trace" || env == "debug"
14+
client := cleanhttp.DefaultClient()
15+
client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
16+
return http.ErrUseLastResponse
2017
}
21-
22-
b := newBackend(&sdk.LogRoundTripper{
23-
Debug: debug,
24-
})
25-
18+
b := newBackend(client)
2619
if err := b.Setup(ctx, conf); err != nil {
2720
return nil, err
2821
}
29-
3022
return b, nil
3123
}
3224

33-
func newBackend(transport http.RoundTripper) *backend {
25+
func newBackend(client *http.Client) *backend {
3426
b := &backend{
35-
transport: transport,
27+
identityClient: client,
3628
}
37-
3829
b.Backend = &framework.Backend{
3930
AuthRenew: b.pathLoginRenew,
4031
Help: backendHelp,
@@ -45,21 +36,21 @@ func newBackend(transport http.RoundTripper) *backend {
4536
},
4637
Paths: []*framework.Path{
4738
pathLogin(b),
39+
pathListRole(b),
4840
pathListRoles(b),
4941
pathRole(b),
42+
pathConfigClient(b),
5043
},
5144
BackendType: logical.TypeCredential,
5245
}
53-
5446
return b
5547
}
5648

5749
type backend struct {
5850
*framework.Backend
59-
60-
transport http.RoundTripper
51+
identityClient *http.Client
6152
}
6253

6354
const backendHelp = `
64-
That TencentCloud CAM auth method allows entities to authenticate based on their identity and pre-configured roles.
55+
6556
`

0 commit comments

Comments
 (0)