2016-12-19 18:15:58 +00:00
package database
import (
2017-12-14 22:03:11 +00:00
"context"
2016-12-19 18:15:58 +00:00
"fmt"
2018-04-03 16:20:20 +00:00
"time"
2016-12-19 18:15:58 +00:00
2020-10-15 19:20:12 +00:00
v4 "github.com/hashicorp/vault/sdk/database/dbplugin"
v5 "github.com/hashicorp/vault/sdk/database/dbplugin/v5"
2019-04-12 21:54:35 +00:00
"github.com/hashicorp/vault/sdk/framework"
2019-04-13 07:44:06 +00:00
"github.com/hashicorp/vault/sdk/logical"
2016-12-19 18:15:58 +00:00
)
const SecretCredsType = "creds"
func secretCreds ( b * databaseBackend ) * framework . Secret {
return & framework . Secret {
2017-03-08 22:46:53 +00:00
Type : SecretCredsType ,
Fields : map [ string ] * framework . FieldSchema { } ,
2016-12-19 18:15:58 +00:00
2017-04-11 18:50:34 +00:00
Renew : b . secretCredsRenew ( ) ,
Revoke : b . secretCredsRevoke ( ) ,
2016-12-19 18:15:58 +00:00
}
}
2017-04-11 18:50:34 +00:00
func ( b * databaseBackend ) secretCredsRenew ( ) framework . OperationFunc {
2018-01-08 18:31:38 +00:00
return func ( ctx context . Context , req * logical . Request , data * framework . FieldData ) ( * logical . Response , error ) {
2017-04-11 18:50:34 +00:00
// Get the username from the internal data
usernameRaw , ok := req . Secret . InternalData [ "username" ]
if ! ok {
return nil , fmt . Errorf ( "secret is missing username internal data" )
}
username , ok := usernameRaw . ( string )
2016-12-19 18:15:58 +00:00
2017-04-11 18:50:34 +00:00
roleNameRaw , ok := req . Secret . InternalData [ "role" ]
if ! ok {
2018-04-05 15:49:21 +00:00
return nil , fmt . Errorf ( "could not find role with name: %q" , req . Secret . InternalData [ "role" ] )
2017-04-11 18:50:34 +00:00
}
2016-12-19 18:15:58 +00:00
2018-01-19 06:44:44 +00:00
role , err := b . Role ( ctx , req . Storage , roleNameRaw . ( string ) )
2017-04-11 18:50:34 +00:00
if err != nil {
return nil , err
}
if role == nil {
2018-04-05 15:49:21 +00:00
return nil , fmt . Errorf ( "error during renew: could not find role with name %q" , req . Secret . InternalData [ "role" ] )
2017-04-11 18:50:34 +00:00
}
2016-12-19 18:15:58 +00:00
2017-04-26 22:23:14 +00:00
// Get the Database object
2020-09-18 21:10:54 +00:00
dbi , err := b . GetConnection ( ctx , req . Storage , role . DBName )
2018-03-21 19:05:56 +00:00
if err != nil {
return nil , err
2016-12-19 18:15:58 +00:00
}
2020-09-18 21:10:54 +00:00
dbi . RLock ( )
defer dbi . RUnlock ( )
2018-03-21 19:05:56 +00:00
2017-04-11 18:50:34 +00:00
// Make sure we increase the VALID UNTIL endpoint for this user.
2018-04-03 16:20:20 +00:00
ttl , _ , err := framework . CalculateTTL ( b . System ( ) , req . Secret . Increment , role . DefaultTTL , 0 , role . MaxTTL , 0 , req . Secret . IssueTime )
if err != nil {
return nil , err
}
if ttl > 0 {
expireTime := time . Now ( ) . Add ( ttl )
// Adding a small buffer since the TTL will be calculated again after this call
// to ensure the database credential does not expire before the lease
expireTime = expireTime . Add ( 5 * time . Second )
2020-09-18 21:10:54 +00:00
2020-10-15 19:20:12 +00:00
updateReq := v5 . UpdateUserRequest {
2020-09-18 21:10:54 +00:00
Username : username ,
2020-10-15 19:20:12 +00:00
Expiration : & v5 . ChangeExpiration {
2020-09-18 21:10:54 +00:00
NewExpiration : expireTime ,
2020-10-15 19:20:12 +00:00
Statements : v5 . Statements {
2020-09-18 21:10:54 +00:00
Commands : role . Statements . Renewal ,
} ,
} ,
}
_ , err := dbi . database . UpdateUser ( ctx , updateReq , false )
2017-04-11 18:50:34 +00:00
if err != nil {
2020-09-18 21:10:54 +00:00
b . CloseIfShutdown ( dbi , err )
2017-04-11 18:50:34 +00:00
return nil , err
}
}
2018-04-03 16:20:20 +00:00
resp := & logical . Response { Secret : req . Secret }
resp . Secret . TTL = role . DefaultTTL
resp . Secret . MaxTTL = role . MaxTTL
2017-04-11 18:50:34 +00:00
return resp , nil
2016-12-19 18:15:58 +00:00
}
2017-04-11 18:50:34 +00:00
}
2016-12-19 18:15:58 +00:00
2017-04-11 18:50:34 +00:00
func ( b * databaseBackend ) secretCredsRevoke ( ) framework . OperationFunc {
2018-01-08 18:31:38 +00:00
return func ( ctx context . Context , req * logical . Request , data * framework . FieldData ) ( * logical . Response , error ) {
2017-04-11 18:50:34 +00:00
// Get the username from the internal data
usernameRaw , ok := req . Secret . InternalData [ "username" ]
if ! ok {
return nil , fmt . Errorf ( "secret is missing username internal data" )
}
username , ok := usernameRaw . ( string )
2016-12-19 18:15:58 +00:00
2017-04-11 18:50:34 +00:00
var resp * logical . Response
2016-12-19 18:15:58 +00:00
2017-04-11 18:50:34 +00:00
roleNameRaw , ok := req . Secret . InternalData [ "role" ]
if ! ok {
return nil , fmt . Errorf ( "no role name was provided" )
}
2016-12-19 18:15:58 +00:00
2018-06-19 15:24:28 +00:00
var dbName string
2020-10-15 19:20:12 +00:00
var statements v4 . Statements
2018-06-19 15:24:28 +00:00
2018-01-19 06:44:44 +00:00
role , err := b . Role ( ctx , req . Storage , roleNameRaw . ( string ) )
2016-12-19 18:15:58 +00:00
if err != nil {
return nil , err
}
2018-06-19 15:24:28 +00:00
if role != nil {
dbName = role . DBName
statements = role . Statements
} else {
2020-09-18 21:10:54 +00:00
dbNameRaw , ok := req . Secret . InternalData [ "db_name" ]
if ! ok {
2018-06-19 15:24:28 +00:00
return nil , fmt . Errorf ( "error during revoke: could not find role with name %q or embedded revocation db name data" , req . Secret . InternalData [ "role" ] )
}
2020-09-18 21:10:54 +00:00
dbName = dbNameRaw . ( string )
statementsRaw , ok := req . Secret . InternalData [ "revocation_statements" ]
if ! ok {
2018-06-19 15:24:28 +00:00
return nil , fmt . Errorf ( "error during revoke: could not find role with name %q or embedded revocation statement data" , req . Secret . InternalData [ "role" ] )
2020-09-18 21:10:54 +00:00
}
// If we don't actually have any statements, because none were
// set in the role, we'll end up with an empty one and the
// default for the db type will be attempted
if statementsRaw != nil {
statementsSlice , ok := statementsRaw . ( [ ] interface { } )
if ! ok {
return nil , fmt . Errorf ( "error during revoke: could not find role with name %q and embedded reovcation data could not be read" , req . Secret . InternalData [ "role" ] )
}
for _ , v := range statementsSlice {
statements . Revocation = append ( statements . Revocation , v . ( string ) )
2018-06-28 16:42:04 +00:00
}
2018-06-19 15:24:28 +00:00
}
2016-12-19 18:15:58 +00:00
}
2017-04-11 18:50:34 +00:00
// Get our connection
2020-09-18 21:10:54 +00:00
dbi , err := b . GetConnection ( ctx , req . Storage , dbName )
2018-03-21 19:05:56 +00:00
if err != nil {
return nil , err
2017-04-11 18:50:34 +00:00
}
2016-12-19 18:15:58 +00:00
2020-09-18 21:10:54 +00:00
dbi . RLock ( )
defer dbi . RUnlock ( )
2018-03-21 19:05:56 +00:00
2020-10-15 19:20:12 +00:00
deleteReq := v5 . DeleteUserRequest {
2020-09-18 21:10:54 +00:00
Username : username ,
2020-10-15 19:20:12 +00:00
Statements : v5 . Statements {
2020-09-18 21:10:54 +00:00
Commands : statements . Revocation ,
} ,
}
_ , err = dbi . database . DeleteUser ( ctx , deleteReq )
if err != nil {
b . CloseIfShutdown ( dbi , err )
2017-04-11 18:50:34 +00:00
return nil , err
}
return resp , nil
}
2016-12-19 18:15:58 +00:00
}