github.com/hoffie/larasync@v0.0.0-20151025221940-0384d2bddcef/api/client/authorizationUrl.go (about)

     1  package client
     2  
     3  import (
     4  	"encoding/hex"
     5  	"errors"
     6  	"fmt"
     7  	"net/url"
     8  	"regexp"
     9  
    10  	edhelpers "github.com/hoffie/larasync/helpers/ed25519"
    11  	"github.com/hoffie/larasync/repository"
    12  )
    13  
    14  var (
    15  	signKeyRegexp     = regexp.MustCompile("AuthSignKey=(?P<key>[^&]+)")
    16  	encKeyRegexp      = regexp.MustCompile("AuthEncKey=(?P<key>[^&]+)")
    17  	fingerprintRegexp = regexp.MustCompile("Fingerprint=(?P<key>[^&]+)")
    18  )
    19  
    20  // AuthorizationURL is used to pass and create a authorizations
    21  // for registering against a new server.
    22  type AuthorizationURL struct {
    23  	URL         *url.URL
    24  	SignKey     [PrivateKeySize]byte
    25  	EncKey      [repository.EncryptionKeySize]byte
    26  	Fingerprint string
    27  }
    28  
    29  // parseAuthURL takes a URL and tries to extract the encryption key
    30  // and the auhtorization key from the fragment.
    31  func parseAuthURL(URL *url.URL) (*AuthorizationURL, error) {
    32  	authURL := &AuthorizationURL{}
    33  	err := authURL.parse(URL)
    34  	if err != nil {
    35  		return nil, err
    36  	}
    37  	return authURL, nil
    38  }
    39  
    40  // parseAuthURLString takes a string URL and uses parseAuthURL on it to derive
    41  // an AuthorizationURL object.
    42  func parseAuthURLString(urlString string) (*AuthorizationURL, error) {
    43  	u, err := url.Parse(urlString)
    44  	if err != nil {
    45  		return nil, fmt.Errorf("unparsable url (%s)", err)
    46  	}
    47  	return parseAuthURL(u)
    48  }
    49  
    50  // NewAuthURL generates a new authorization URL with the passed
    51  // arguments.
    52  func NewAuthURL(repositoryBaseURL string,
    53  	signingPrivKey *[PrivateKeySize]byte,
    54  	encryptionKey *[repository.EncryptionKeySize]byte,
    55  	fingerprint string) (*AuthorizationURL, error) {
    56  
    57  	pubKey := edhelpers.GetPublicKeyFromPrivate(*signingPrivKey)
    58  	pubKeyString := hex.EncodeToString(pubKey[:])
    59  
    60  	u, err := url.Parse(fmt.Sprintf("%s/authorizations/%s", repositoryBaseURL, pubKeyString))
    61  	if err != nil {
    62  		return nil, err
    63  	}
    64  
    65  	authURL := &AuthorizationURL{
    66  		SignKey:     *signingPrivKey,
    67  		EncKey:      *encryptionKey,
    68  		Fingerprint: fingerprint,
    69  		URL:         u,
    70  	}
    71  	return authURL, nil
    72  }
    73  
    74  // SignKeyString returns the authorization key encoded as hex.
    75  func (a *AuthorizationURL) SignKeyString() string {
    76  	return hex.EncodeToString(a.SignKey[:])
    77  }
    78  
    79  // EncKeyString returns the encryption key encoded as hex.
    80  func (a *AuthorizationURL) EncKeyString() string {
    81  	return hex.EncodeToString(a.EncKey[:])
    82  }
    83  
    84  // String formats the AuthorizationURL which should be passed to
    85  // the new client to authorize.
    86  func (a *AuthorizationURL) String() string {
    87  	return fmt.Sprintf("%s#AuthEncKey=%s&AuthSignKey=%s&Fingerprint=%s",
    88  		a.URL.String(), a.EncKeyString(), a.SignKeyString(), a.Fingerprint)
    89  }
    90  
    91  func (a *AuthorizationURL) parse(URL *url.URL) error {
    92  	authData := URL.Fragment
    93  	URL.Fragment = ""
    94  
    95  	encKey, err := a.parseForEncKey(authData)
    96  	if err != nil {
    97  		return err
    98  	}
    99  
   100  	signKey, err := a.parseForSignKey(authData)
   101  	if err != nil {
   102  		return err
   103  	}
   104  
   105  	fingerprint, err := a.parseForFingerprint(authData)
   106  	if err != nil {
   107  		return err
   108  	}
   109  
   110  	a.URL = URL
   111  	a.SignKey = signKey
   112  	a.EncKey = encKey
   113  	a.Fingerprint = fingerprint
   114  	return nil
   115  }
   116  
   117  // parseForEncKey tries to extract the encryption key.
   118  func (a *AuthorizationURL) parseForEncKey(data string) ([repository.EncryptionKeySize]byte, error) {
   119  	encKeySlice, err := a.parseForKey(data, encKeyRegexp)
   120  	encKey := [repository.EncryptionKeySize]byte{}
   121  	if err != nil {
   122  		return encKey, errors.New("Could not retrieve encryption key.")
   123  	}
   124  	if len(encKeySlice) != repository.EncryptionKeySize {
   125  		return encKey, errors.New("Invalid encryption key size.")
   126  	}
   127  
   128  	copy(encKey[:], encKeySlice)
   129  	return encKey, nil
   130  }
   131  
   132  // parseForSignKey tries to extract the signing key.
   133  func (a *AuthorizationURL) parseForSignKey(data string) ([PrivateKeySize]byte, error) {
   134  	signKeySlice, err := a.parseForKey(data, signKeyRegexp)
   135  	signKey := [PrivateKeySize]byte{}
   136  	if err != nil {
   137  		return signKey, errors.New("Could not retrieve signing key.")
   138  	}
   139  	if len(signKeySlice) != PrivateKeySize {
   140  		return signKey, errors.New("Invalid signature key size.")
   141  	}
   142  
   143  	copy(signKey[:], signKeySlice)
   144  	return signKey, nil
   145  }
   146  
   147  // parseForFingerprint tries to extract the fingerprint
   148  func (a *AuthorizationURL) parseForFingerprint(data string) (string, error) {
   149  	matches := fingerprintRegexp.FindStringSubmatch(data)
   150  	if len(matches) < 2 {
   151  		return "", errors.New("Could not parse fingerprint")
   152  	}
   153  	return string(matches[1]), nil
   154  }
   155  
   156  // parseForKey tries to parse a key from the data string and parses it with the given regexp.
   157  func (a *AuthorizationURL) parseForKey(data string, r *regexp.Regexp) ([]byte, error) {
   158  	keyMatches := r.FindStringSubmatch(data)
   159  	extractionError := errors.New("Could not extract key.")
   160  
   161  	if len(keyMatches) < 2 {
   162  		return nil, extractionError
   163  	}
   164  	keyString := keyMatches[1]
   165  	keySlice, err := hex.DecodeString(keyString)
   166  	if err != nil {
   167  		return nil, extractionError
   168  	}
   169  	return keySlice, nil
   170  }