Blame vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1/requests.go

Packit 63bb0d
package oauth1
Packit 63bb0d
Packit 63bb0d
import (
Packit 63bb0d
	"crypto/hmac"
Packit 63bb0d
	"crypto/sha1"
Packit 63bb0d
	"encoding/base64"
Packit 63bb0d
	"fmt"
Packit 63bb0d
	"io/ioutil"
Packit 63bb0d
	"math/rand"
Packit 63bb0d
	"net/url"
Packit 63bb0d
	"sort"
Packit 63bb0d
	"strconv"
Packit 63bb0d
	"strings"
Packit 63bb0d
	"time"
Packit 63bb0d
Packit 63bb0d
	"github.com/gophercloud/gophercloud"
Packit 63bb0d
	"github.com/gophercloud/gophercloud/openstack/identity/v3/tokens"
Packit 63bb0d
	"github.com/gophercloud/gophercloud/pagination"
Packit 63bb0d
)
Packit 63bb0d
Packit 63bb0d
// Type SignatureMethod is a OAuth1 SignatureMethod type.
Packit 63bb0d
type SignatureMethod string
Packit 63bb0d
Packit 63bb0d
const (
Packit 63bb0d
	// HMACSHA1 is a recommended OAuth1 signature method.
Packit 63bb0d
	HMACSHA1 SignatureMethod = "HMAC-SHA1"
Packit 63bb0d
Packit 63bb0d
	// PLAINTEXT signature method is not recommended to be used in
Packit 63bb0d
	// production environment.
Packit 63bb0d
	PLAINTEXT SignatureMethod = "PLAINTEXT"
Packit 63bb0d
Packit 63bb0d
	// OAuth1TokenContentType is a supported content type for an OAuth1
Packit 63bb0d
	// token.
Packit 63bb0d
	OAuth1TokenContentType = "application/x-www-form-urlencoded"
Packit 63bb0d
)
Packit 63bb0d
Packit 63bb0d
// AuthOptions represents options for authenticating a user using OAuth1 tokens.
Packit 63bb0d
type AuthOptions struct {
Packit 63bb0d
	// OAuthConsumerKey is the OAuth1 Consumer Key.
Packit 63bb0d
	OAuthConsumerKey string `q:"oauth_consumer_key" required:"true"`
Packit 63bb0d
Packit 63bb0d
	// OAuthConsumerSecret is the OAuth1 Consumer Secret. Used to generate
Packit 63bb0d
	// an OAuth1 request signature.
Packit 63bb0d
	OAuthConsumerSecret string `required:"true"`
Packit 63bb0d
Packit 63bb0d
	// OAuthToken is the OAuth1 Request Token.
Packit 63bb0d
	OAuthToken string `q:"oauth_token" required:"true"`
Packit 63bb0d
Packit 63bb0d
	// OAuthTokenSecret is the OAuth1 Request Token Secret. Used to generate
Packit 63bb0d
	// an OAuth1 request signature.
Packit 63bb0d
	OAuthTokenSecret string `required:"true"`
Packit 63bb0d
Packit 63bb0d
	// OAuthSignatureMethod is the OAuth1 signature method the Consumer used
Packit 63bb0d
	// to sign the request. Supported values are "HMAC-SHA1" or "PLAINTEXT".
Packit 63bb0d
	// "PLAINTEXT" is not recommended for production usage.
Packit 63bb0d
	OAuthSignatureMethod SignatureMethod `q:"oauth_signature_method" required:"true"`
Packit 63bb0d
Packit 63bb0d
	// OAuthTimestamp is an OAuth1 request timestamp. If nil, current Unix
Packit 63bb0d
	// timestamp will be used.
Packit 63bb0d
	OAuthTimestamp *time.Time
Packit 63bb0d
Packit 63bb0d
	// OAuthNonce is an OAuth1 request nonce. Nonce must be a random string,
Packit 63bb0d
	// uniquely generated for each request. Will be generated automatically
Packit 63bb0d
	// when it is not set.
Packit 63bb0d
	OAuthNonce string `q:"oauth_nonce"`
Packit 63bb0d
Packit 63bb0d
	// AllowReauth allows Gophercloud to re-authenticate automatically
Packit 63bb0d
	// if/when your token expires.
Packit 63bb0d
	AllowReauth bool
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// ToTokenV3HeadersMap builds the headers required for an OAuth1-based create
Packit 63bb0d
// request.
Packit 63bb0d
func (opts AuthOptions) ToTokenV3HeadersMap(headerOpts map[string]interface{}) (map[string]string, error) {
Packit 63bb0d
	q, err := buildOAuth1QueryString(opts, opts.OAuthTimestamp, "")
Packit 63bb0d
	if err != nil {
Packit 63bb0d
		return nil, err
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	signatureKeys := []string{opts.OAuthConsumerSecret, opts.OAuthTokenSecret}
Packit 63bb0d
Packit 63bb0d
	method := headerOpts["method"].(string)
Packit 63bb0d
	u := headerOpts["url"].(string)
Packit 63bb0d
	stringToSign := buildStringToSign(method, u, q.Query())
Packit 63bb0d
	signature := url.QueryEscape(signString(opts.OAuthSignatureMethod, stringToSign, signatureKeys))
Packit 63bb0d
Packit 63bb0d
	authHeader := buildAuthHeader(q.Query(), signature)
Packit 63bb0d
Packit 63bb0d
	headers := map[string]string{
Packit 63bb0d
		"Authorization": authHeader,
Packit 63bb0d
		"X-Auth-Token":  "",
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	return headers, nil
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// ToTokenV3ScopeMap allows AuthOptions to satisfy the tokens.AuthOptionsBuilder
Packit 63bb0d
// interface.
Packit 63bb0d
func (opts AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) {
Packit 63bb0d
	return nil, nil
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// CanReauth allows AuthOptions to satisfy the tokens.AuthOptionsBuilder
Packit 63bb0d
// interface.
Packit 63bb0d
func (opts AuthOptions) CanReauth() bool {
Packit 63bb0d
	return opts.AllowReauth
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// ToTokenV3CreateMap builds a create request body.
Packit 63bb0d
func (opts AuthOptions) ToTokenV3CreateMap(map[string]interface{}) (map[string]interface{}, error) {
Packit 63bb0d
	// identityReq defines the "identity" portion of an OAuth1-based authentication
Packit 63bb0d
	// create request body.
Packit 63bb0d
	type identityReq struct {
Packit 63bb0d
		Methods []string `json:"methods"`
Packit 63bb0d
		OAuth1  struct{} `json:"oauth1"`
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	// authReq defines the "auth" portion of an OAuth1-based authentication
Packit 63bb0d
	// create request body.
Packit 63bb0d
	type authReq struct {
Packit 63bb0d
		Identity identityReq `json:"identity"`
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	// oauth1Request defines how  an OAuth1-based authentication create
Packit 63bb0d
	// request body looks.
Packit 63bb0d
	type oauth1Request struct {
Packit 63bb0d
		Auth authReq `json:"auth"`
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	var req oauth1Request
Packit 63bb0d
Packit 63bb0d
	req.Auth.Identity.Methods = []string{"oauth1"}
Packit 63bb0d
	return gophercloud.BuildRequestBody(req, "")
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// Create authenticates and either generates a new OpenStack token from an
Packit 63bb0d
// OAuth1 token.
Packit 63bb0d
func Create(client *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) (r tokens.CreateResult) {
Packit 63bb0d
	b, err := opts.ToTokenV3CreateMap(nil)
Packit 63bb0d
	if err != nil {
Packit 63bb0d
		r.Err = err
Packit 63bb0d
		return
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	headerOpts := map[string]interface{}{
Packit 63bb0d
		"method": "POST",
Packit 63bb0d
		"url":    authURL(client),
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	h, err := opts.ToTokenV3HeadersMap(headerOpts)
Packit 63bb0d
	if err != nil {
Packit 63bb0d
		r.Err = err
Packit 63bb0d
		return
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	resp, err := client.Post(authURL(client), b, &r.Body, &gophercloud.RequestOpts{
Packit 63bb0d
		MoreHeaders: h,
Packit 63bb0d
		OkCodes:     []int{201},
Packit 63bb0d
	})
Packit 63bb0d
	_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
Packit 63bb0d
	return
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// CreateConsumerOptsBuilder allows extensions to add additional parameters to
Packit 63bb0d
// the CreateConsumer request.
Packit 63bb0d
type CreateConsumerOptsBuilder interface {
Packit 63bb0d
	ToOAuth1CreateConsumerMap() (map[string]interface{}, error)
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// CreateConsumerOpts provides options used to create a new Consumer.
Packit 63bb0d
type CreateConsumerOpts struct {
Packit 63bb0d
	// Description is the consumer description.
Packit 63bb0d
	Description string `json:"description"`
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// ToOAuth1CreateConsumerMap formats a CreateConsumerOpts into a create request.
Packit 63bb0d
func (opts CreateConsumerOpts) ToOAuth1CreateConsumerMap() (map[string]interface{}, error) {
Packit 63bb0d
	return gophercloud.BuildRequestBody(opts, "consumer")
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// Create creates a new Consumer.
Packit 63bb0d
func CreateConsumer(client *gophercloud.ServiceClient, opts CreateConsumerOptsBuilder) (r CreateConsumerResult) {
Packit 63bb0d
	b, err := opts.ToOAuth1CreateConsumerMap()
Packit 63bb0d
	if err != nil {
Packit 63bb0d
		r.Err = err
Packit 63bb0d
		return
Packit 63bb0d
	}
Packit 63bb0d
	resp, err := client.Post(consumersURL(client), b, &r.Body, &gophercloud.RequestOpts{
Packit 63bb0d
		OkCodes: []int{201},
Packit 63bb0d
	})
Packit 63bb0d
	_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
Packit 63bb0d
	return
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// Delete deletes a Consumer.
Packit 63bb0d
func DeleteConsumer(client *gophercloud.ServiceClient, id string) (r DeleteConsumerResult) {
Packit 63bb0d
	resp, err := client.Delete(consumerURL(client, id), nil)
Packit 63bb0d
	_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
Packit 63bb0d
	return
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// List enumerates Consumers.
Packit 63bb0d
func ListConsumers(client *gophercloud.ServiceClient) pagination.Pager {
Packit 63bb0d
	return pagination.NewPager(client, consumersURL(client), func(r pagination.PageResult) pagination.Page {
Packit 63bb0d
		return ConsumersPage{pagination.LinkedPageBase{PageResult: r}}
Packit 63bb0d
	})
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// GetConsumer retrieves details on a single Consumer by ID.
Packit 63bb0d
func GetConsumer(client *gophercloud.ServiceClient, id string) (r GetConsumerResult) {
Packit 63bb0d
	resp, err := client.Get(consumerURL(client, id), &r.Body, nil)
Packit 63bb0d
	_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
Packit 63bb0d
	return
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// UpdateConsumerOpts provides options used to update a consumer.
Packit 63bb0d
type UpdateConsumerOpts struct {
Packit 63bb0d
	// Description is the consumer description.
Packit 63bb0d
	Description string `json:"description"`
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// ToOAuth1UpdateConsumerMap formats an UpdateConsumerOpts into a consumer update
Packit 63bb0d
// request.
Packit 63bb0d
func (opts UpdateConsumerOpts) ToOAuth1UpdateConsumerMap() (map[string]interface{}, error) {
Packit 63bb0d
	return gophercloud.BuildRequestBody(opts, "consumer")
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// UpdateConsumer updates an existing Consumer.
Packit 63bb0d
func UpdateConsumer(client *gophercloud.ServiceClient, id string, opts UpdateConsumerOpts) (r UpdateConsumerResult) {
Packit 63bb0d
	b, err := opts.ToOAuth1UpdateConsumerMap()
Packit 63bb0d
	if err != nil {
Packit 63bb0d
		r.Err = err
Packit 63bb0d
		return
Packit 63bb0d
	}
Packit 63bb0d
	resp, err := client.Patch(consumerURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
Packit 63bb0d
		OkCodes: []int{200},
Packit 63bb0d
	})
Packit 63bb0d
	_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
Packit 63bb0d
	return
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// RequestTokenOptsBuilder allows extensions to add additional parameters to the
Packit 63bb0d
// RequestToken request.
Packit 63bb0d
type RequestTokenOptsBuilder interface {
Packit 63bb0d
	ToOAuth1RequestTokenHeaders(string, string) (map[string]string, error)
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// RequestTokenOpts provides options used to get a consumer unauthorized
Packit 63bb0d
// request token.
Packit 63bb0d
type RequestTokenOpts struct {
Packit 63bb0d
	// OAuthConsumerKey is the OAuth1 Consumer Key.
Packit 63bb0d
	OAuthConsumerKey string `q:"oauth_consumer_key" required:"true"`
Packit 63bb0d
Packit 63bb0d
	// OAuthConsumerSecret is the OAuth1 Consumer Secret. Used to generate
Packit 63bb0d
	// an OAuth1 request signature.
Packit 63bb0d
	OAuthConsumerSecret string `required:"true"`
Packit 63bb0d
Packit 63bb0d
	// OAuthSignatureMethod is the OAuth1 signature method the Consumer used
Packit 63bb0d
	// to sign the request. Supported values are "HMAC-SHA1" or "PLAINTEXT".
Packit 63bb0d
	// "PLAINTEXT" is not recommended for production usage.
Packit 63bb0d
	OAuthSignatureMethod SignatureMethod `q:"oauth_signature_method" required:"true"`
Packit 63bb0d
Packit 63bb0d
	// OAuthTimestamp is an OAuth1 request timestamp. If nil, current Unix
Packit 63bb0d
	// timestamp will be used.
Packit 63bb0d
	OAuthTimestamp *time.Time
Packit 63bb0d
Packit 63bb0d
	// OAuthNonce is an OAuth1 request nonce. Nonce must be a random string,
Packit 63bb0d
	// uniquely generated for each request. Will be generated automatically
Packit 63bb0d
	// when it is not set.
Packit 63bb0d
	OAuthNonce string `q:"oauth_nonce"`
Packit 63bb0d
Packit 63bb0d
	// RequestedProjectID is a Project ID a consumer user requested an
Packit 63bb0d
	// access to.
Packit 63bb0d
	RequestedProjectID string `h:"Requested-Project-Id"`
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// ToOAuth1RequestTokenHeaders formats a RequestTokenOpts into a map of request
Packit 63bb0d
// headers.
Packit 63bb0d
func (opts RequestTokenOpts) ToOAuth1RequestTokenHeaders(method, u string) (map[string]string, error) {
Packit 63bb0d
	q, err := buildOAuth1QueryString(opts, opts.OAuthTimestamp, "oob")
Packit 63bb0d
	if err != nil {
Packit 63bb0d
		return nil, err
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	h, err := gophercloud.BuildHeaders(opts)
Packit 63bb0d
	if err != nil {
Packit 63bb0d
		return nil, err
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	signatureKeys := []string{opts.OAuthConsumerSecret}
Packit 63bb0d
	stringToSign := buildStringToSign(method, u, q.Query())
Packit 63bb0d
	signature := url.QueryEscape(signString(opts.OAuthSignatureMethod, stringToSign, signatureKeys))
Packit 63bb0d
	authHeader := buildAuthHeader(q.Query(), signature)
Packit 63bb0d
Packit 63bb0d
	h["Authorization"] = authHeader
Packit 63bb0d
Packit 63bb0d
	return h, nil
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// RequestToken requests an unauthorized OAuth1 Token.
Packit 63bb0d
func RequestToken(client *gophercloud.ServiceClient, opts RequestTokenOptsBuilder) (r TokenResult) {
Packit 63bb0d
	h, err := opts.ToOAuth1RequestTokenHeaders("POST", requestTokenURL(client))
Packit 63bb0d
	if err != nil {
Packit 63bb0d
		r.Err = err
Packit 63bb0d
		return
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	resp, err := client.Post(requestTokenURL(client), nil, nil, &gophercloud.RequestOpts{
Packit 63bb0d
		MoreHeaders:      h,
Packit 63bb0d
		OkCodes:          []int{201},
Packit 63bb0d
		KeepResponseBody: true,
Packit 63bb0d
	})
Packit 63bb0d
	_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
Packit 63bb0d
	if r.Err != nil {
Packit 63bb0d
		return
Packit 63bb0d
	}
Packit 63bb0d
	defer resp.Body.Close()
Packit 63bb0d
	if v := r.Header.Get("Content-Type"); v != OAuth1TokenContentType {
Packit 63bb0d
		r.Err = fmt.Errorf("unsupported Content-Type: %q", v)
Packit 63bb0d
		return
Packit 63bb0d
	}
Packit 63bb0d
	r.Body, r.Err = ioutil.ReadAll(resp.Body)
Packit 63bb0d
	return
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// AuthorizeTokenOptsBuilder allows extensions to add additional parameters to
Packit 63bb0d
// the AuthorizeToken request.
Packit 63bb0d
type AuthorizeTokenOptsBuilder interface {
Packit 63bb0d
	ToOAuth1AuthorizeTokenMap() (map[string]interface{}, error)
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// AuthorizeTokenOpts provides options used to authorize a request token.
Packit 63bb0d
type AuthorizeTokenOpts struct {
Packit 63bb0d
	Roles []Role `json:"roles"`
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// Role is a struct representing a role object in a AuthorizeTokenOpts struct.
Packit 63bb0d
type Role struct {
Packit 63bb0d
	ID   string `json:"id,omitempty"`
Packit 63bb0d
	Name string `json:"name,omitempty"`
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// ToOAuth1AuthorizeTokenMap formats an AuthorizeTokenOpts into an authorize token
Packit 63bb0d
// request.
Packit 63bb0d
func (opts AuthorizeTokenOpts) ToOAuth1AuthorizeTokenMap() (map[string]interface{}, error) {
Packit 63bb0d
	for _, r := range opts.Roles {
Packit 63bb0d
		if r == (Role{}) {
Packit 63bb0d
			return nil, fmt.Errorf("role must not be empty")
Packit 63bb0d
		}
Packit 63bb0d
	}
Packit 63bb0d
	return gophercloud.BuildRequestBody(opts, "")
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// AuthorizeToken authorizes an unauthorized consumer token.
Packit 63bb0d
func AuthorizeToken(client *gophercloud.ServiceClient, id string, opts AuthorizeTokenOptsBuilder) (r AuthorizeTokenResult) {
Packit 63bb0d
	b, err := opts.ToOAuth1AuthorizeTokenMap()
Packit 63bb0d
	if err != nil {
Packit 63bb0d
		r.Err = err
Packit 63bb0d
		return
Packit 63bb0d
	}
Packit 63bb0d
	resp, err := client.Put(authorizeTokenURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
Packit 63bb0d
		OkCodes: []int{200},
Packit 63bb0d
	})
Packit 63bb0d
	_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
Packit 63bb0d
	return
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// CreateAccessTokenOptsBuilder allows extensions to add additional parameters
Packit 63bb0d
// to the CreateAccessToken request.
Packit 63bb0d
type CreateAccessTokenOptsBuilder interface {
Packit 63bb0d
	ToOAuth1CreateAccessTokenHeaders(string, string) (map[string]string, error)
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// CreateAccessTokenOpts provides options used to create an OAuth1 token.
Packit 63bb0d
type CreateAccessTokenOpts struct {
Packit 63bb0d
	// OAuthConsumerKey is the OAuth1 Consumer Key.
Packit 63bb0d
	OAuthConsumerKey string `q:"oauth_consumer_key" required:"true"`
Packit 63bb0d
Packit 63bb0d
	// OAuthConsumerSecret is the OAuth1 Consumer Secret. Used to generate
Packit 63bb0d
	// an OAuth1 request signature.
Packit 63bb0d
	OAuthConsumerSecret string `required:"true"`
Packit 63bb0d
Packit 63bb0d
	// OAuthToken is the OAuth1 Request Token.
Packit 63bb0d
	OAuthToken string `q:"oauth_token" required:"true"`
Packit 63bb0d
Packit 63bb0d
	// OAuthTokenSecret is the OAuth1 Request Token Secret. Used to generate
Packit 63bb0d
	// an OAuth1 request signature.
Packit 63bb0d
	OAuthTokenSecret string `required:"true"`
Packit 63bb0d
Packit 63bb0d
	// OAuthVerifier is the OAuth1 verification code.
Packit 63bb0d
	OAuthVerifier string `q:"oauth_verifier" required:"true"`
Packit 63bb0d
Packit 63bb0d
	// OAuthSignatureMethod is the OAuth1 signature method the Consumer used
Packit 63bb0d
	// to sign the request. Supported values are "HMAC-SHA1" or "PLAINTEXT".
Packit 63bb0d
	// "PLAINTEXT" is not recommended for production usage.
Packit 63bb0d
	OAuthSignatureMethod SignatureMethod `q:"oauth_signature_method" required:"true"`
Packit 63bb0d
Packit 63bb0d
	// OAuthTimestamp is an OAuth1 request timestamp. If nil, current Unix
Packit 63bb0d
	// timestamp will be used.
Packit 63bb0d
	OAuthTimestamp *time.Time
Packit 63bb0d
Packit 63bb0d
	// OAuthNonce is an OAuth1 request nonce. Nonce must be a random string,
Packit 63bb0d
	// uniquely generated for each request. Will be generated automatically
Packit 63bb0d
	// when it is not set.
Packit 63bb0d
	OAuthNonce string `q:"oauth_nonce"`
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// ToOAuth1CreateAccessTokenHeaders formats a CreateAccessTokenOpts into a map of
Packit 63bb0d
// request headers.
Packit 63bb0d
func (opts CreateAccessTokenOpts) ToOAuth1CreateAccessTokenHeaders(method, u string) (map[string]string, error) {
Packit 63bb0d
	q, err := buildOAuth1QueryString(opts, opts.OAuthTimestamp, "")
Packit 63bb0d
	if err != nil {
Packit 63bb0d
		return nil, err
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	signatureKeys := []string{opts.OAuthConsumerSecret, opts.OAuthTokenSecret}
Packit 63bb0d
	stringToSign := buildStringToSign(method, u, q.Query())
Packit 63bb0d
	signature := url.QueryEscape(signString(opts.OAuthSignatureMethod, stringToSign, signatureKeys))
Packit 63bb0d
	authHeader := buildAuthHeader(q.Query(), signature)
Packit 63bb0d
Packit 63bb0d
	headers := map[string]string{
Packit 63bb0d
		"Authorization": authHeader,
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	return headers, nil
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// CreateAccessToken creates a new OAuth1 Access Token
Packit 63bb0d
func CreateAccessToken(client *gophercloud.ServiceClient, opts CreateAccessTokenOptsBuilder) (r TokenResult) {
Packit 63bb0d
	h, err := opts.ToOAuth1CreateAccessTokenHeaders("POST", createAccessTokenURL(client))
Packit 63bb0d
	if err != nil {
Packit 63bb0d
		r.Err = err
Packit 63bb0d
		return
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	resp, err := client.Post(createAccessTokenURL(client), nil, nil, &gophercloud.RequestOpts{
Packit 63bb0d
		MoreHeaders:      h,
Packit 63bb0d
		OkCodes:          []int{201},
Packit 63bb0d
		KeepResponseBody: true,
Packit 63bb0d
	})
Packit 63bb0d
	_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
Packit 63bb0d
	if r.Err != nil {
Packit 63bb0d
		return
Packit 63bb0d
	}
Packit 63bb0d
	defer resp.Body.Close()
Packit 63bb0d
	if v := r.Header.Get("Content-Type"); v != OAuth1TokenContentType {
Packit 63bb0d
		r.Err = fmt.Errorf("unsupported Content-Type: %q", v)
Packit 63bb0d
		return
Packit 63bb0d
	}
Packit 63bb0d
	r.Body, r.Err = ioutil.ReadAll(resp.Body)
Packit 63bb0d
	return
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// GetAccessToken retrieves details on a single OAuth1 access token by an ID.
Packit 63bb0d
func GetAccessToken(client *gophercloud.ServiceClient, userID string, id string) (r GetAccessTokenResult) {
Packit 63bb0d
	resp, err := client.Get(userAccessTokenURL(client, userID, id), &r.Body, nil)
Packit 63bb0d
	_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
Packit 63bb0d
	return
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// RevokeAccessToken revokes an OAuth1 access token.
Packit 63bb0d
func RevokeAccessToken(client *gophercloud.ServiceClient, userID string, id string) (r RevokeAccessTokenResult) {
Packit 63bb0d
	resp, err := client.Delete(userAccessTokenURL(client, userID, id), nil)
Packit 63bb0d
	_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
Packit 63bb0d
	return
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// ListAccessTokens enumerates authorized access tokens.
Packit 63bb0d
func ListAccessTokens(client *gophercloud.ServiceClient, userID string) pagination.Pager {
Packit 63bb0d
	url := userAccessTokensURL(client, userID)
Packit 63bb0d
	return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
Packit 63bb0d
		return AccessTokensPage{pagination.LinkedPageBase{PageResult: r}}
Packit 63bb0d
	})
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// ListAccessTokenRoles enumerates authorized access token roles.
Packit 63bb0d
func ListAccessTokenRoles(client *gophercloud.ServiceClient, userID string, id string) pagination.Pager {
Packit 63bb0d
	url := userAccessTokenRolesURL(client, userID, id)
Packit 63bb0d
	return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
Packit 63bb0d
		return AccessTokenRolesPage{pagination.LinkedPageBase{PageResult: r}}
Packit 63bb0d
	})
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// GetAccessTokenRole retrieves details on a single OAuth1 access token role by
Packit 63bb0d
// an ID.
Packit 63bb0d
func GetAccessTokenRole(client *gophercloud.ServiceClient, userID string, id string, roleID string) (r GetAccessTokenRoleResult) {
Packit 63bb0d
	resp, err := client.Get(userAccessTokenRoleURL(client, userID, id, roleID), &r.Body, nil)
Packit 63bb0d
	_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
Packit 63bb0d
	return
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// The following are small helper functions used to help build the signature.
Packit 63bb0d
Packit 63bb0d
// buildOAuth1QueryString builds a URLEncoded parameters string specific for
Packit 63bb0d
// OAuth1-based requests.
Packit 63bb0d
func buildOAuth1QueryString(opts interface{}, timestamp *time.Time, callback string) (*url.URL, error) {
Packit 63bb0d
	q, err := gophercloud.BuildQueryString(opts)
Packit 63bb0d
	if err != nil {
Packit 63bb0d
		return nil, err
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	query := q.Query()
Packit 63bb0d
Packit 63bb0d
	if timestamp != nil {
Packit 63bb0d
		// use provided timestamp
Packit 63bb0d
		query.Set("oauth_timestamp", strconv.FormatInt(timestamp.Unix(), 10))
Packit 63bb0d
	} else {
Packit 63bb0d
		// use current timestamp
Packit 63bb0d
		query.Set("oauth_timestamp", strconv.FormatInt(time.Now().UTC().Unix(), 10))
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	if query.Get("oauth_nonce") == "" {
Packit 63bb0d
		// when nonce is not set, generate a random one
Packit 63bb0d
		query.Set("oauth_nonce", strconv.FormatInt(rand.Int63(), 10)+query.Get("oauth_timestamp"))
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	if callback != "" {
Packit 63bb0d
		query.Set("oauth_callback", callback)
Packit 63bb0d
	}
Packit 63bb0d
	query.Set("oauth_version", "1.0")
Packit 63bb0d
Packit 63bb0d
	return &url.URL{RawQuery: query.Encode()}, nil
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// buildStringToSign builds a string to be signed.
Packit 63bb0d
func buildStringToSign(method string, u string, query url.Values) []byte {
Packit 63bb0d
	parsedURL, _ := url.Parse(u)
Packit 63bb0d
	p := parsedURL.Port()
Packit 63bb0d
	s := parsedURL.Scheme
Packit 63bb0d
Packit 63bb0d
	// Default scheme port must be stripped
Packit 63bb0d
	if s == "http" && p == "80" || s == "https" && p == "443" {
Packit 63bb0d
		parsedURL.Host = strings.TrimSuffix(parsedURL.Host, ":"+p)
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	// Ensure that URL doesn't contain queries
Packit 63bb0d
	parsedURL.RawQuery = ""
Packit 63bb0d
Packit 63bb0d
	v := strings.Join(
Packit 63bb0d
		[]string{method, url.QueryEscape(parsedURL.String()), url.QueryEscape(query.Encode())}, "&")
Packit 63bb0d
Packit 63bb0d
	return []byte(v)
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// signString signs a string using an OAuth1 signature method.
Packit 63bb0d
func signString(signatureMethod SignatureMethod, strToSign []byte, signatureKeys []string) string {
Packit 63bb0d
	var key []byte
Packit 63bb0d
	for i, k := range signatureKeys {
Packit 63bb0d
		key = append(key, []byte(url.QueryEscape(k))...)
Packit 63bb0d
		if i == 0 {
Packit 63bb0d
			key = append(key, '&')
Packit 63bb0d
		}
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	var signedString string
Packit 63bb0d
	switch signatureMethod {
Packit 63bb0d
	case PLAINTEXT:
Packit 63bb0d
		signedString = string(key)
Packit 63bb0d
	default:
Packit 63bb0d
		h := hmac.New(sha1.New, key)
Packit 63bb0d
		h.Write(strToSign)
Packit 63bb0d
		signedString = base64.StdEncoding.EncodeToString(h.Sum(nil))
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	return signedString
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// buildAuthHeader generates an OAuth1 Authorization header with a signature
Packit 63bb0d
// calculated using an OAuth1 signature method.
Packit 63bb0d
func buildAuthHeader(query url.Values, signature string) string {
Packit 63bb0d
	var authHeader []string
Packit 63bb0d
	var keys []string
Packit 63bb0d
	for k := range query {
Packit 63bb0d
		keys = append(keys, k)
Packit 63bb0d
	}
Packit 63bb0d
	sort.Strings(keys)
Packit 63bb0d
Packit 63bb0d
	for _, k := range keys {
Packit 63bb0d
		for _, v := range query[k] {
Packit 63bb0d
			authHeader = append(authHeader, fmt.Sprintf("%s=%q", k, url.QueryEscape(v)))
Packit 63bb0d
		}
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	authHeader = append(authHeader, fmt.Sprintf("oauth_signature=%q", signature))
Packit 63bb0d
Packit 63bb0d
	return "OAuth " + strings.Join(authHeader, ", ")
Packit 63bb0d
}