1package alicloud 2 3import ( 4 "context" 5 "errors" 6 7 "github.com/hashicorp/vault/sdk/framework" 8 "github.com/hashicorp/vault/sdk/logical" 9) 10 11func (b *backend) pathConfig() *framework.Path { 12 return &framework.Path{ 13 Pattern: "config", 14 Fields: map[string]*framework.FieldSchema{ 15 "access_key": { 16 Type: framework.TypeString, 17 Description: "Access key with appropriate permissions.", 18 }, 19 "secret_key": { 20 Type: framework.TypeString, 21 Description: "Secret key with appropriate permissions.", 22 }, 23 }, 24 Callbacks: map[logical.Operation]framework.OperationFunc{ 25 // Your access key and secret are generated together at the same time, 26 // so you always need to clobber your previous ones. Thus, we don't need two separate operations. 27 // When we don't use an existence check, all operations come through as an update operation, 28 // which is why it's the one fulfilled here. 29 logical.UpdateOperation: b.operationConfigUpdate, 30 logical.ReadOperation: b.operationConfigRead, 31 logical.DeleteOperation: b.operationConfigDelete, 32 }, 33 HelpSynopsis: pathConfigRootHelpSyn, 34 HelpDescription: pathConfigRootHelpDesc, 35 } 36} 37 38func (b *backend) operationConfigUpdate(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 39 // Access keys and secrets are generated in pairs. You would never need 40 // to update one or the other alone, always both together. 41 accessKey := "" 42 if accessKeyIfc, ok := data.GetOk("access_key"); ok { 43 accessKey = accessKeyIfc.(string) 44 } else { 45 return nil, errors.New("access_key is required") 46 } 47 secretKey := "" 48 if secretKeyIfc, ok := data.GetOk("secret_key"); ok { 49 secretKey = secretKeyIfc.(string) 50 } else { 51 return nil, errors.New("secret_key is required") 52 } 53 entry, err := logical.StorageEntryJSON("config", credConfig{ 54 AccessKey: accessKey, 55 SecretKey: secretKey, 56 }) 57 if err != nil { 58 return nil, err 59 } 60 if err := req.Storage.Put(ctx, entry); err != nil { 61 return nil, err 62 } 63 return nil, nil 64} 65 66func (b *backend) operationConfigRead(ctx context.Context, req *logical.Request, _ *framework.FieldData) (*logical.Response, error) { 67 creds, err := readCredentials(ctx, req.Storage) 68 if err != nil { 69 return nil, err 70 } 71 if creds == nil { 72 return nil, nil 73 } 74 75 // "secret_key" is intentionally not returned by this endpoint 76 return &logical.Response{ 77 Data: map[string]interface{}{ 78 "access_key": creds.AccessKey, 79 }, 80 }, nil 81} 82 83func (b *backend) operationConfigDelete(ctx context.Context, req *logical.Request, _ *framework.FieldData) (*logical.Response, error) { 84 if err := req.Storage.Delete(ctx, "config"); err != nil { 85 return nil, err 86 } 87 return nil, nil 88} 89 90func readCredentials(ctx context.Context, storage logical.Storage) (*credConfig, error) { 91 entry, err := storage.Get(ctx, "config") 92 if err != nil { 93 return nil, err 94 } 95 if entry == nil { 96 return nil, nil 97 } 98 creds := &credConfig{} 99 if err := entry.DecodeJSON(creds); err != nil { 100 return nil, err 101 } 102 return creds, nil 103} 104 105type credConfig struct { 106 AccessKey string `json:"access_key"` 107 SecretKey string `json:"secret_key"` 108} 109 110const pathConfigRootHelpSyn = ` 111Configure the access key and secret to use for RAM and STS calls. 112` 113 114const pathConfigRootHelpDesc = ` 115Before doing anything, the AliCloud backend needs credentials that are able 116to manage RAM users, policies, and access keys, and that can call STS AssumeRole. 117This endpoint is used to configure those credentials. 118` 119