1package mssql 2 3import ( 4 "context" 5 "fmt" 6 "strings" 7 8 "github.com/hashicorp/vault/sdk/framework" 9 "github.com/hashicorp/vault/sdk/helper/strutil" 10 "github.com/hashicorp/vault/sdk/logical" 11) 12 13func pathListRoles(b *backend) *framework.Path { 14 return &framework.Path{ 15 Pattern: "roles/?$", 16 17 Callbacks: map[logical.Operation]framework.OperationFunc{ 18 logical.ListOperation: b.pathRoleList, 19 }, 20 21 HelpSynopsis: pathRoleHelpSyn, 22 HelpDescription: pathRoleHelpDesc, 23 } 24} 25 26func pathRoles(b *backend) *framework.Path { 27 return &framework.Path{ 28 Pattern: "roles/" + framework.GenericNameRegex("name"), 29 Fields: map[string]*framework.FieldSchema{ 30 "name": &framework.FieldSchema{ 31 Type: framework.TypeString, 32 Description: "Name of the role.", 33 }, 34 35 "sql": &framework.FieldSchema{ 36 Type: framework.TypeString, 37 Description: "SQL string to create a role. See help for more info.", 38 }, 39 }, 40 41 Callbacks: map[logical.Operation]framework.OperationFunc{ 42 logical.ReadOperation: b.pathRoleRead, 43 logical.UpdateOperation: b.pathRoleCreate, 44 logical.DeleteOperation: b.pathRoleDelete, 45 }, 46 47 HelpSynopsis: pathRoleHelpSyn, 48 HelpDescription: pathRoleHelpDesc, 49 } 50} 51 52func (b *backend) Role(ctx context.Context, s logical.Storage, n string) (*roleEntry, error) { 53 entry, err := s.Get(ctx, "role/"+n) 54 if err != nil { 55 return nil, err 56 } 57 if entry == nil { 58 return nil, nil 59 } 60 61 var result roleEntry 62 if err := entry.DecodeJSON(&result); err != nil { 63 return nil, err 64 } 65 66 return &result, nil 67} 68 69func (b *backend) pathRoleDelete(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 70 err := req.Storage.Delete(ctx, "role/"+data.Get("name").(string)) 71 if err != nil { 72 return nil, err 73 } 74 75 return nil, nil 76} 77 78func (b *backend) pathRoleRead(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 79 role, err := b.Role(ctx, req.Storage, data.Get("name").(string)) 80 if err != nil { 81 return nil, err 82 } 83 if role == nil { 84 return nil, nil 85 } 86 87 return &logical.Response{ 88 Data: map[string]interface{}{ 89 "sql": role.SQL, 90 }, 91 }, nil 92} 93 94func (b *backend) pathRoleList(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { 95 entries, err := req.Storage.List(ctx, "role/") 96 if err != nil { 97 return nil, err 98 } 99 100 return logical.ListResponse(entries), nil 101} 102 103func (b *backend) pathRoleCreate(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 104 name := data.Get("name").(string) 105 sql := data.Get("sql").(string) 106 107 // Get our connection 108 db, err := b.DB(ctx, req.Storage) 109 if err != nil { 110 return nil, err 111 } 112 113 // Test the query by trying to prepare it 114 for _, query := range strutil.ParseArbitraryStringSlice(sql, ";") { 115 query = strings.TrimSpace(query) 116 if len(query) == 0 { 117 continue 118 } 119 120 stmt, err := db.Prepare(Query(query, map[string]string{ 121 "name": "foo", 122 "password": "bar", 123 })) 124 if err != nil { 125 return logical.ErrorResponse(fmt.Sprintf( 126 "Error testing query: %s", err)), nil 127 } 128 stmt.Close() 129 } 130 131 // Store it 132 entry, err := logical.StorageEntryJSON("role/"+name, &roleEntry{ 133 SQL: sql, 134 }) 135 if err != nil { 136 return nil, err 137 } 138 if err := req.Storage.Put(ctx, entry); err != nil { 139 return nil, err 140 } 141 return nil, nil 142} 143 144type roleEntry struct { 145 SQL string `json:"sql"` 146} 147 148const pathRoleHelpSyn = ` 149Manage the roles that can be created with this backend. 150` 151 152const pathRoleHelpDesc = ` 153This path lets you manage the roles that can be created with this backend. 154 155The "sql" parameter customizes the SQL string used to create the login to 156the server. The parameter can be a sequence of SQL queries, each semi-colon 157separated. Some substitution will be done to the SQL string for certain keys. 158The names of the variables must be surrounded by "{{" and "}}" to be replaced. 159 160 * "name" - The random username generated for the DB user. 161 162 * "password" - The random password generated for the DB user. 163 164Example SQL query to use: 165 166 CREATE LOGIN [{{name}}] WITH PASSWORD = '{{password}}'; 167 CREATE USER [{{name}}] FROM LOGIN [{{name}}]; 168 GRANT SELECT, UPDATE, DELETE, INSERT on SCHEMA::dbo TO [{{name}}]; 169 170Please see the Microsoft SQL Server manual on the GRANT command to learn how to 171do more fine grained access. 172` 173