github.com/Tyktechnologies/tyk@v2.9.5+incompatible/gateway/oauth_manager.go (about)

     1  package gateway
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/base64"
     6  	"encoding/json"
     7  	"errors"
     8  	"fmt"
     9  	"math"
    10  	"net/http"
    11  	"net/url"
    12  	"strings"
    13  	"sync"
    14  	"time"
    15  
    16  	"github.com/lonelycode/osin"
    17  	uuid "github.com/satori/go.uuid"
    18  	"golang.org/x/crypto/bcrypt"
    19  
    20  	"strconv"
    21  
    22  	"github.com/TykTechnologies/tyk/config"
    23  	"github.com/TykTechnologies/tyk/headers"
    24  	"github.com/TykTechnologies/tyk/storage"
    25  	"github.com/TykTechnologies/tyk/user"
    26  )
    27  
    28  /*
    29  Sample Oaut Flow:
    30  -----------------
    31  
    32  1. Request to /authorize
    33  2. Tyk extracts all relevant data and pre-screens client_id, client_secret and redirect_uri
    34  3. Instead of proxying the request it redirects the user to the login page on the resource with the client_id & secret as a POST (basically passed through)
    35  4. Resource presents approve / deny window to user
    36  5. If approve is clicked, resource pings oauth/authorise which is the actual authorize endpoint (requires admin key),
    37     this returns oauth details to resource as well as redirect URI which it can then redirec to
    38  6. User is redirected to redirect URI with auth_code
    39  7. Client makes auth request for bearer token
    40  8. Client API makes all calls with bearer token
    41  
    42  Effort required by Resource Owner:
    43  1. Create a login & approve/deny page
    44  2. Send an API request to Tyk to generate an auth_code
    45  3. Create endpoint to accept key change notifications
    46  */
    47  
    48  // OAuthClient is a representation within an APISpec of a client
    49  type OAuthClient struct {
    50  	ClientID          string      `json:"id"`
    51  	ClientSecret      string      `json:"secret"`
    52  	ClientRedirectURI string      `json:"redirecturi"`
    53  	MetaData          interface{} `json:"meta_data,omitempty"`
    54  	PolicyID          string      `json:"policyid"`
    55  	Description       string      `json:"description"`
    56  }
    57  
    58  func (oc *OAuthClient) GetId() string {
    59  	return oc.ClientID
    60  }
    61  
    62  func (oc *OAuthClient) GetSecret() string {
    63  	return oc.ClientSecret
    64  }
    65  
    66  func (oc *OAuthClient) GetRedirectUri() string {
    67  	return oc.ClientRedirectURI
    68  }
    69  
    70  func (oc *OAuthClient) GetUserData() interface{} {
    71  	return oc.MetaData
    72  }
    73  
    74  func (oc *OAuthClient) GetPolicyID() string {
    75  	return oc.PolicyID
    76  }
    77  
    78  func (oc *OAuthClient) GetDescription() string {
    79  	return oc.Description
    80  }
    81  
    82  // OAuthNotificationType const to reduce risk of collisions
    83  type OAuthNotificationType string
    84  
    85  // Notification codes for new and refresh codes
    86  const (
    87  	newAccessToken     OAuthNotificationType = "new"
    88  	refreshAccessToken OAuthNotificationType = "refresh"
    89  )
    90  
    91  // NewOAuthNotification is a notification sent to a
    92  // web-hook when an access request or a refresh request comes in.
    93  type NewOAuthNotification struct {
    94  	AuthCode         string                `json:"auth_code"`
    95  	NewOAuthToken    string                `json:"new_oauth_token"`
    96  	RefreshToken     string                `json:"refresh_token"`
    97  	OldRefreshToken  string                `json:"old_refresh_token"`
    98  	NotificationType OAuthNotificationType `json:"notification_type"`
    99  }
   100  
   101  // OAuthHandlers are the HTTP Handlers that manage the Tyk OAuth flow
   102  type OAuthHandlers struct {
   103  	Manager OAuthManager
   104  }
   105  
   106  func (o *OAuthHandlers) generateOAuthOutputFromOsinResponse(osinResponse *osin.Response) []byte {
   107  
   108  	// TODO: Might need to clear this out
   109  	if osinResponse.Output["state"] == "" {
   110  		log.Debug("Removing state")
   111  		delete(osinResponse.Output, "state")
   112  	}
   113  
   114  	redirect, err := osinResponse.GetRedirectUrl()
   115  	if err == nil {
   116  		// Hack to inject redirect into response
   117  		osinResponse.Output["redirect_to"] = redirect
   118  	}
   119  
   120  	buffer := &bytes.Buffer{}
   121  	encoder := json.NewEncoder(buffer)
   122  	encoder.SetEscapeHTML(false)
   123  	err = encoder.Encode(&osinResponse.Output)
   124  	if err != nil {
   125  		return nil
   126  	}
   127  	return buffer.Bytes()
   128  }
   129  
   130  func (o *OAuthHandlers) notifyClientOfNewOauth(notification NewOAuthNotification) {
   131  	log.Info("[OAuth] Notifying client host")
   132  	go o.Manager.API.NotificationsDetails.SendRequest(false, 0, notification)
   133  }
   134  
   135  // HandleGenerateAuthCodeData handles a resource provider approving an OAuth request from a client
   136  func (o *OAuthHandlers) HandleGenerateAuthCodeData(w http.ResponseWriter, r *http.Request) {
   137  	// On AUTH grab session state data and add to UserData (not validated, not good!)
   138  	sessionJSONData := r.FormValue("key_rules")
   139  	if sessionJSONData == "" {
   140  		log.Warning("Authorise request is missing key_rules in params, policy will be required!")
   141  	}
   142  
   143  	// Handle the authorisation and write the JSON output to the resource provider
   144  	resp := o.Manager.HandleAuthorisation(r, true, sessionJSONData)
   145  	code := http.StatusOK
   146  	msg := o.generateOAuthOutputFromOsinResponse(resp)
   147  	if resp.IsError {
   148  		code = resp.ErrorStatusCode
   149  		log.Error("[OAuth] OAuth response marked as error: ", resp)
   150  	}
   151  	w.WriteHeader(code)
   152  	w.Write(msg)
   153  }
   154  
   155  // HandleAuthorizePassthrough handles a Client Auth request, first it checks if the client
   156  // is OK (otherwise it blocks the request), then it forwards on to the resource providers approval URI
   157  func (o *OAuthHandlers) HandleAuthorizePassthrough(w http.ResponseWriter, r *http.Request) {
   158  	// Extract client data and check
   159  	resp := o.Manager.HandleAuthorisation(r, false, "")
   160  	if resp.IsError {
   161  		log.Error("There was an error with the request: ", resp)
   162  		// Something went wrong, write out the error details and kill the response
   163  		doJSONWrite(w, resp.ErrorStatusCode, apiError(resp.StatusText))
   164  		return
   165  	}
   166  	if r.Method == "GET" {
   167  		loginURL := fmt.Sprintf("%s?%s", o.Manager.API.Oauth2Meta.AuthorizeLoginRedirect, r.URL.RawQuery)
   168  		w.Header().Add("Location", loginURL)
   169  	} else {
   170  		w.Header().Add("Location", o.Manager.API.Oauth2Meta.AuthorizeLoginRedirect)
   171  	}
   172  	w.WriteHeader(307)
   173  
   174  }
   175  
   176  // HandleAccessRequest handles the OAuth 2.0 token or refresh access request, and wraps Tyk's own and Osin's OAuth handlers,
   177  // returns a response to the client and notifies the provider of the access request (in order to track identity against
   178  // OAuth tokens without revealing tokens before they are requested).
   179  func (o *OAuthHandlers) HandleAccessRequest(w http.ResponseWriter, r *http.Request) {
   180  	w.Header().Set(headers.ContentType, headers.ApplicationJSON)
   181  	// Handle response
   182  	resp := o.Manager.HandleAccess(r)
   183  	msg := o.generateOAuthOutputFromOsinResponse(resp)
   184  
   185  	if resp.IsError {
   186  		// Something went wrong, write out the error details and kill the response
   187  		w.WriteHeader(resp.ErrorStatusCode)
   188  		w.Write(msg)
   189  		return
   190  	}
   191  
   192  	// Ping endpoint with o_auth key and auth_key
   193  	authCode := r.FormValue("code")
   194  	oldRefreshToken := r.FormValue("refresh_token")
   195  	log.Debug("AUTH CODE: ", authCode)
   196  	newOauthToken := ""
   197  	if resp.Output["access_token"] != nil {
   198  		newOauthToken = resp.Output["access_token"].(string)
   199  	}
   200  	log.Debug("TOKEN: ", newOauthToken)
   201  	refreshToken := ""
   202  	if resp.Output["refresh_token"] != nil {
   203  		refreshToken = resp.Output["refresh_token"].(string)
   204  	}
   205  	log.Debug("REFRESH: ", refreshToken)
   206  	log.Debug("Old REFRESH: ", oldRefreshToken)
   207  
   208  	notificationType := newAccessToken
   209  	if oldRefreshToken != "" {
   210  		notificationType = refreshAccessToken
   211  	}
   212  
   213  	newNotification := NewOAuthNotification{
   214  		AuthCode:         authCode,
   215  		NewOAuthToken:    newOauthToken,
   216  		RefreshToken:     refreshToken,
   217  		OldRefreshToken:  oldRefreshToken,
   218  		NotificationType: notificationType,
   219  	}
   220  
   221  	o.notifyClientOfNewOauth(newNotification)
   222  
   223  	w.WriteHeader(http.StatusOK)
   224  	w.Write(msg)
   225  }
   226  
   227  const (
   228  	accessToken  = "access_token"
   229  	refreshToken = "refresh_token"
   230  )
   231  
   232  //in compliance with https://tools.ietf.org/html/rfc7009#section-2.1
   233  //ToDo: set an authentication mechanism
   234  func (o *OAuthHandlers) HandleRevokeToken(w http.ResponseWriter, r *http.Request) {
   235  	err := r.ParseForm()
   236  	if err != nil {
   237  		doJSONWrite(w, http.StatusBadRequest, apiError("error parsing form. Form malformed"))
   238  		return
   239  	}
   240  
   241  	token := r.PostFormValue("token")
   242  	tokenTypeHint := r.PostFormValue("token_type_hint")
   243  
   244  	if token == "" {
   245  		doJSONWrite(w, http.StatusBadRequest, apiError(oauthTokenEmpty))
   246  		return
   247  	}
   248  
   249  	RevokeToken(o.Manager.OsinServer.Storage, token, tokenTypeHint)
   250  	doJSONWrite(w, http.StatusOK, apiOk("token revoked successfully"))
   251  }
   252  
   253  func RevokeToken(storage ExtendedOsinStorageInterface, token, tokenTypeHint string) {
   254  	switch tokenTypeHint {
   255  	case accessToken:
   256  		storage.RemoveAccess(token)
   257  	case refreshToken:
   258  		storage.RemoveRefresh(token)
   259  	default:
   260  		storage.RemoveAccess(token)
   261  		storage.RemoveRefresh(token)
   262  	}
   263  }
   264  
   265  func (o *OAuthHandlers) HandleRevokeAllTokens(w http.ResponseWriter, r *http.Request) {
   266  	err := r.ParseForm()
   267  	if err != nil {
   268  		doJSONWrite(w, http.StatusBadRequest, apiError("error parsing form. Form malformed"))
   269  		return
   270  	}
   271  
   272  	clientId := r.PostFormValue("client_id")
   273  	secret := r.PostFormValue("client_secret")
   274  
   275  	if clientId == "" {
   276  		doJSONWrite(w, http.StatusUnauthorized, apiError(oauthClientIdEmpty))
   277  		return
   278  	}
   279  
   280  	if secret == "" {
   281  		doJSONWrite(w, http.StatusUnauthorized, apiError(oauthClientSecretEmpty))
   282  		return
   283  	}
   284  
   285  	status, tokens, err := RevokeAllTokens(o.Manager.OsinServer.Storage, clientId, secret)
   286  	if err != nil {
   287  		doJSONWrite(w, status, apiError(err.Error()))
   288  		return
   289  	}
   290  
   291  	n := Notification{
   292  		Command: KeySpaceUpdateNotification,
   293  		Payload: strings.Join(tokens, ","),
   294  	}
   295  	MainNotifier.Notify(n)
   296  
   297  	doJSONWrite(w, http.StatusOK, apiOk("tokens revoked successfully"))
   298  }
   299  
   300  func RevokeAllTokens(storage ExtendedOsinStorageInterface, clientId, clientSecret string) (int, []string, error) {
   301  	resp := []string{}
   302  	client, err := storage.GetClient(clientId)
   303  	log.Debug("Revoke all tokens")
   304  	if err != nil {
   305  		return http.StatusNotFound, resp, errors.New("error getting oauth client")
   306  	}
   307  
   308  	if client.GetSecret() != clientSecret {
   309  		return http.StatusUnauthorized, resp, errors.New(oauthClientSecretWrong)
   310  	}
   311  
   312  	clientTokens, err := storage.GetClientTokens(clientId)
   313  	if err != nil {
   314  		return http.StatusBadRequest, resp, errors.New("cannot retrieve client tokens")
   315  	}
   316  
   317  	log.Debug("Tokens found to be revoked:", len(clientTokens))
   318  	for _, token := range clientTokens {
   319  		access, err := storage.LoadAccess(token.Token)
   320  		if err == nil {
   321  			resp = append(resp, access.AccessToken)
   322  			storage.RemoveAccess(access.AccessToken)
   323  			storage.RemoveRefresh(access.RefreshToken)
   324  		} else {
   325  			log.Debug("error loading access:", err.Error())
   326  		}
   327  	}
   328  
   329  	return http.StatusOK, resp, nil
   330  }
   331  
   332  // OAuthManager handles and wraps osin OAuth2 functions to handle authorise and access requests
   333  type OAuthManager struct {
   334  	API        *APISpec
   335  	OsinServer *TykOsinServer
   336  }
   337  
   338  // HandleAuthorisation creates the authorisation data for the request
   339  func (o *OAuthManager) HandleAuthorisation(r *http.Request, complete bool, session string) *osin.Response {
   340  	resp := o.OsinServer.NewResponse()
   341  
   342  	if ar := o.OsinServer.HandleAuthorizeRequest(resp, r); ar != nil {
   343  		// Since this is called by the Reource provider (proxied API), we assume it has been approved
   344  		ar.Authorized = true
   345  
   346  		if complete {
   347  			ar.UserData = session
   348  			o.OsinServer.FinishAuthorizeRequest(resp, r, ar)
   349  		}
   350  	}
   351  	if resp.IsError && resp.InternalError != nil {
   352  		log.Error(resp.InternalError)
   353  	}
   354  
   355  	return resp
   356  }
   357  
   358  // JSONToFormValues if r has header Content-Type set to application/json this
   359  // will decode request body as json to map[string]string and adds the key/value
   360  // pairs in r.Form.
   361  func JSONToFormValues(r *http.Request) error {
   362  	if r.Header.Get("Content-Type") == "application/json" {
   363  		var o map[string]string
   364  		err := json.NewDecoder(r.Body).Decode(&o)
   365  		if err != nil {
   366  			return err
   367  		}
   368  		if len(o) > 0 {
   369  			if r.Form == nil {
   370  				r.Form = make(url.Values)
   371  			}
   372  			for k, v := range o {
   373  				r.Form.Set(k, v)
   374  			}
   375  		}
   376  
   377  	}
   378  	return nil
   379  }
   380  
   381  // HandleAccess wraps an access request with osin's primitives
   382  func (o *OAuthManager) HandleAccess(r *http.Request) *osin.Response {
   383  	resp := o.OsinServer.NewResponse()
   384  	// we are intentionally ignoring errors, because this is called again by
   385  	// osin.We are only doing this to ensure r.From is properly initialized incase
   386  	// r.ParseForm was success
   387  	r.ParseForm()
   388  	if err := JSONToFormValues(r); err != nil {
   389  		log.Errorf("trying to set url values decoded from json body :%v", err)
   390  	}
   391  	var username string
   392  
   393  	if ar := o.OsinServer.HandleAccessRequest(resp, r); ar != nil {
   394  
   395  		session := &user.SessionState{Mutex: &sync.RWMutex{}}
   396  		if ar.Type == osin.PASSWORD {
   397  			username = r.Form.Get("username")
   398  			password := r.Form.Get("password")
   399  			searchKey := "apikey-" + storage.HashKey(o.API.OrgID+username)
   400  			log.Debug("Getting: ", searchKey)
   401  
   402  			var err error
   403  			session, err = o.OsinServer.Storage.GetUser(searchKey)
   404  			if err != nil {
   405  				log.Warning("Attempted access with non-existent user (OAuth password flow).")
   406  			} else {
   407  				var passMatch bool
   408  				if session.BasicAuthData.Hash == user.HashBCrypt {
   409  					err := bcrypt.CompareHashAndPassword([]byte(session.BasicAuthData.Password), []byte(password))
   410  					if err == nil {
   411  						passMatch = true
   412  					}
   413  				}
   414  
   415  				if session.BasicAuthData.Hash == user.HashPlainText &&
   416  					session.BasicAuthData.Password == password {
   417  					passMatch = true
   418  				}
   419  
   420  				if passMatch {
   421  					ar.Authorized = true
   422  					// not ideal, but we need to copy the session state across
   423  					pw := session.BasicAuthData.Password
   424  					hs := session.BasicAuthData.Hash
   425  
   426  					session.BasicAuthData.Password = ""
   427  					session.BasicAuthData.Hash = ""
   428  					asString, _ := json.Marshal(session)
   429  					ar.UserData = string(asString)
   430  
   431  					session.BasicAuthData.Password = pw
   432  					session.BasicAuthData.Hash = hs
   433  
   434  					//log.Warning("Old Keys: ", session.OauthKeys)
   435  				}
   436  			}
   437  		} else {
   438  			// Using a manual flow
   439  			ar.Authorized = true
   440  		}
   441  
   442  		// Does the user have an old OAuth token for this client?
   443  		if session != nil && session.OauthKeys != nil {
   444  			log.Debug("There's keys here bill...")
   445  			oldToken, foundKey := session.OauthKeys[ar.Client.GetId()]
   446  			if foundKey {
   447  				log.Info("Found old token, revoking: ", oldToken)
   448  				o.API.SessionManager.RemoveSession(oldToken, false)
   449  			}
   450  		}
   451  
   452  		log.Debug("[OAuth] Finishing access request ")
   453  		o.OsinServer.FinishAccessRequest(resp, r, ar)
   454  		new_token, foundNewToken := resp.Output["access_token"]
   455  		if username != "" && foundNewToken {
   456  			log.Debug("Updating token data in key")
   457  			if session.OauthKeys == nil {
   458  				session.OauthKeys = make(map[string]string)
   459  			}
   460  			session.OauthKeys[ar.Client.GetId()] = new_token.(string)
   461  			log.Debug("New token: ", new_token.(string))
   462  			log.Debug("Keys: ", session.OauthKeys)
   463  
   464  			// add oauth-client user_fields to session's meta
   465  			if userData := ar.Client.GetUserData(); userData != nil {
   466  				var ok bool
   467  				session.MetaData, ok = userData.(map[string]interface{})
   468  				if !ok {
   469  					log.WithField("oauthClientID", ar.Client.GetId()).
   470  						Error("Could not set session meta_data from oauth-client fields, type mismatch")
   471  				} else {
   472  					// set session alias to developer email as we do it for regular API keys created for developer
   473  					if devEmail, found := session.GetMetaData()[keyDataDeveloperEmail].(string); found {
   474  						session.Alias = devEmail
   475  						// we don't need it in meta-data as we set it to alias
   476  						session.RemoveMetaData(keyDataDeveloperEmail)
   477  					}
   478  				}
   479  			}
   480  
   481  			keyName := generateToken(o.API.OrgID, username)
   482  
   483  			log.Debug("Updating user:", keyName)
   484  			err := o.API.SessionManager.UpdateSession(keyName, session, session.Lifetime(o.API.SessionLifetime), false)
   485  			if err != nil {
   486  				log.Error(err)
   487  			}
   488  		}
   489  	}
   490  
   491  	if resp.IsError && resp.InternalError != nil {
   492  		log.Error("ERROR: ", resp.InternalError)
   493  	}
   494  
   495  	return resp
   496  }
   497  
   498  // These enums fix the prefix to use when storing various OAuth keys and data, since we
   499  // delegate everything to the osin framework
   500  const (
   501  	prefixAuth            = "oauth-authorize."
   502  	prefixClient          = "oauth-clientid."
   503  	prefixAccess          = "oauth-access."
   504  	prefixRefresh         = "oauth-refresh."
   505  	prefixClientset       = "oauth-clientset."
   506  	prefixClientIndexList = "oauth-client-index."
   507  	prefixClientTokens    = "oauth-client-tokens."
   508  )
   509  
   510  // swagger:model
   511  type OAuthClientToken struct {
   512  	Token   string `json:"code"`
   513  	Expires int64  `json:"expires"`
   514  }
   515  
   516  type ExtendedOsinClientInterface interface {
   517  	osin.Client
   518  	GetDescription() string
   519  }
   520  
   521  type ExtendedOsinStorageInterface interface {
   522  	osin.Storage
   523  
   524  	// Create OAuth clients
   525  	SetClient(id string, orgID string, client osin.Client, ignorePrefix bool) error
   526  
   527  	// Custom getter to handle prefixing issues in Redis
   528  	GetClientNoPrefix(id string) (osin.Client, error)
   529  
   530  	GetClientTokens(id string) ([]OAuthClientToken, error)
   531  	GetPaginatedClientTokens(id string, page int) ([]OAuthClientToken, int, error)
   532  
   533  	GetExtendedClient(id string) (ExtendedOsinClientInterface, error)
   534  
   535  	// Custom getter to handle prefixing issues in Redis
   536  	GetExtendedClientNoPrefix(id string) (ExtendedOsinClientInterface, error)
   537  
   538  	GetClients(filter string, orgID string, ignorePrefix bool) ([]ExtendedOsinClientInterface, error)
   539  
   540  	DeleteClient(id string, orgID string, ignorePrefix bool) error
   541  
   542  	// GetUser retrieves a Basic Access user token type from the key store
   543  	GetUser(string) (*user.SessionState, error)
   544  
   545  	// SetUser updates a Basic Access user token type in the key store
   546  	SetUser(string, *user.SessionState, int64) error
   547  }
   548  
   549  // TykOsinServer subclasses osin.Server so we can add the SetClient method without wrecking the lbrary
   550  type TykOsinServer struct {
   551  	osin.Server
   552  	Config            *osin.ServerConfig
   553  	Storage           ExtendedOsinStorageInterface
   554  	AuthorizeTokenGen osin.AuthorizeTokenGen
   555  	AccessTokenGen    osin.AccessTokenGen
   556  }
   557  
   558  // TykOsinNewServer creates a new server instance, but uses an extended interface so we can SetClient() too.
   559  func TykOsinNewServer(config *osin.ServerConfig, storage ExtendedOsinStorageInterface) *TykOsinServer {
   560  
   561  	overrideServer := TykOsinServer{
   562  		Config:            config,
   563  		Storage:           storage,
   564  		AuthorizeTokenGen: &osin.AuthorizeTokenGenDefault{},
   565  		AccessTokenGen:    accessTokenGen{},
   566  	}
   567  
   568  	overrideServer.Server.Config = config
   569  	overrideServer.Server.Storage = storage
   570  	overrideServer.Server.AuthorizeTokenGen = overrideServer.AuthorizeTokenGen
   571  	overrideServer.Server.AccessTokenGen = accessTokenGen{}
   572  
   573  	return &overrideServer
   574  }
   575  
   576  // TODO: Refactor this to move prefix handling into a checker method, then it can be an unexported setting in the struct.
   577  // RedisOsinStorageInterface implements osin.Storage interface to use Tyk's own storage mechanism
   578  type RedisOsinStorageInterface struct {
   579  	store          storage.Handler
   580  	sessionManager SessionHandler
   581  	redisStore     storage.Handler
   582  	orgID          string
   583  }
   584  
   585  func (r *RedisOsinStorageInterface) Clone() osin.Storage {
   586  	return r
   587  }
   588  
   589  func (r *RedisOsinStorageInterface) Close() {}
   590  
   591  // GetClient will retrieve client data
   592  func (r *RedisOsinStorageInterface) GetClient(id string) (osin.Client, error) {
   593  	key := prefixClient + id
   594  
   595  	log.Info("Getting client ID:", id)
   596  
   597  	clientJSON, err := r.store.GetKey(key)
   598  	if err != nil {
   599  		log.Errorf("Failure retrieving client ID key %q: %v", key, err)
   600  		return nil, err
   601  	}
   602  
   603  	client := new(OAuthClient)
   604  	if err := json.Unmarshal([]byte(clientJSON), &client); err != nil {
   605  		log.Error("Couldn't unmarshal OAuth client object: ", err)
   606  	}
   607  
   608  	return client, nil
   609  }
   610  
   611  // GetClientNoPrefix will retrieve client data, but not assign a prefix - this is an unfortunate hack,
   612  // but we don't want to change the signature in Osin for GetClient to support the odd Redis prefixing
   613  func (r *RedisOsinStorageInterface) GetClientNoPrefix(id string) (osin.Client, error) {
   614  
   615  	key := id
   616  
   617  	clientJSON, err := r.store.GetKey(key)
   618  
   619  	if err != nil {
   620  		log.Error("Failure retrieving client ID key: ", err)
   621  		return nil, err
   622  	}
   623  
   624  	client := new(OAuthClient)
   625  	if err := json.Unmarshal([]byte(clientJSON), client); err != nil {
   626  		log.Error("Couldn't unmarshal OAuth client object: ", err)
   627  	}
   628  
   629  	return client, nil
   630  }
   631  
   632  func (r *RedisOsinStorageInterface) GetExtendedClient(id string) (ExtendedOsinClientInterface, error) {
   633  	osinClient, err := r.GetClient(id)
   634  	if err != nil {
   635  		log.WithError(err).Error("Failure retrieving client ID key")
   636  		return nil, err
   637  	}
   638  
   639  	return osinClient.(*OAuthClient), err
   640  }
   641  
   642  // GetExtendedClientNoPrefix custom getter to handle prefixing issues in Redis,
   643  func (r *RedisOsinStorageInterface) GetExtendedClientNoPrefix(id string) (ExtendedOsinClientInterface, error) {
   644  	osinClient, err := r.GetClientNoPrefix(id)
   645  	if err != nil {
   646  		log.WithError(err).Error("Failure retrieving client ID key")
   647  		return nil, err
   648  	}
   649  	return osinClient.(*OAuthClient), err
   650  }
   651  
   652  // GetClients will retrieve a list of clients for a prefix
   653  func (r *RedisOsinStorageInterface) GetClients(filter string, orgID string, ignorePrefix bool) ([]ExtendedOsinClientInterface, error) {
   654  	key := prefixClient + filter
   655  	if ignorePrefix {
   656  		key = filter
   657  	}
   658  
   659  	indexKey := prefixClientIndexList + orgID
   660  
   661  	var clientJSON map[string]string
   662  	if !config.Global().Storage.EnableCluster {
   663  		exists, _ := r.store.Exists(indexKey)
   664  		if exists {
   665  			keys, err := r.store.GetListRange(indexKey, 0, -1)
   666  			if err != nil {
   667  				log.Error("Couldn't get OAuth client index list: ", err)
   668  				return nil, err
   669  			}
   670  			keyVals, err := r.store.GetMultiKey(keys)
   671  			if err != nil {
   672  				log.Error("Couldn't get OAuth client index list values: ", err)
   673  				return nil, err
   674  			}
   675  
   676  			clientJSON = make(map[string]string)
   677  			for i, key := range keys {
   678  				clientJSON[key] = keyVals[i]
   679  			}
   680  		} else {
   681  			clientJSON = r.store.GetKeysAndValuesWithFilter(key)
   682  			for key := range clientJSON {
   683  				r.store.AppendToSet(indexKey, key)
   684  			}
   685  		}
   686  	} else {
   687  		keyForSet := prefixClientset + prefixClient // Org ID
   688  		var err error
   689  		if clientJSON, err = r.store.GetSet(keyForSet); err != nil {
   690  			return nil, err
   691  		}
   692  	}
   693  
   694  	theseClients := []ExtendedOsinClientInterface{}
   695  	for _, clientJSON := range clientJSON {
   696  		client := new(OAuthClient)
   697  		if err := json.Unmarshal([]byte(clientJSON), &client); err != nil {
   698  			log.Error("Couldn't unmarshal OAuth client object: ", err)
   699  			return nil, err
   700  		}
   701  		theseClients = append(theseClients, client)
   702  	}
   703  
   704  	return theseClients, nil
   705  }
   706  
   707  // GetPaginatedClientTokens returns all tokens associated with the given id.
   708  // It returns the tokens, the total number of pages of the tokens after
   709  // pagination and an error if any
   710  func (r *RedisOsinStorageInterface) GetPaginatedClientTokens(id string, page int) ([]OAuthClientToken, int, error) {
   711  	key := prefixClientTokens + id
   712  
   713  	// use current timestamp as a start score so all expired tokens won't be picked
   714  	nowTs := time.Now().Unix()
   715  	startScore := strconv.FormatInt(nowTs, 10)
   716  
   717  	log.Info("Getting client tokens sorted list:", key)
   718  
   719  	tokens, scores, err := r.store.GetSortedSetRange(key, startScore, "+inf")
   720  	if err != nil {
   721  		return nil, 0, err
   722  	}
   723  
   724  	// clean up expired tokens in sorted set (remove all tokens with score up to current timestamp minus retention)
   725  	if config.Global().OauthTokenExpiredRetainPeriod > 0 {
   726  		cleanupStartScore := strconv.FormatInt(nowTs-int64(config.Global().OauthTokenExpiredRetainPeriod), 10)
   727  		go r.store.RemoveSortedSetRange(key, "-inf", cleanupStartScore)
   728  	}
   729  
   730  	itemsPerPage := 100
   731  
   732  	tokenNumber := len(tokens)
   733  
   734  	if tokenNumber == 0 {
   735  		return []OAuthClientToken{}, 0, nil
   736  	}
   737  
   738  	startIdx := (page - 1) * itemsPerPage
   739  	endIdx := startIdx + itemsPerPage
   740  
   741  	if tokenNumber < startIdx {
   742  		startIdx = tokenNumber
   743  	}
   744  
   745  	if tokenNumber < endIdx {
   746  		endIdx = tokenNumber
   747  	}
   748  
   749  	totalPages := int(math.Ceil(float64(len(tokens)) / float64(itemsPerPage)))
   750  
   751  	tokens = tokens[startIdx:endIdx]
   752  
   753  	// convert sorted set data and scores into reply struct
   754  	tokensData := make([]OAuthClientToken, len(tokens))
   755  	for i := range tokens {
   756  		tokensData[i] = OAuthClientToken{
   757  			Token:   tokens[i],
   758  			Expires: int64(scores[i]), // we store expire timestamp as a score
   759  		}
   760  	}
   761  
   762  	return tokensData, totalPages, nil
   763  }
   764  
   765  func (r *RedisOsinStorageInterface) GetClientTokens(id string) ([]OAuthClientToken, error) {
   766  	key := prefixClientTokens + id
   767  
   768  	// use current timestamp as a start score so all expired tokens won't be picked
   769  	nowTs := time.Now().Unix()
   770  	startScore := strconv.FormatInt(nowTs, 10)
   771  
   772  	log.Info("Getting client tokens sorted list:", key)
   773  
   774  	tokens, scores, err := r.redisStore.GetSortedSetRange(key, startScore, "+inf")
   775  	if err != nil {
   776  		return nil, err
   777  	}
   778  
   779  	// clean up expired tokens in sorted set (remove all tokens with score up to current timestamp minus retention)
   780  	if config.Global().OauthTokenExpiredRetainPeriod > 0 {
   781  		cleanupStartScore := strconv.FormatInt(nowTs-int64(config.Global().OauthTokenExpiredRetainPeriod), 10)
   782  		go r.redisStore.RemoveSortedSetRange(key, "-inf", cleanupStartScore)
   783  	}
   784  
   785  	if len(tokens) == 0 {
   786  		return []OAuthClientToken{}, nil
   787  	}
   788  
   789  	// convert sorted set data and scores into reply struct
   790  	tokensData := make([]OAuthClientToken, len(tokens))
   791  	for i := range tokens {
   792  		tokensData[i] = OAuthClientToken{
   793  			Token:   tokens[i],
   794  			Expires: int64(scores[i]), // we store expire timestamp as a score
   795  		}
   796  	}
   797  
   798  	return tokensData, nil
   799  }
   800  
   801  // SetClient creates client data
   802  func (r *RedisOsinStorageInterface) SetClient(id string, orgID string, client osin.Client, ignorePrefix bool) error {
   803  	clientDataJSON, err := json.Marshal(client)
   804  
   805  	if err != nil {
   806  		log.Error("Couldn't marshal client data: ", err)
   807  		return err
   808  	}
   809  
   810  	key := prefixClient + id
   811  
   812  	if ignorePrefix {
   813  		key = id
   814  	}
   815  
   816  	log.Debug("CREATING: ", key)
   817  
   818  	r.store.SetKey(key, string(clientDataJSON), 0)
   819  
   820  	log.Debug("Storing copy in set")
   821  
   822  	keyForSet := prefixClientset + prefixClient // Org ID
   823  
   824  	indexKey := prefixClientIndexList + orgID
   825  	//check if the indexKey exists
   826  	exists, err := r.store.Exists(indexKey)
   827  	if err != nil {
   828  		return err
   829  	}
   830  	// if it exists, delete it to avoid duplicity in the client index list
   831  	if exists {
   832  		r.store.RemoveFromList(indexKey, key)
   833  	}
   834  	// append to oauth client index list
   835  	r.store.AppendToSet(indexKey, key)
   836  
   837  	// In set, there is no option for update so the existing client should be removed before adding new one.
   838  	set, _ := r.store.GetSet(keyForSet)
   839  	for _, v := range set {
   840  		if strings.Contains(v, client.GetId()) {
   841  			r.store.RemoveFromSet(keyForSet, v)
   842  		}
   843  	}
   844  
   845  	r.store.AddToSet(keyForSet, string(clientDataJSON))
   846  	return nil
   847  }
   848  
   849  // DeleteClient Removes a client from the system
   850  func (r *RedisOsinStorageInterface) DeleteClient(id string, orgID string, ignorePrefix bool) error {
   851  	key := prefixClient + id
   852  	if ignorePrefix {
   853  		key = id
   854  	}
   855  
   856  	// Get the raw vals:
   857  	clientJSON, err := r.store.GetKey(key)
   858  	keyForSet := prefixClientset + prefixClient // Org ID
   859  	if err == nil {
   860  		log.Debug("Removing from set")
   861  		r.store.RemoveFromSet(keyForSet, clientJSON)
   862  	}
   863  
   864  	r.store.DeleteKey(key)
   865  
   866  	indexKey := prefixClientIndexList + orgID
   867  	// delete from oauth client
   868  	r.store.RemoveFromList(indexKey, key)
   869  
   870  	// delete list of tokens for this client
   871  	r.store.DeleteKey(prefixClientTokens + id)
   872  	if config.Global().SlaveOptions.UseRPC {
   873  		r.redisStore.RemoveFromList(indexKey, key)
   874  		r.redisStore.DeleteKey(prefixClientTokens + id)
   875  		r.redisStore.RemoveFromSet(keyForSet, clientJSON)
   876  	}
   877  
   878  	return nil
   879  }
   880  
   881  // SaveAuthorize saves authorisation data to Redis
   882  func (r *RedisOsinStorageInterface) SaveAuthorize(authData *osin.AuthorizeData) error {
   883  	authDataJSON, err := json.Marshal(&authData)
   884  	if err != nil {
   885  		return err
   886  	}
   887  	key := prefixAuth + authData.Code
   888  	log.Debug("Saving auth code: ", key)
   889  
   890  	r.store.SetKey(key, string(authDataJSON), int64(authData.ExpiresIn))
   891  
   892  	return nil
   893  }
   894  
   895  // LoadAuthorize loads auth data from redis
   896  func (r *RedisOsinStorageInterface) LoadAuthorize(code string) (*osin.AuthorizeData, error) {
   897  	key := prefixAuth + code
   898  	log.Debug("Loading auth code: ", key)
   899  	authJSON, err := r.store.GetKey(key)
   900  
   901  	if err != nil {
   902  		log.Error("Failure retreiving auth code key: ", err)
   903  		return nil, err
   904  	}
   905  
   906  	authData := osin.AuthorizeData{Client: new(OAuthClient)}
   907  	if err := json.Unmarshal([]byte(authJSON), &authData); err != nil {
   908  		log.Error("Couldn't unmarshal OAuth auth data object (LoadAuthorize): ", err)
   909  		return nil, err
   910  	}
   911  
   912  	return &authData, nil
   913  }
   914  
   915  // RemoveAuthorize removes authorisation keys from redis
   916  func (r *RedisOsinStorageInterface) RemoveAuthorize(code string) error {
   917  	key := prefixAuth + code
   918  	r.store.DeleteKey(key)
   919  	return nil
   920  }
   921  
   922  // SaveAccess will save a token and it's access data to redis
   923  func (r *RedisOsinStorageInterface) SaveAccess(accessData *osin.AccessData) error {
   924  	authDataJSON, err := json.Marshal(accessData)
   925  	if err != nil {
   926  		return err
   927  	}
   928  	key := prefixAccess + storage.HashKey(accessData.AccessToken)
   929  	log.Debug("Saving ACCESS key: ", key)
   930  
   931  	// Overide default ExpiresIn:
   932  	if oauthTokenExpire := config.Global().OauthTokenExpire; oauthTokenExpire != 0 {
   933  		accessData.ExpiresIn = oauthTokenExpire
   934  	}
   935  
   936  	r.store.SetKey(key, string(authDataJSON), int64(accessData.ExpiresIn))
   937  
   938  	// add code to list of tokens for this client
   939  	sortedListKey := prefixClientTokens + accessData.Client.GetId()
   940  	log.Debug("Adding ACCESS key to sorted list: ", sortedListKey)
   941  	r.redisStore.AddToSortedSet(
   942  		sortedListKey,
   943  		storage.HashKey(accessData.AccessToken),
   944  		float64(accessData.CreatedAt.Unix()+int64(accessData.ExpiresIn)), // set score as token expire timestamp
   945  	)
   946  
   947  	// Create a user.SessionState object and register it with the authmanager
   948  	var newSession user.SessionState
   949  
   950  	// ------
   951  	checkPolicy := true
   952  	if accessData.UserData != nil {
   953  		checkPolicy = false
   954  		err := json.Unmarshal([]byte(accessData.UserData.(string)), &newSession)
   955  		if err != nil {
   956  			log.Info("Couldn't decode user.SessionState from UserData, checking policy: ", err)
   957  			checkPolicy = true
   958  		}
   959  	}
   960  
   961  	if checkPolicy {
   962  		// defined in JWT middleware
   963  		sessionFromPolicy, err := generateSessionFromPolicy(accessData.Client.GetPolicyID(), "", false)
   964  		if err != nil {
   965  			return errors.New("Couldn't use policy or key rules to create token, failing")
   966  		}
   967  
   968  		newSession = sessionFromPolicy
   969  	}
   970  
   971  	// ------
   972  
   973  	// Set the client ID for analytics
   974  	newSession.OauthClientID = accessData.Client.GetId()
   975  
   976  	// Override timeouts so that we can be in sync with Osin
   977  	newSession.Expires = time.Now().Unix() + int64(accessData.ExpiresIn)
   978  
   979  	c, ok := accessData.Client.(*OAuthClient)
   980  	if ok && c.MetaData != nil {
   981  		if newSession.GetMetaData() == nil {
   982  			newSession.SetMetaData(make(map[string]interface{}))
   983  		}
   984  
   985  		// Allow session inherit and *override* client values
   986  		for k, v := range c.MetaData.(map[string]interface{}) {
   987  			if _, found := newSession.GetMetaDataByKey(k); !found {
   988  				newSession.MetaData[k] = v
   989  			}
   990  		}
   991  	}
   992  
   993  	// Use the default session expiry here as this is OAuth
   994  	r.sessionManager.UpdateSession(accessData.AccessToken, &newSession, int64(accessData.ExpiresIn), false)
   995  
   996  	// Store the refresh token too
   997  	if accessData.RefreshToken != "" {
   998  		accessDataJSON, err := json.Marshal(accessData)
   999  		if err != nil {
  1000  			return err
  1001  		}
  1002  		key := prefixRefresh + accessData.RefreshToken
  1003  		refreshExpire := int64(1209600) // 14 days
  1004  		if oauthRefreshExpire := config.Global().OauthRefreshExpire; oauthRefreshExpire != 0 {
  1005  			refreshExpire = oauthRefreshExpire
  1006  		}
  1007  		r.store.SetKey(key, string(accessDataJSON), refreshExpire)
  1008  		log.Debug("STORING ACCESS DATA: ", string(accessDataJSON))
  1009  		return nil
  1010  	}
  1011  
  1012  	return nil
  1013  }
  1014  
  1015  // LoadAccess will load access data from redis
  1016  func (r *RedisOsinStorageInterface) LoadAccess(token string) (*osin.AccessData, error) {
  1017  	key := prefixAccess + storage.HashKey(token)
  1018  	log.Debug("Loading ACCESS key: ", key)
  1019  	accessJSON, err := r.store.GetKey(key)
  1020  
  1021  	if err != nil {
  1022  		// Fallback to unhashed value for backward compatibility
  1023  		key = prefixAccess + token
  1024  		accessJSON, err = r.store.GetKey(key)
  1025  
  1026  		if err != nil {
  1027  			log.Error("Failure retreiving access token by key: ", err)
  1028  			return nil, err
  1029  		}
  1030  	}
  1031  
  1032  	accessData := osin.AccessData{Client: new(OAuthClient)}
  1033  	if err := json.Unmarshal([]byte(accessJSON), &accessData); err != nil {
  1034  		log.Error("Couldn't unmarshal OAuth auth data object (LoadAccess): ", err)
  1035  		return nil, err
  1036  	}
  1037  
  1038  	return &accessData, nil
  1039  }
  1040  
  1041  // RemoveAccess will remove access data from Redis
  1042  func (r *RedisOsinStorageInterface) RemoveAccess(token string) error {
  1043  
  1044  	access, err := r.LoadAccess(token)
  1045  	if err == nil {
  1046  		key := prefixClientTokens + access.Client.GetId()
  1047  		//remove from set oauth.client-tokens
  1048  		log.Debug("removing token from oauth client tokens list")
  1049  		limit := strconv.FormatFloat(float64(access.ExpireAt().Unix()), 'f', 0, 64)
  1050  		r.redisStore.RemoveSortedSetRange(key, limit, limit)
  1051  	} else {
  1052  		log.Warning("Cannot load access token:", token)
  1053  	}
  1054  
  1055  	key := prefixAccess + storage.HashKey(token)
  1056  	r.store.DeleteKey(key)
  1057  
  1058  	// remove the access token from central storage too
  1059  	r.sessionManager.RemoveSession(token, false)
  1060  
  1061  	return nil
  1062  }
  1063  
  1064  // LoadRefresh will load access data from Redis
  1065  func (r *RedisOsinStorageInterface) LoadRefresh(token string) (*osin.AccessData, error) {
  1066  	key := prefixRefresh + token
  1067  	log.Debug("Loading REFRESH key: ", key)
  1068  	accessJSON, err := r.store.GetKey(key)
  1069  
  1070  	if err != nil {
  1071  		log.Error("Failure retreiving access token by key: ", err)
  1072  		return nil, err
  1073  	}
  1074  
  1075  	// new interface means having to make this nested... ick.
  1076  	accessData := osin.AccessData{Client: new(OAuthClient)}
  1077  	if err := json.Unmarshal([]byte(accessJSON), &accessData); err != nil {
  1078  		log.Error("Couldn't unmarshal OAuth auth data object (LoadRefresh): ", err,
  1079  			"; Decoding: ", accessJSON)
  1080  		return nil, err
  1081  	}
  1082  
  1083  	return &accessData, nil
  1084  }
  1085  
  1086  // RemoveRefresh will remove a refresh token from redis
  1087  func (r *RedisOsinStorageInterface) RemoveRefresh(token string) error {
  1088  	log.Debug("is going to revoke refresh token: ", token)
  1089  	key := prefixRefresh + token
  1090  	r.store.DeleteKey(key)
  1091  	return nil
  1092  }
  1093  
  1094  // accessTokenGen is a modified authorization token generator that uses the same method used to generate tokens for Tyk authHandler
  1095  type accessTokenGen struct{}
  1096  
  1097  // GenerateAccessToken generates base64-encoded UUID access and refresh tokens
  1098  func (accessTokenGen) GenerateAccessToken(data *osin.AccessData, generaterefresh bool) (accesstoken, refreshtoken string, err error) {
  1099  	log.Info("[OAuth] Generating new token")
  1100  
  1101  	var newSession user.SessionState
  1102  	checkPolicy := true
  1103  	if data.UserData != nil {
  1104  		checkPolicy = false
  1105  		err := json.Unmarshal([]byte(data.UserData.(string)), &newSession)
  1106  		if err != nil {
  1107  			log.Info("[GenerateAccessToken] Couldn't decode user.SessionState from UserData, checking policy: ", err)
  1108  			checkPolicy = true
  1109  		}
  1110  	}
  1111  
  1112  	if checkPolicy {
  1113  		// defined in JWT middleware
  1114  		sessionFromPolicy, err := generateSessionFromPolicy(data.Client.GetPolicyID(), "", false)
  1115  		if err != nil {
  1116  			return "", "", errors.New("Couldn't use policy or key rules to create token, failing")
  1117  		}
  1118  
  1119  		newSession = sessionFromPolicy
  1120  	}
  1121  
  1122  	accesstoken = keyGen.GenerateAuthKey(newSession.OrgID)
  1123  	if generaterefresh {
  1124  		u6 := uuid.NewV4()
  1125  		refreshtoken = base64.StdEncoding.EncodeToString([]byte(u6.String()))
  1126  	}
  1127  	return
  1128  }
  1129  
  1130  // LoadRefresh will load access data from Redis
  1131  func (r *RedisOsinStorageInterface) GetUser(username string) (*user.SessionState, error) {
  1132  	key := username
  1133  	log.Debug("Loading User key: ", key)
  1134  	accessJSON, err := r.store.GetRawKey(key)
  1135  
  1136  	if err != nil {
  1137  		log.Error("Failure retreiving access token by key: ", err)
  1138  		return nil, err
  1139  	}
  1140  
  1141  	// new interface means having to make this nested... ick.
  1142  	session := user.SessionState{Mutex: &sync.RWMutex{}}
  1143  	if err := json.Unmarshal([]byte(accessJSON), &session); err != nil {
  1144  		log.Error("Couldn't unmarshal OAuth auth data object (LoadRefresh): ", err,
  1145  			"; Decoding: ", accessJSON)
  1146  		return nil, err
  1147  	}
  1148  
  1149  	return &session, nil
  1150  }
  1151  
  1152  func (r *RedisOsinStorageInterface) SetUser(username string, session *user.SessionState, timeout int64) error {
  1153  	key := username
  1154  	authDataJSON, err := json.Marshal(session)
  1155  	if err != nil {
  1156  		return err
  1157  	}
  1158  
  1159  	if err := r.store.SetRawKey(key, string(authDataJSON), timeout); err != nil {
  1160  		log.Error("Failure setting user token by key: ", err)
  1161  		return err
  1162  	}
  1163  
  1164  	return nil
  1165  
  1166  }