1package awsauth
2
3import (
4	"context"
5	"time"
6
7	"github.com/hashicorp/vault/logical"
8	"github.com/hashicorp/vault/logical/framework"
9)
10
11func pathIdentityWhitelist(b *backend) *framework.Path {
12	return &framework.Path{
13		Pattern: "identity-whitelist/" + framework.GenericNameRegex("instance_id"),
14		Fields: map[string]*framework.FieldSchema{
15			"instance_id": &framework.FieldSchema{
16				Type: framework.TypeString,
17				Description: `EC2 instance ID. A successful login operation from an EC2 instance
18gets cached in this whitelist, keyed off of instance ID.`,
19			},
20		},
21
22		Callbacks: map[logical.Operation]framework.OperationFunc{
23			logical.ReadOperation:   b.pathIdentityWhitelistRead,
24			logical.DeleteOperation: b.pathIdentityWhitelistDelete,
25		},
26
27		HelpSynopsis:    pathIdentityWhitelistSyn,
28		HelpDescription: pathIdentityWhitelistDesc,
29	}
30}
31
32func pathListIdentityWhitelist(b *backend) *framework.Path {
33	return &framework.Path{
34		Pattern: "identity-whitelist/?",
35
36		Callbacks: map[logical.Operation]framework.OperationFunc{
37			logical.ListOperation: b.pathWhitelistIdentitiesList,
38		},
39
40		HelpSynopsis:    pathListIdentityWhitelistHelpSyn,
41		HelpDescription: pathListIdentityWhitelistHelpDesc,
42	}
43}
44
45// pathWhitelistIdentitiesList is used to list all the instance IDs that are present
46// in the identity whitelist. This will list both valid and expired entries.
47func (b *backend) pathWhitelistIdentitiesList(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
48	identities, err := req.Storage.List(ctx, "whitelist/identity/")
49	if err != nil {
50		return nil, err
51	}
52	return logical.ListResponse(identities), nil
53}
54
55// Fetch an item from the whitelist given an instance ID.
56func whitelistIdentityEntry(ctx context.Context, s logical.Storage, instanceID string) (*whitelistIdentity, error) {
57	entry, err := s.Get(ctx, "whitelist/identity/"+instanceID)
58	if err != nil {
59		return nil, err
60	}
61	if entry == nil {
62		return nil, nil
63	}
64
65	var result whitelistIdentity
66	if err := entry.DecodeJSON(&result); err != nil {
67		return nil, err
68	}
69	return &result, nil
70}
71
72// Stores an instance ID and the information required to validate further login/renewal attempts from
73// the same instance ID.
74func setWhitelistIdentityEntry(ctx context.Context, s logical.Storage, instanceID string, identity *whitelistIdentity) error {
75	entry, err := logical.StorageEntryJSON("whitelist/identity/"+instanceID, identity)
76	if err != nil {
77		return err
78	}
79
80	if err := s.Put(ctx, entry); err != nil {
81		return err
82	}
83	return nil
84}
85
86// pathIdentityWhitelistDelete is used to delete an entry from the identity whitelist given an instance ID.
87func (b *backend) pathIdentityWhitelistDelete(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
88	instanceID := data.Get("instance_id").(string)
89	if instanceID == "" {
90		return logical.ErrorResponse("missing instance_id"), nil
91	}
92
93	return nil, req.Storage.Delete(ctx, "whitelist/identity/"+instanceID)
94}
95
96// pathIdentityWhitelistRead is used to view an entry in the identity whitelist given an instance ID.
97func (b *backend) pathIdentityWhitelistRead(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
98	instanceID := data.Get("instance_id").(string)
99	if instanceID == "" {
100		return logical.ErrorResponse("missing instance_id"), nil
101	}
102
103	entry, err := whitelistIdentityEntry(ctx, req.Storage, instanceID)
104	if err != nil {
105		return nil, err
106	}
107	if entry == nil {
108		return nil, nil
109	}
110
111	return &logical.Response{
112		Data: map[string]interface{}{
113			"role":                      entry.Role,
114			"client_nonce":              entry.ClientNonce,
115			"creation_time":             entry.CreationTime.Format(time.RFC3339Nano),
116			"disallow_reauthentication": entry.DisallowReauthentication,
117			"pending_time":              entry.PendingTime,
118			"expiration_time":           entry.ExpirationTime.Format(time.RFC3339Nano),
119			"last_updated_time":         entry.LastUpdatedTime.Format(time.RFC3339Nano),
120		},
121	}, nil
122}
123
124// Struct to represent each item in the identity whitelist.
125type whitelistIdentity struct {
126	Role                     string    `json:"role"`
127	ClientNonce              string    `json:"client_nonce"`
128	CreationTime             time.Time `json:"creation_time"`
129	DisallowReauthentication bool      `json:"disallow_reauthentication"`
130	PendingTime              string    `json:"pending_time"`
131	ExpirationTime           time.Time `json:"expiration_time"`
132	LastUpdatedTime          time.Time `json:"last_updated_time"`
133}
134
135const pathIdentityWhitelistSyn = `
136Read or delete entries in the identity whitelist.
137`
138
139const pathIdentityWhitelistDesc = `
140Each login from an EC2 instance creates/updates an entry in the identity whitelist.
141
142Entries in this list can be viewed or deleted using this endpoint.
143
144By default, a cron task will periodically look for expired entries in the whitelist
145and deletes them. The duration to periodically run this, is one hour by default.
146However, this can be configured using the 'config/tidy/identities' endpoint. This tidy
147action can be triggered via the API as well, using the 'tidy/identities' endpoint.
148`
149
150const pathListIdentityWhitelistHelpSyn = `
151Lists the items present in the identity whitelist.
152`
153
154const pathListIdentityWhitelistHelpDesc = `
155The entries in the identity whitelist is keyed off of the EC2 instance IDs.
156This endpoint lists all the entries present in the identity whitelist, both
157expired and un-expired entries. Use 'tidy/identities' endpoint to clean-up
158the whitelist of identities.
159`
160