2020-07-16 22:12:26 -04:00
package auth
import (
"context"
"errors"
"fmt"
2020-07-22 00:14:28 -04:00
"net/http"
2020-07-16 22:12:26 -04:00
"strings"
2020-08-20 13:34:03 -04:00
"time"
2020-07-16 22:12:26 -04:00
2020-08-24 13:22:10 -04:00
"github.com/hashicorp/boundary/globals"
2020-08-10 17:15:02 -04:00
"github.com/hashicorp/boundary/internal/gen/controller/api/resources/scopes"
2020-09-14 12:30:11 -04:00
"github.com/hashicorp/boundary/internal/gen/controller/tokens"
2020-08-20 13:34:03 -04:00
"github.com/hashicorp/boundary/internal/kms"
2020-08-10 17:15:02 -04:00
"github.com/hashicorp/boundary/internal/perms"
"github.com/hashicorp/boundary/internal/servers/controller/common"
2020-08-16 00:42:36 -04:00
"github.com/hashicorp/boundary/internal/servers/controller/handlers"
2020-08-10 17:15:02 -04:00
"github.com/hashicorp/boundary/internal/types/action"
2020-09-16 11:15:42 -04:00
"github.com/hashicorp/boundary/internal/types/resource"
2020-08-10 17:15:02 -04:00
"github.com/hashicorp/boundary/internal/types/scope"
2020-09-09 01:24:25 -04:00
"github.com/hashicorp/boundary/sdk/recovery"
2020-07-16 22:12:26 -04:00
"github.com/hashicorp/go-hclog"
2020-08-25 00:20:50 -04:00
wrapping "github.com/hashicorp/go-kms-wrapping"
2020-07-16 22:12:26 -04:00
"github.com/kr/pretty"
2020-09-26 14:01:49 -04:00
"github.com/mr-tron/base58"
2020-08-25 00:20:50 -04:00
"google.golang.org/protobuf/proto"
2020-07-16 22:12:26 -04:00
)
2020-07-22 00:14:28 -04:00
type TokenFormat int
const (
2020-09-08 23:57:26 -04:00
// We weren't given one or couldn't parse it
2020-07-22 00:14:28 -04:00
AuthTokenTypeUnknown TokenFormat = iota
2020-08-25 00:20:50 -04:00
// Came in via the Authentication: Bearer header
2020-07-22 00:14:28 -04:00
AuthTokenTypeBearer
2020-08-25 00:20:50 -04:00
// Came in via split cookies
2020-07-22 00:14:28 -04:00
AuthTokenTypeSplitCookie
2020-08-25 00:20:50 -04:00
// It's of recovery type
AuthTokenTypeRecoveryKms
2020-07-22 00:14:28 -04:00
)
2020-07-16 22:12:26 -04:00
type key int
2020-08-24 13:22:10 -04:00
var verifierKey key
2020-07-16 22:12:26 -04:00
// RequestInfo contains request parameters necessary for checking authn/authz
type RequestInfo struct {
2020-09-14 12:30:11 -04:00
Path string
Method string
PublicId string
EncryptedToken string
Token string
TokenFormat TokenFormat
2020-07-22 15:10:44 -04:00
2020-07-22 00:14:28 -04:00
// The following are useful for tests
2020-09-14 12:30:11 -04:00
scopeIdOverride string
userIdOverride string
2020-07-22 15:10:44 -04:00
DisableAuthzFailures bool
DisableAuthEntirely bool
2020-07-22 00:14:28 -04:00
}
type VerifyResults struct {
2020-09-19 00:18:17 -04:00
UserId string
AuthTokenId string
Error error
Scope * scopes . ScopeInfo
2020-09-23 23:39:44 -04:00
2020-10-22 08:40:00 -04:00
// RoundTripValue can be set to allow the function performing authentication
// (often accompanied by lookup(s)) to return a result of that lookup to the
// calling function. It is opaque to this package.
RoundTripValue interface {}
2020-09-23 23:39:44 -04:00
// Used for additional verification
v * verifier
2020-07-16 22:12:26 -04:00
}
type verifier struct {
logger hclog . Logger
iamRepoFn common . IamRepoFactory
authTokenRepoFn common . AuthTokenRepoFactory
2020-08-24 13:22:10 -04:00
serversRepoFn common . ServersRepoFactory
2020-08-20 13:34:03 -04:00
kms * kms . Kms
2020-07-16 22:12:26 -04:00
requestInfo RequestInfo
res * perms . Resource
act action . Type
ctx context . Context
2020-09-23 23:39:44 -04:00
acl perms . ACL
2020-07-16 22:12:26 -04:00
}
// NewVerifierContext creates a context that carries a verifier object from the
// HTTP handlers to the gRPC service handlers. It should only be created in the
// HTTP handler and should exist for every request that reaches the service
// handlers.
func NewVerifierContext ( ctx context . Context ,
logger hclog . Logger ,
iamRepoFn common . IamRepoFactory ,
authTokenRepoFn common . AuthTokenRepoFactory ,
2020-08-24 13:22:10 -04:00
serversRepoFn common . ServersRepoFactory ,
2020-08-20 13:34:03 -04:00
kms * kms . Kms ,
2020-07-16 22:12:26 -04:00
requestInfo RequestInfo ) context . Context {
return context . WithValue ( ctx , verifierKey , & verifier {
logger : logger ,
iamRepoFn : iamRepoFn ,
authTokenRepoFn : authTokenRepoFn ,
2020-08-24 13:22:10 -04:00
serversRepoFn : serversRepoFn ,
2020-08-20 13:34:03 -04:00
kms : kms ,
2020-07-16 22:12:26 -04:00
requestInfo : requestInfo ,
})
}
// Verify takes in a context that has expected parameters as values and runs an
// authn/authz check. It returns a user ID, the scope ID for the request (which
// may come from the URL and may come from the token) and whether or not to
// proceed, e.g. whether the authn/authz check resulted in failure. If an error
// occurs it's logged to the system log.
2020-07-22 15:10:44 -04:00
func Verify ( ctx context . Context , opt ... Option ) ( ret VerifyResults ) {
2020-08-16 00:42:36 -04:00
ret . Error = handlers . ForbiddenError ()
2020-07-16 22:12:26 -04:00
v , ok := ctx . Value ( verifierKey ).( * verifier )
if ! ok {
// We don't have a logger yet and this should never happen in any
// context we won't catch in tests
panic ( "no verifier information found in context" )
}
2020-09-23 23:39:44 -04:00
ret . v = v
2020-09-01 12:31:54 -04:00
v . ctx = ctx
2020-08-13 17:39:48 -04:00
opts := getOpts ( opt ... )
2020-09-01 12:31:54 -04:00
2020-07-22 00:14:28 -04:00
ret . Scope = new ( scopes . ScopeInfo )
if v . requestInfo . DisableAuthEntirely {
2020-09-14 12:30:11 -04:00
ret . Scope . Id = v . requestInfo . scopeIdOverride
2020-09-01 12:31:54 -04:00
if ret . Scope . Id == "" {
ret . Scope . Id = opts . withScopeId
}
2020-07-22 00:14:28 -04:00
switch {
case ret . Scope . Id == "global" :
ret . Scope . Type = "global"
case strings . HasPrefix ( ret . Scope . Id , scope . Org . Prefix ()):
ret . Scope . Type = scope . Org . String ()
case strings . HasPrefix ( ret . Scope . Id , scope . Project . Prefix ()):
ret . Scope . Type = scope . Project . String ()
}
2020-08-13 17:39:48 -04:00
ret . UserId = v . requestInfo . userIdOverride
2020-08-16 00:42:36 -04:00
ret . Error = nil
2020-07-22 00:14:28 -04:00
return
}
2020-08-20 13:34:03 -04:00
2020-09-08 13:27:51 -07:00
v . act = opts . withAction
v . res = & perms . Resource {
ScopeId : opts . withScopeId ,
Id : opts . withId ,
Pin : opts . withPin ,
Type : opts . withType ,
2020-07-16 22:12:26 -04:00
}
2020-09-16 11:15:42 -04:00
// Global scope has no parent ID; account for this
if opts . withId == scope . Global . String () && opts . withType == resource . Scope {
v . res . ScopeId = scope . Global . String ()
}
2020-07-16 22:12:26 -04:00
2020-09-14 12:30:11 -04:00
if v . requestInfo . EncryptedToken != "" {
v . decryptToken ()
}
2020-09-23 23:39:44 -04:00
var authResults perms . ACLResults
2020-07-22 00:14:28 -04:00
var err error
2020-09-23 23:39:44 -04:00
authResults , ret . UserId , ret . Scope , v . acl , err = v . performAuthCheck ()
2020-07-16 22:12:26 -04:00
if err != nil {
v . logger . Error ( "error performing authn/authz check" , "error" , err )
return
}
2020-09-23 23:39:44 -04:00
2020-09-19 00:18:17 -04:00
ret . AuthTokenId = v . requestInfo . PublicId
2020-07-16 22:12:26 -04:00
if ! authResults . Allowed {
if v . requestInfo . DisableAuthzFailures {
2020-08-16 00:42:36 -04:00
ret . Error = nil
// TODO: Decide whether to remove this
2020-07-22 00:14:28 -04:00
v . logger . Info ( "failed authz info for request" , "resource" , pretty . Sprint ( v . res ), "user_id" , ret . UserId , "action" , v . act . String ())
2020-07-16 22:12:26 -04:00
} else {
2020-09-08 23:57:26 -04:00
// If the anon user was used (either no token, or invalid (perhaps
// expired) token), return a 401. That way if it's an authn'd user
// that is not authz'd we'll return 403 to be explicit.
if ret . UserId == "u_anon" {
ret . Error = handlers . UnauthenticatedError ()
}
2020-07-16 22:12:26 -04:00
return
}
}
2020-08-16 00:42:36 -04:00
ret . Error = nil
2020-07-16 22:12:26 -04:00
return
}
2020-09-23 23:39:44 -04:00
// AdditionalVerification is used to perform checks of additional resources for
// actions that need to touch more than one.
func ( r * VerifyResults ) AdditionalVerification ( ctx context . Context , opt ... Option ) ( ret VerifyResults ) {
v := r . v
ret . Error = handlers . ForbiddenError ()
// Set other parameters the same to start with
ret . Scope = r . Scope
ret . UserId = r . UserId
ret . AuthTokenId = r . AuthTokenId
ret . v = r . v
opts := getOpts ( opt ... )
act := opts . withAction
res := perms . Resource {
ScopeId : opts . withScopeId ,
Id : opts . withId ,
Pin : opts . withPin ,
Type : opts . withType ,
}
// Global scope has no parent ID; account for this
if opts . withId == scope . Global . String () && opts . withType == resource . Scope {
res . ScopeId = scope . Global . String ()
}
// Only perform lookup if it's actually different, otherwise use cached info
if res . ScopeId != r . Scope . Id {
iamRepo , err := v . iamRepoFn ()
if err != nil {
v . logger . Error ( "additional verification: failed to get iam repo" , "error" , err )
return
}
// Look up scope details to return. We can skip a lookup when using the
// global scope
switch res . ScopeId {
case "global" :
ret . Scope = & scopes . ScopeInfo {
Id : scope . Global . String (),
Type : scope . Global . String (),
Name : scope . Global . String (),
Description : "Global Scope" ,
ParentScopeId : "" ,
}
default :
scp , err := iamRepo . LookupScope ( v . ctx , v . res . ScopeId )
if err != nil {
v . logger . Error ( "additional verification: failed to get look up scope" , "error" , err )
return
}
if scp == nil {
v . logger . Error ( "additional verification: non-existent scope" , "error" , err )
return
}
ret . Scope = & scopes . ScopeInfo {
Id : scp . GetPublicId (),
Type : scp . GetType (),
Name : scp . GetName (),
Description : scp . GetDescription (),
ParentScopeId : scp . GetParentId (),
}
}
}
// Always allowed
if v . requestInfo . TokenFormat == AuthTokenTypeRecoveryKms {
ret . Error = nil
return
}
aclResults := v . acl . Allowed ( res , act )
if ! aclResults . Allowed {
if v . requestInfo . DisableAuthzFailures {
ret . Error = nil
// TODO: Decide whether to remove this
v . logger . Info ( "failed authz info for request" , "resource" , pretty . Sprint ( v . res ), "user_id" , ret . UserId , "action" , v . act . String ())
} else {
// If the anon user was used (either no token, or invalid (perhaps
// expired) token), return a 401. That way if it's an authn'd user
// that is not authz'd we'll return 403 to be explicit.
if ret . UserId == "u_anon" {
ret . Error = handlers . UnauthenticatedError ()
}
return
}
}
ret . Error = nil
return
}
func ( v verifier ) performAuthCheck () ( aclResults perms . ACLResults , userId string , scopeInfo * scopes . ScopeInfo , retAcl perms . ACL , retErr error ) {
2020-07-16 22:12:26 -04:00
// Ensure we return an error by default if we forget to set this somewhere
retErr = errors . New ( "unknown" )
2020-07-22 00:14:28 -04:00
// Make the linter happy
_ = retErr
scopeInfo = new ( scopes . ScopeInfo )
2020-07-16 22:12:26 -04:00
userId = "u_anon"
2020-10-01 10:51:54 -04:00
var accountId string
2020-07-16 22:12:26 -04:00
// Validate the token and fetch the corresponding user ID
2020-08-20 13:34:03 -04:00
switch v . requestInfo . TokenFormat {
2020-09-14 12:30:11 -04:00
case AuthTokenTypeUnknown :
// Nothing; remain as the anonymous user
case AuthTokenTypeRecoveryKms :
// We validated the encrypted token in decryptToken and handled the
// nonces there, so just set the user
userId = "u_recovery"
2020-08-20 13:34:03 -04:00
case AuthTokenTypeBearer , AuthTokenTypeSplitCookie :
2020-08-26 23:28:38 -04:00
if v . requestInfo . Token == "" {
// This will end up staying as the anonymous user
break
}
2020-08-07 10:18:40 -07:00
tokenRepo , err := v . authTokenRepoFn ()
if err != nil {
retErr = fmt . Errorf ( "perform auth check: failed to get authtoken repo: %w" , err )
return
}
at , err := tokenRepo . ValidateToken ( v . ctx , v . requestInfo . PublicId , v . requestInfo . Token )
if err != nil {
2020-09-08 23:57:26 -04:00
// Continue as the anonymous user as maybe this token is expired but
// we can still perform the action
v . logger . Error ( "perform auth check: error validating token; continuing as anonymous user" , "error" , err )
break
2020-08-07 10:18:40 -07:00
}
if at != nil {
2020-10-01 10:51:54 -04:00
accountId = at . GetAuthAccountId ()
2020-08-07 10:18:40 -07:00
userId = at . GetIamUserId ()
2020-09-26 13:58:48 -04:00
if userId == "" {
v . logger . Warn ( "perform auth check: valid token did not map to a user, likely because no account is associated with the user any longer; continuing as u_anon" , "token_id" , at . GetPublicId ())
userId = "u_anon"
2020-10-12 10:15:38 -04:00
accountId = ""
2020-09-26 13:58:48 -04:00
}
2020-08-07 10:18:40 -07:00
}
2020-07-22 00:14:28 -04:00
}
2020-07-16 22:12:26 -04:00
iamRepo , err := v . iamRepoFn ()
if err != nil {
retErr = fmt . Errorf ( "perform auth check: failed to get iam repo: %w" , err )
return
}
2020-07-22 00:14:28 -04:00
2020-08-26 23:28:38 -04:00
// Look up scope details to return. We can skip a lookup when using the
// global scope
switch v . res . ScopeId {
case "global" :
scopeInfo = & scopes . ScopeInfo {
Id : scope . Global . String (),
Type : scope . Global . String (),
Name : scope . Global . String (),
Description : "Global Scope" ,
ParentScopeId : "" ,
}
default :
scp , err := iamRepo . LookupScope ( v . ctx , v . res . ScopeId )
if err != nil {
retErr = fmt . Errorf ( "perform auth check: failed to lookup scope: %w" , err )
return
}
if scp == nil {
retErr = fmt . Errorf ( "perform auth check: non-existent scope %q" , v . res . ScopeId )
return
}
scopeInfo = & scopes . ScopeInfo {
Id : scp . GetPublicId (),
Type : scp . GetType (),
Name : scp . GetName (),
Description : scp . GetDescription (),
ParentScopeId : scp . GetParentId (),
}
2020-07-22 00:14:28 -04:00
}
2020-08-20 13:34:03 -04:00
// At this point we don't need to look up grants since it's automatically allowed
2020-08-25 00:20:50 -04:00
if v . requestInfo . TokenFormat == AuthTokenTypeRecoveryKms {
2020-09-23 23:39:44 -04:00
aclResults . Allowed = true
2020-08-20 13:34:03 -04:00
retErr = nil
return
}
2020-07-22 00:14:28 -04:00
var parsedGrants [] perms . Grant
var grantPairs [] perms . GrantPair
2020-08-26 23:28:38 -04:00
// Fetch and parse grants for this user ID (which may include grants for
// u_anon and u_auth)
2020-07-16 22:12:26 -04:00
grantPairs , err = iamRepo . GrantsForUser ( v . ctx , userId )
if err != nil {
retErr = fmt . Errorf ( "perform auth check: failed to query for user grants: %w" , err )
return
}
parsedGrants = make ([] perms . Grant , 0 , len ( grantPairs ))
for _ , pair := range grantPairs {
2020-10-01 10:51:54 -04:00
parsed , err := perms . Parse (
pair . ScopeId ,
pair . Grant ,
perms . WithUserId ( userId ),
perms . WithAccountId ( accountId ),
perms . WithSkipFinalValidation ( true ))
2020-07-16 22:12:26 -04:00
if err != nil {
retErr = fmt . Errorf ( "perform auth check: failed to parse grant %#v: %w" , pair . Grant , err )
return
}
parsedGrants = append ( parsedGrants , parsed )
}
2020-09-23 23:39:44 -04:00
retAcl = perms . NewACL ( parsedGrants ... )
aclResults = retAcl . Allowed ( * v . res , v . act )
2020-07-16 22:12:26 -04:00
retErr = nil
return
}
2020-07-22 00:14:28 -04:00
2020-07-22 15:10:44 -04:00
// GetTokenFromRequest pulls the token from either the Authorization header or
2020-07-22 00:14:28 -04:00
// split cookies and parses it. If it cannot be parsed successfully, the issue
// is logged and we return blank, so logic will continue as the anonymous user.
2020-09-14 12:30:11 -04:00
// The public ID and _encrypted_ token are returned along with the token format.
2020-08-25 00:20:50 -04:00
func GetTokenFromRequest ( logger hclog . Logger , kmsCache * kms . Kms , req * http . Request ) ( string , string , TokenFormat ) {
2020-07-22 00:14:28 -04:00
// First, get the token, either from the authorization header or from split
// cookies
var receivedTokenType TokenFormat
var fullToken string
if authHeader := req . Header . Get ( "Authorization" ); authHeader != "" {
headerSplit := strings . SplitN ( strings . TrimSpace ( authHeader ), " " , 2 )
if len ( headerSplit ) == 2 && strings . EqualFold ( strings . TrimSpace ( headerSplit [ 0 ]), "bearer" ) {
receivedTokenType = AuthTokenTypeBearer
fullToken = strings . TrimSpace ( headerSplit [ 1 ])
}
}
if receivedTokenType != AuthTokenTypeBearer {
var httpCookiePayload string
var jsCookiePayload string
2020-09-01 09:23:41 -07:00
if hc , err := req . Cookie ( handlers . HttpOnlyCookieName ); err == nil {
2020-07-22 00:14:28 -04:00
httpCookiePayload = hc . Value
}
2020-09-01 09:23:41 -07:00
if jc , err := req . Cookie ( handlers . JsVisibleCookieName ); err == nil {
2020-07-22 00:14:28 -04:00
jsCookiePayload = jc . Value
}
if httpCookiePayload != "" && jsCookiePayload != "" {
receivedTokenType = AuthTokenTypeSplitCookie
fullToken = jsCookiePayload + httpCookiePayload
}
}
if receivedTokenType == AuthTokenTypeUnknown || fullToken == "" {
// We didn't find auth info or a client screwed up and put in a blank
// header instead of nothing at all, so return blank which will indicate
// the anonymouse user
return "" , "" , AuthTokenTypeUnknown
}
2020-08-20 13:34:03 -04:00
if strings . HasPrefix ( fullToken , "r_" ) {
2020-08-25 00:20:50 -04:00
return "" , fullToken , AuthTokenTypeRecoveryKms
2020-08-20 13:34:03 -04:00
}
2020-07-22 00:14:28 -04:00
splitFullToken := strings . Split ( fullToken , "_" )
if len ( splitFullToken ) != 3 {
2020-09-08 23:57:26 -04:00
logger . Trace ( "get token from request: unexpected number of segments in token; continuing as anonymous user" , "expected" , 3 , "found" , len ( splitFullToken ))
return "" , "" , AuthTokenTypeUnknown
2020-07-22 00:14:28 -04:00
}
publicId := strings . Join ( splitFullToken [ 0 : 2 ], "_" )
2020-09-14 12:30:11 -04:00
encryptedToken := splitFullToken [ 2 ]
2020-07-22 00:14:28 -04:00
2020-09-14 12:30:11 -04:00
return publicId , encryptedToken , receivedTokenType
}
2020-08-25 00:20:50 -04:00
2020-09-14 12:30:11 -04:00
func ( v * verifier ) decryptToken () {
switch v . requestInfo . TokenFormat {
case AuthTokenTypeUnknown :
// Nothing to decrypt
return
2020-08-25 00:20:50 -04:00
2020-09-14 12:30:11 -04:00
case AuthTokenTypeBearer , AuthTokenTypeSplitCookie :
if v . kms == nil {
v . logger . Trace ( "decrypt token: no KMS object available to authz system" )
v . requestInfo . TokenFormat = AuthTokenTypeUnknown
return
}
2020-08-25 00:20:50 -04:00
2020-09-14 12:30:11 -04:00
tokenRepo , err := v . authTokenRepoFn ()
if err != nil {
v . logger . Warn ( "decrypt bearer token: failed to get authtoken repo" , "error" , err )
v . requestInfo . TokenFormat = AuthTokenTypeUnknown
return
}
2020-08-25 00:20:50 -04:00
2020-09-14 12:30:11 -04:00
at , err := tokenRepo . LookupAuthToken ( v . ctx , v . requestInfo . PublicId )
if err != nil {
v . logger . Trace ( "decrypt bearer token: failed to look up auth token by public ID" , "error" , err )
v . requestInfo . TokenFormat = AuthTokenTypeUnknown
return
}
if at == nil {
v . logger . Trace ( "decrypt bearer token: nil result from looking up auth token by public ID" )
v . requestInfo . TokenFormat = AuthTokenTypeUnknown
return
}
tokenWrapper , err := v . kms . GetWrapper ( v . ctx , at . GetScopeId (), kms . KeyPurposeTokens )
if err != nil {
v . logger . Warn ( "decrypt bearer token: unable to get wrapper for tokens; continuing as anonymous user" , "error" , err )
v . requestInfo . TokenFormat = AuthTokenTypeUnknown
return
}
version := v . requestInfo . EncryptedToken [ 0 : len ( globals . ServiceTokenV1 )]
switch version {
case globals . ServiceTokenV1 :
default :
v . logger . Trace ( "decrypt bearer token: unknown token encryption version; continuing as anonymous user" , "version" , version )
v . requestInfo . TokenFormat = AuthTokenTypeUnknown
return
}
2020-09-26 14:01:49 -04:00
marshaledToken , err := base58 . FastBase58Decoding ( v . requestInfo . EncryptedToken [ len ( globals . ServiceTokenV1 ):])
if err != nil {
v . logger . Trace ( "decrypt bearer token: error unmarshaling base58 token; continuing as anonymous user" , "error" , err )
v . requestInfo . TokenFormat = AuthTokenTypeUnknown
return
}
2020-09-14 12:30:11 -04:00
blobInfo := new ( wrapping . EncryptedBlobInfo )
if err := proto . Unmarshal ( marshaledToken , blobInfo ); err != nil {
v . logger . Trace ( "decrypt bearer token: error decoding encrypted token; continuing as anonymous user" , "error" , err )
v . requestInfo . TokenFormat = AuthTokenTypeUnknown
return
}
s1Bytes , err := tokenWrapper . Decrypt ( v . ctx , blobInfo , [] byte ( v . requestInfo . PublicId ))
if err != nil {
v . logger . Trace ( "decrypt bearer token: error decrypting encrypted token; continuing as anonymous user" , "error" , err )
v . requestInfo . TokenFormat = AuthTokenTypeUnknown
return
}
var s1Info tokens . S1TokenInfo
if err := proto . Unmarshal ( s1Bytes , & s1Info ); err != nil {
v . logger . Trace ( "decrypt bearer token: error unmarshaling token info; continuing as anonymous user" , "error" , err )
v . requestInfo . TokenFormat = AuthTokenTypeUnknown
return
}
if v . requestInfo . TokenFormat == AuthTokenTypeUnknown || s1Info . Token == "" || v . requestInfo . PublicId == "" {
v . logger . Trace ( "decrypt bearer token: after parsing, could not find valid token; continuing as anonymous user" )
v . requestInfo . TokenFormat = AuthTokenTypeUnknown
return
}
2020-07-22 00:14:28 -04:00
2020-09-14 12:30:11 -04:00
v . requestInfo . Token = s1Info . Token
return
case AuthTokenTypeRecoveryKms :
if v . kms == nil {
v . logger . Trace ( "decrypt recovery token: no KMS object available to authz system" )
v . requestInfo . TokenFormat = AuthTokenTypeUnknown
return
}
wrapper := v . kms . GetExternalWrappers (). Recovery ()
if wrapper == nil {
v . logger . Trace ( "decrypt recovery token: no recovery KMS is available" )
v . requestInfo . TokenFormat = AuthTokenTypeUnknown
return
}
info , err := recovery . ParseRecoveryToken ( v . ctx , wrapper , v . requestInfo . EncryptedToken )
if err != nil {
v . logger . Trace ( "decrypt recovery token: error parsing and validating recovery token" , "error" , err )
v . requestInfo . TokenFormat = AuthTokenTypeUnknown
return
}
// If we add the validity period to the creation time (which we've
// verified is before the current time, with a minute of fudging), and
// it's before now, it's expired and might be a replay.
if info . CreationTime . Add ( globals . RecoveryTokenValidityPeriod ). Before ( time . Now ()) {
2020-09-23 01:23:10 -04:00
v . logger . Warn ( "decrypt recovery token: recovery token has expired (possible replay attack)" )
2020-09-14 12:30:11 -04:00
v . requestInfo . TokenFormat = AuthTokenTypeUnknown
return
}
repo , err := v . serversRepoFn ()
if err != nil {
v . logger . Trace ( "decrypt recovery token: error fetching servers repo" , "error" , err )
v . requestInfo . TokenFormat = AuthTokenTypeUnknown
return
}
if err := repo . AddRecoveryNonce ( v . ctx , info . Nonce ); err != nil {
2020-09-23 01:23:10 -04:00
v . logger . Warn ( "decrypt recovery token: error adding nonce to database (possible replay attack)" , "error" , err )
2020-09-14 12:30:11 -04:00
v . requestInfo . TokenFormat = AuthTokenTypeUnknown
return
}
2020-09-23 01:23:10 -04:00
v . logger . Warn ( "recovery KMS was used to authorize a call" , "url" , v . requestInfo . Path , "method" , v . requestInfo . Method )
2020-09-14 12:30:11 -04:00
}
2020-07-22 00:14:28 -04:00
}