1package radius 2 3import ( 4 "context" 5 "fmt" 6 "strings" 7 8 "github.com/hashicorp/vault/sdk/framework" 9 "github.com/hashicorp/vault/sdk/helper/policyutil" 10 "github.com/hashicorp/vault/sdk/logical" 11) 12 13func pathUsersList(b *backend) *framework.Path { 14 return &framework.Path{ 15 Pattern: "users/?$", 16 17 Callbacks: map[logical.Operation]framework.OperationFunc{ 18 logical.ListOperation: b.pathUserList, 19 }, 20 21 HelpSynopsis: pathUserHelpSyn, 22 HelpDescription: pathUserHelpDesc, 23 } 24} 25 26func pathUsers(b *backend) *framework.Path { 27 return &framework.Path{ 28 Pattern: `users/(?P<name>.+)`, 29 Fields: map[string]*framework.FieldSchema{ 30 "name": &framework.FieldSchema{ 31 Type: framework.TypeString, 32 Description: "Name of the RADIUS user.", 33 }, 34 35 "policies": &framework.FieldSchema{ 36 Type: framework.TypeCommaStringSlice, 37 Description: "Comma-separated list of policies associated to the user.", 38 }, 39 }, 40 41 Callbacks: map[logical.Operation]framework.OperationFunc{ 42 logical.DeleteOperation: b.pathUserDelete, 43 logical.ReadOperation: b.pathUserRead, 44 logical.UpdateOperation: b.pathUserWrite, 45 logical.CreateOperation: b.pathUserWrite, 46 }, 47 48 ExistenceCheck: b.userExistenceCheck, 49 50 HelpSynopsis: pathUserHelpSyn, 51 HelpDescription: pathUserHelpDesc, 52 } 53} 54 55func (b *backend) userExistenceCheck(ctx context.Context, req *logical.Request, data *framework.FieldData) (bool, error) { 56 userEntry, err := b.user(ctx, req.Storage, data.Get("name").(string)) 57 if err != nil { 58 return false, err 59 } 60 61 return userEntry != nil, nil 62} 63 64func (b *backend) user(ctx context.Context, s logical.Storage, username string) (*UserEntry, error) { 65 if username == "" { 66 return nil, fmt.Errorf("missing username") 67 } 68 69 entry, err := s.Get(ctx, "user/"+strings.ToLower(username)) 70 if err != nil { 71 return nil, err 72 } 73 if entry == nil { 74 return nil, nil 75 } 76 77 var result UserEntry 78 if err := entry.DecodeJSON(&result); err != nil { 79 return nil, err 80 } 81 82 return &result, nil 83} 84 85func (b *backend) pathUserDelete(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { 86 err := req.Storage.Delete(ctx, "user/"+d.Get("name").(string)) 87 if err != nil { 88 return nil, err 89 } 90 91 return nil, nil 92} 93 94func (b *backend) pathUserRead(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { 95 user, err := b.user(ctx, req.Storage, d.Get("name").(string)) 96 if err != nil { 97 return nil, err 98 } 99 if user == nil { 100 return nil, nil 101 } 102 103 return &logical.Response{ 104 Data: map[string]interface{}{ 105 "policies": user.Policies, 106 }, 107 }, nil 108} 109 110func (b *backend) pathUserWrite(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { 111 112 var policies = policyutil.ParsePolicies(d.Get("policies")) 113 for _, policy := range policies { 114 if policy == "root" { 115 return logical.ErrorResponse("root policy cannot be granted by an auth method"), nil 116 } 117 } 118 119 // Store it 120 entry, err := logical.StorageEntryJSON("user/"+d.Get("name").(string), &UserEntry{ 121 Policies: policies, 122 }) 123 if err != nil { 124 return nil, err 125 } 126 if err := req.Storage.Put(ctx, entry); err != nil { 127 return nil, err 128 } 129 130 return nil, nil 131} 132 133func (b *backend) pathUserList(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { 134 users, err := req.Storage.List(ctx, "user/") 135 if err != nil { 136 return nil, err 137 } 138 return logical.ListResponse(users), nil 139} 140 141type UserEntry struct { 142 Policies []string 143} 144 145const pathUserHelpSyn = ` 146Manage users allowed to authenticate. 147` 148 149const pathUserHelpDesc = ` 150This endpoint allows you to create, read, update, and delete configuration 151for RADIUS users that are allowed to authenticate, and associate policies to 152them. 153 154Deleting a user will not revoke auth for prior authenticated users. 155To do this, do a revoke token by path on "auth/radius/login/<username>" 156for the usernames you want revoked. 157` 158