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