func (r *Realm) ProjectKeyExists(userID id.UserID, projectKey string) (bool, error) {
	cli, err := r.JIRAClient(userID, true)
	if err != nil {
		return false, err
	}
	var projects []jira.Project
	req, err := cli.NewRequest("GET", "rest/api/2/project", nil)
	if err != nil {
		return false, err
	}
	res, err := cli.Do(req, &projects)
	if err != nil {
		return false, err
	}
	if res == nil {
		return false, errors.New("No response returned")
	}
	if res.StatusCode < 200 || res.StatusCode >= 300 {
		return false, fmt.Errorf(
			"%srest/api/2/project returned code %d",
			r.JIRAEndpoint, res.StatusCode,
		)
	}
	for _, p := range projects {
		if strings.EqualFold(p.Key, projectKey) {
			return true, nil
		}
	}
	return false, nil
}
ProjectKeyExists returns true if the given project key exists on this JIRA realm.
An authenticated client for userID will be used if one exists, else an
unauthenticated client will be used, which may not be able to see the complete list
of projects.
package jira
import (
	"crypto/rsa"
	"crypto/x509"
	"database/sql"
	"encoding/json"
	"encoding/pem"
	"errors"
	"fmt"
	"net/http"
	"strings"
	jira "github.com/andygrunwald/go-jira"
	"github.com/dghubble/oauth1"
	"github.com/matrix-org/go-neb/database"
	"github.com/matrix-org/go-neb/realms/jira/urls"
	"github.com/matrix-org/go-neb/types"
	log "github.com/sirupsen/logrus"
	"golang.org/x/net/context"
	"maunium.net/go/mautrix/id"
)
const RealmType = "jira"
type Realm struct {
	id          string
	redirectURL string
	privateKey  *rsa.PrivateKey
	JIRAEndpoint string
	ConsumerName string
	ConsumerKey string
	ConsumerSecret string
	PrivateKeyPEM string
	StarterLink string
	Server string
	Version string
	PublicKeyPEM string
	HasWebhook bool
}
type Session struct {
	id      string  
	userID  id.UserID
	realmID string
	RequestSecret string
	AccessToken string
	AccessSecret string
	ClientsRedirectURL string
}
type AuthRequest struct {
	RedirectURL string
}
type AuthResponse struct {
	URL string
}
func (s *Session) Authenticated() bool {
	return s.AccessToken != "" && s.AccessSecret != ""
}
func (s *Session) Info() interface{} {
	return nil
}
func (s *Session) UserID() id.UserID {
	return s.userID
}
func (s *Session) RealmID() string {
	return s.realmID
}
func (s *Session) ID() string {
	return s.id
}
func (r *Realm) ID() string {
	return r.id
}
func (r *Realm) Type() string {
	return RealmType
}
func (r *Realm) Init() error {
	if err := r.parsePrivateKey(); err != nil {
		log.WithError(err).Print("Failed to parse private key")
		return err
	}
	ju, err := urls.ParseJIRAURL(r.JIRAEndpoint)
	if err != nil {
		log.WithError(err).Print("Failed to parse JIRA endpoint")
		return err
	}
	r.JIRAEndpoint = ju.Base
	return nil
}
func (r *Realm) Register() error {
	if r.ConsumerName == "" || r.ConsumerKey == "" || r.ConsumerSecret == "" || r.PrivateKeyPEM == "" {
		return errors.New("ConsumerName, ConsumerKey, ConsumerSecret, PrivateKeyPEM must be specified")
	}
	if r.JIRAEndpoint == "" {
		return errors.New("JIRAEndpoint must be specified")
	}
	r.HasWebhook = false  
	cli, err := r.JIRAClient("", true)
	if err != nil {
		return err
	}
	info, err := jiraServerInfo(cli)
	if err != nil {
		return err
	}
	log.WithFields(log.Fields{
		"jira_url": r.JIRAEndpoint,
		"title":    info.ServerTitle,
		"version":  info.Version,
	}).Print("Found JIRA endpoint")
	r.Server = info.ServerTitle
	r.Version = info.Version
	return nil
}
func (r *Realm) RequestAuthSession(userID id.UserID, req json.RawMessage) interface{} {
	logger := log.WithField("jira_url", r.JIRAEndpoint)
	var reqBody AuthRequest
	if err := json.Unmarshal(req, &reqBody); err != nil {
		log.WithError(err).Print("Failed to decode request body")
		return nil
	}
	authConfig := r.oauth1Config(r.JIRAEndpoint)
	reqToken, reqSec, err := authConfig.RequestToken()
	if err != nil {
		logger.WithError(err).Print("Failed to request auth token")
		return nil
	}
	logger.WithField("req_token", reqToken).Print("Received request token")
	authURL, err := authConfig.AuthorizationURL(reqToken)
	if err != nil {
		logger.WithError(err).Print("Failed to create authorization URL")
		return nil
	}
	_, err = database.GetServiceDB().StoreAuthSession(&Session{
		id:                 reqToken,
		userID:             userID,
		realmID:            r.id,
		RequestSecret:      reqSec,
		ClientsRedirectURL: reqBody.RedirectURL,
	})
	if err != nil {
		log.WithError(err).Print("Failed to store new auth session")
		return nil
	}
	return &AuthResponse{authURL.String()}
}
func (r *Realm) OnReceiveRedirect(w http.ResponseWriter, req *http.Request) {
	logger := log.WithField("jira_url", r.JIRAEndpoint)
	requestToken, verifier, err := oauth1.ParseAuthorizationCallback(req)
	if err != nil {
		failWith(logger, w, 400, "Failed to parse authorization callback", err)
		return
	}
	logger = logger.WithField("req_token", requestToken)
	logger.Print("Received authorization callback")
	session, err := database.GetServiceDB().LoadAuthSessionByID(r.id, requestToken)
	if err != nil {
		failWith(logger, w, 400, "Unrecognised request token", err)
		return
	}
	jiraSession, ok := session.(*Session)
	if !ok {
		failWith(logger, w, 500, "Unexpected session type found.", nil)
		return
	}
	logger = logger.WithField("user_id", jiraSession.UserID())
	logger.Print("Retrieved auth session for user")
	oauthConfig := r.oauth1Config(r.JIRAEndpoint)
	accessToken, accessSecret, err := oauthConfig.AccessToken(requestToken, jiraSession.RequestSecret, verifier)
	if err != nil {
		failWith(logger, w, 502, "Failed exchange for access token.", err)
		return
	}
	logger.Print("Exchanged for access token")
	jiraSession.AccessToken = accessToken
	jiraSession.AccessSecret = accessSecret
	_, err = database.GetServiceDB().StoreAuthSession(jiraSession)
	if err != nil {
		failWith(logger, w, 500, "Failed to persist JIRA session", err)
		return
	}
	if jiraSession.ClientsRedirectURL != "" {
		w.WriteHeader(302)
		w.Header().Set("Location", jiraSession.ClientsRedirectURL)
		w.Write([]byte(jiraSession.ClientsRedirectURL))
	} else {
		w.WriteHeader(200)
		w.Write([]byte(
			fmt.Sprintf("You have successfully linked your JIRA account on %s to %s",
				r.JIRAEndpoint, jiraSession.UserID(),
			),
		))
	}
}
func (r *Realm) AuthSession(id string, userID id.UserID, realmID string) types.AuthSession {
	return &Session{
		id:      id,
		userID:  userID,
		realmID: realmID,
	}
}