blob: 920960de878b4190987621de9c19fc5f7cf1443d [file] [log] [blame]
// Copyright 2012-2016 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package gomaasapi
import (
"crypto/rand"
"fmt"
"net/http"
"net/url"
"strconv"
"strings"
"time"
)
// Not a true uuidgen, but at least creates same length random
func generateNonce() (string, error) {
randBytes := make([]byte, 16)
_, err := rand.Read(randBytes)
if err != nil {
return "", err
}
return fmt.Sprintf("%16x", randBytes), nil
}
func generateTimestamp() string {
return strconv.Itoa(int(time.Now().Unix()))
}
type OAuthSigner interface {
OAuthSign(request *http.Request) error
}
type OAuthToken struct {
ConsumerKey string
ConsumerSecret string
TokenKey string
TokenSecret string
}
// Trick to ensure *plainTextOAuthSigner implements the OAuthSigner interface.
var _ OAuthSigner = (*plainTextOAuthSigner)(nil)
type plainTextOAuthSigner struct {
token *OAuthToken
realm string
}
func NewPlainTestOAuthSigner(token *OAuthToken, realm string) (OAuthSigner, error) {
return &plainTextOAuthSigner{token, realm}, nil
}
// OAuthSignPLAINTEXT signs the provided request using the OAuth PLAINTEXT
// method: http://oauth.net/core/1.0/#anchor22.
func (signer plainTextOAuthSigner) OAuthSign(request *http.Request) error {
signature := signer.token.ConsumerSecret + `&` + signer.token.TokenSecret
nonce, err := generateNonce()
if err != nil {
return err
}
authData := map[string]string{
"realm": signer.realm,
"oauth_consumer_key": signer.token.ConsumerKey,
"oauth_token": signer.token.TokenKey,
"oauth_signature_method": "PLAINTEXT",
"oauth_signature": signature,
"oauth_timestamp": generateTimestamp(),
"oauth_nonce": nonce,
"oauth_version": "1.0",
}
// Build OAuth header.
var authHeader []string
for key, value := range authData {
authHeader = append(authHeader, fmt.Sprintf(`%s="%s"`, key, url.QueryEscape(value)))
}
strHeader := "OAuth " + strings.Join(authHeader, ", ")
request.Header.Add("Authorization", strHeader)
return nil
}