shithub: jirafs

ref: 6d908ded15fff42e38d317dc9cbb677e27ea1b5e
dir: /client.go/

View raw version
package main

import (
	"bytes"
	"crypto/tls"
	"crypto/x509"
	"encoding/json"
	"encoding/pem"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"

	"github.com/mrjones/oauth"
)

type Client struct {
	*http.Client

	user, pass              string
	jiraURL                 *url.URL
	cookies                 []*http.Cookie
	alwaysLogin, usingOAuth bool

	maxlisting int
}

type RPCError struct {
	Status      string
	Body        []byte
	Description string
}

func (rpc *RPCError) Error() string {
	return fmt.Sprintf("RPCError: %s: status %s, %s", rpc.Description, rpc.Status, rpc.Body)
}

func (c *Client) RPC(method, path string, body, target interface{}) error {
	u, err := c.jiraURL.Parse(path)
	if err != nil {
		return err
	}

	var b io.Reader
	switch x := body.(type) {
	case nil:
	case []byte:
		b = bytes.NewReader(x)
	default:
		buf, err := json.Marshal(body)
		if err != nil {
			return err
		}
		b = bytes.NewReader(buf)
	}

	req, err := http.NewRequest(method, u.String(), b)
	if err != nil {
		return err
	}

	if body != nil {
		req.Header.Set("Content-Type", "application/json")
	}
	req.Header.Set("X-Atlassian-Token", "nocheck")

	if c.alwaysLogin && !c.usingOAuth {
		if err := c.AcquireSessionCookie(c.user, c.pass); err != nil {
			return err
		}
	}

	for _, cookie := range c.cookies {
		req.AddCookie(cookie)
	}

	resp, err := c.Client.Do(req)
	if err != nil {
		return err
	}

	respBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	resp.Body.Close()

	if !(resp.StatusCode >= 200 && resp.StatusCode <= 299) {
		err = &RPCError{
			Description: "request failed",
			Status:      resp.Status,
			Body:        respBody,
		}
		return err
	}

	if target != nil {
		if err := json.Unmarshal(respBody, target); err != nil {
			return err
		}
	}

	return nil

}

func (c *Client) AcquireSessionCookie(username, password string) error {
	url, err := c.jiraURL.Parse("/rest/auth/1/session")
	if err != nil {
		return err
	}

	body := struct {
		Username string `json:"username"`
		Password string `json:"password"`
	}{username, password}
	b, err := json.Marshal(body)
	if err != nil {
		return err
	}

	req, err := http.NewRequest("POST", url.String(), bytes.NewReader(b))
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", "application/json")

	resp, err := c.Client.Do(req)
	if _, err := ioutil.ReadAll(resp.Body); err != nil {
		return err
	}
	resp.Body.Close()
	c.cookies = resp.Cookies()

	if err != nil {
		return fmt.Errorf("Auth at JIRA instance failed (HTTP(S) request). %s", err)
	}
	if resp != nil && resp.StatusCode != 200 {
		return fmt.Errorf("Auth at JIRA instance failed (HTTP(S) request). Status code: %d", resp.StatusCode)
	}

	return nil
}

func (c *Client) login() error {
	if c.usingOAuth {
		return nil
	}
	if err := c.AcquireSessionCookie(c.user, c.pass); err != nil {
		return fmt.Errorf("Could not authenticate to JIRA: %v\n", err)
	}
	return nil
}

func (c *Client) oauth(consumerKey, privateKeyFile string) error {
	pvf, err := ioutil.ReadFile(privateKeyFile)
	if err != nil {
		return err
	}

	block, _ := pem.Decode(pvf)
	privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
	if err != nil {
		return err
	}

	url1, _ := c.jiraURL.Parse("/plugins/servlet/oauth/request-token")
	url2, _ := c.jiraURL.Parse("/plugins/servlet/oauth/authorize")
	url3, _ := c.jiraURL.Parse("/plugins/servlet/oauth/access-token")

	t := oauth.NewRSAConsumer(
		consumerKey,
		privateKey,
		oauth.ServiceProvider{
			RequestTokenUrl:   url1.String(),
			AuthorizeTokenUrl: url2.String(),
			AccessTokenUrl:    url3.String(),
			HttpMethod:        "POST",
		},
	)

	t.HttpClient = &http.Client{
		Transport: &http.Transport{
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
		},
	}

	requestToken, url, err := t.GetRequestTokenAndUrl("oob")
	if err != nil {
		return err
	}

	fmt.Printf("OAuth token requested. Please to go the following URL:\n\t%s\n\nEnter verification code: ", url)
	var verificationCode string
	fmt.Scanln(&verificationCode)
	accessToken, err := t.AuthorizeToken(requestToken, verificationCode)
	if err != nil {
		return err
	}
	fmt.Printf("OAuth token authorized.\n")

	client, err := t.MakeHttpClient(accessToken)
	if err != nil {
		return err
	}

	c.Client = client
	return nil
}