github.com/anuvu/tyk@v2.9.0-beta9-dl-apic+incompatible/gateway/oauth_manager.go (about)

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