1package rabbitmq 2 3import ( 4 "context" 5 "fmt" 6 7 "github.com/hashicorp/errwrap" 8 multierror "github.com/hashicorp/go-multierror" 9 uuid "github.com/hashicorp/go-uuid" 10 "github.com/hashicorp/vault/logical" 11 "github.com/hashicorp/vault/logical/framework" 12 rabbithole "github.com/michaelklishin/rabbit-hole" 13) 14 15func pathCreds(b *backend) *framework.Path { 16 return &framework.Path{ 17 Pattern: "creds/" + framework.GenericNameRegex("name"), 18 Fields: map[string]*framework.FieldSchema{ 19 "name": &framework.FieldSchema{ 20 Type: framework.TypeString, 21 Description: "Name of the role.", 22 }, 23 }, 24 25 Callbacks: map[logical.Operation]framework.OperationFunc{ 26 logical.ReadOperation: b.pathCredsRead, 27 }, 28 29 HelpSynopsis: pathRoleCreateReadHelpSyn, 30 HelpDescription: pathRoleCreateReadHelpDesc, 31 } 32} 33 34// Issues the credential based on the role name 35func (b *backend) pathCredsRead(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { 36 name := d.Get("name").(string) 37 if name == "" { 38 return logical.ErrorResponse("missing name"), nil 39 } 40 41 // Get the role 42 role, err := b.Role(ctx, req.Storage, name) 43 if err != nil { 44 return nil, err 45 } 46 if role == nil { 47 return logical.ErrorResponse(fmt.Sprintf("unknown role: %s", name)), nil 48 } 49 50 // Ensure username is unique 51 uuidVal, err := uuid.GenerateUUID() 52 if err != nil { 53 return nil, err 54 } 55 username := fmt.Sprintf("%s-%s", req.DisplayName, uuidVal) 56 57 password, err := uuid.GenerateUUID() 58 if err != nil { 59 return nil, err 60 } 61 62 // Get the client configuration 63 client, err := b.Client(ctx, req.Storage) 64 if err != nil { 65 return nil, err 66 } 67 if client == nil { 68 return logical.ErrorResponse("failed to get the client"), nil 69 } 70 71 // Register the generated credentials in the backend, with the RabbitMQ server 72 if _, err = client.PutUser(username, rabbithole.UserSettings{ 73 Password: password, 74 Tags: role.Tags, 75 }); err != nil { 76 return nil, fmt.Errorf("failed to create a new user with the generated credentials") 77 } 78 79 // If the role had vhost permissions specified, assign those permissions 80 // to the created username for respective vhosts. 81 for vhost, permission := range role.VHosts { 82 if _, err := client.UpdatePermissionsIn(vhost, username, rabbithole.Permissions{ 83 Configure: permission.Configure, 84 Write: permission.Write, 85 Read: permission.Read, 86 }); err != nil { 87 outerErr := errwrap.Wrapf(fmt.Sprintf("failed to update permissions to the %q user: {{err}}", username), err) 88 // Delete the user because it's in an unknown state 89 if _, rmErr := client.DeleteUser(username); rmErr != nil { 90 return nil, multierror.Append(errwrap.Wrapf("failed to delete user: {{err}}", rmErr), outerErr) 91 } 92 return nil, outerErr 93 } 94 } 95 96 // Return the secret 97 resp := b.Secret(SecretCredsType).Response(map[string]interface{}{ 98 "username": username, 99 "password": password, 100 }, map[string]interface{}{ 101 "username": username, 102 }) 103 104 // Determine if we have a lease 105 lease, err := b.Lease(ctx, req.Storage) 106 if err != nil { 107 return nil, err 108 } 109 110 if lease != nil { 111 resp.Secret.TTL = lease.TTL 112 resp.Secret.MaxTTL = lease.MaxTTL 113 } 114 115 return resp, nil 116} 117 118const pathRoleCreateReadHelpSyn = ` 119Request RabbitMQ credentials for a certain role. 120` 121 122const pathRoleCreateReadHelpDesc = ` 123This path reads RabbitMQ credentials for a certain role. The 124RabbitMQ credentials will be generated on demand and will be automatically 125revoked when the lease is up. 126` 127