github.com/billybanfield/evergreen@v0.0.0-20170525200750-eeee692790f7/thirdparty/crowd.go (about)

     1  package thirdparty
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"io/ioutil"
     7  	"net/http"
     8  	"net/url"
     9  
    10  	"github.com/mongodb/grip"
    11  	"github.com/pkg/errors"
    12  )
    13  
    14  type CrowdUser struct {
    15  	Active       bool   `json:"active"`
    16  	DispName     string `json:"display-name"`
    17  	EmailAddress string `json:"email"`
    18  	FirstName    string `json:"first-name"`
    19  	LastName     string `json:"last-name"`
    20  	Name         string `json:"name"`
    21  }
    22  
    23  type Session struct {
    24  	Expand      string      `json:"active"`
    25  	CreatedDate int64       `json:"created-date"`
    26  	ExpiryDate  int64       `json:"expiry-date"`
    27  	User        SessionUser `json:"user"`
    28  	Link        SessionLink `json:"link"`
    29  	Token       string      `json:"token"`
    30  }
    31  
    32  type SessionUser struct {
    33  	Name string      `json:"name"`
    34  	Link SessionLink `json:"link"`
    35  }
    36  
    37  type SessionLink struct {
    38  	Href string `json:"href"`
    39  	Rel  string `json:"rel"`
    40  }
    41  
    42  type WrapCrowdUser struct {
    43  	User CrowdUser `json:"user"`
    44  }
    45  
    46  func (self *CrowdUser) DisplayName() string {
    47  	return self.DispName
    48  }
    49  
    50  func (self *CrowdUser) Email() string {
    51  	return self.EmailAddress
    52  }
    53  
    54  func (self *CrowdUser) Username() string {
    55  	return self.Name
    56  }
    57  
    58  type RESTCrowdService struct {
    59  	crowdUsername string
    60  	crowdPassword string
    61  	apiRoot       *url.URL
    62  }
    63  
    64  //NewRESTCrowdService constructs a REST-based implementation that allows
    65  //fetching user info from Crowd. Takes the username/password credentials, and
    66  //a URL which is the base of the REST endpoint, e.g. "https://crowd.10gen.com/"
    67  
    68  func NewRESTCrowdService(crowdUsername string, crowdPassword string, baseUrl string) (*RESTCrowdService, error) {
    69  	apiRoot, err := url.Parse(baseUrl)
    70  	if err != nil {
    71  		return nil, err
    72  	}
    73  	return &RESTCrowdService{crowdUsername, crowdPassword, apiRoot}, nil
    74  }
    75  
    76  func (self *RESTCrowdService) GetUser(username string) (*CrowdUser, error) {
    77  	values := url.Values{}
    78  	values.Add("username", username)
    79  	subUrl, err := self.apiRoot.Parse("/crowd/rest/usermanagement/latest/user?" + values.Encode())
    80  	if err != nil {
    81  		return nil, errors.Wrap(err, "invalid URL")
    82  	}
    83  
    84  	req, err := http.NewRequest("GET", subUrl.String(), nil)
    85  	if err != nil {
    86  		return nil, errors.Wrap(err, "Could not create request")
    87  	}
    88  
    89  	req.Header.Add("Accept", "application/json")
    90  	req.Header.Add("Content-Type", "application/json")
    91  	req.SetBasicAuth(self.crowdUsername, self.crowdPassword)
    92  	client := &http.Client{}
    93  	resp, err := client.Do(req)
    94  
    95  	if err != nil {
    96  		return nil, errors.Wrap(err, "Error making http request")
    97  	}
    98  
    99  	if resp.StatusCode != http.StatusOK {
   100  		return nil, errors.Errorf("Received unexpected status code from crowd: %v", resp.StatusCode)
   101  	}
   102  	result := CrowdUser{}
   103  
   104  	defer resp.Body.Close()
   105  	body, err := ioutil.ReadAll(resp.Body)
   106  	if err != nil {
   107  		return nil, errors.Errorf("Error occurred reading data from response: %v", resp.StatusCode)
   108  	}
   109  
   110  	err = json.Unmarshal(body, &result)
   111  	if err != nil {
   112  		return nil, errors.Wrap(err, "Error parsing json from crowd")
   113  	}
   114  
   115  	return &result, nil
   116  }
   117  
   118  func (self *RESTCrowdService) GetUserFromToken(token string) (*CrowdUser, error) {
   119  	// Note: at this point the token is the actual token string.
   120  
   121  	subUrl, err := self.apiRoot.Parse("/crowd/rest/usermanagement/latest/session/" + token)
   122  	if err != nil {
   123  		return nil, errors.Wrap(err, "invalid URL")
   124  	}
   125  
   126  	req, err := http.NewRequest("GET", subUrl.String(), nil)
   127  	if err != nil {
   128  		return nil, errors.Wrap(err, "Could not create request")
   129  	}
   130  
   131  	req.Header.Add("Accept", "application/json")
   132  	req.Header.Add("Content-Type", "application/json")
   133  	req.SetBasicAuth(self.crowdUsername, self.crowdPassword)
   134  	client := &http.Client{}
   135  	resp, err := client.Do(req)
   136  	if err != nil {
   137  		return nil, errors.Wrap(err, "Error making http request")
   138  	}
   139  
   140  	defer resp.Body.Close()
   141  
   142  	if resp.StatusCode != http.StatusOK {
   143  		return nil, errors.Errorf("Received unexpected status code from crowd: %v", resp.StatusCode)
   144  	}
   145  
   146  	body, err := ioutil.ReadAll(resp.Body)
   147  	if err != nil {
   148  		return nil, errors.Errorf("Error occurred reading data from response: %v", resp.StatusCode)
   149  	}
   150  
   151  	result := WrapCrowdUser{}
   152  	err = json.Unmarshal(body, &result)
   153  	if err != nil {
   154  		return nil, errors.Wrap(err, "Error parsing json from crowd")
   155  	}
   156  
   157  	return &(result.User), nil
   158  }
   159  
   160  func (self *RESTCrowdService) CreateSession(username, password string) (*Session, error) {
   161  	grip.Debugf("Requesting user session for '%v' from crowd", username)
   162  	subUrl, err := self.apiRoot.Parse("/crowd/rest/usermanagement/latest/session")
   163  	if err != nil {
   164  		return nil, errors.Wrap(err, "invalid URL")
   165  	}
   166  	postData := map[string]string{
   167  		"username": username,
   168  		"password": password,
   169  	}
   170  	jsonBytes, err := json.Marshal(postData)
   171  	if err != nil {
   172  		return nil, errors.WithStack(err)
   173  	}
   174  	req, err := http.NewRequest("POST", subUrl.String(), ioutil.NopCloser(bytes.NewReader(jsonBytes)))
   175  	if err != nil {
   176  		return nil, errors.Wrap(err, "Could not create request")
   177  	}
   178  	req.Header.Add("Accept", "application/json")
   179  	req.Header.Add("Content-Type", "application/json")
   180  	req.SetBasicAuth(self.crowdUsername, self.crowdPassword)
   181  
   182  	client := &http.Client{}
   183  	resp, err := client.Do(req)
   184  	if err != nil {
   185  		return nil, errors.Wrap(err, "error making http request")
   186  	}
   187  	if resp == nil {
   188  		return nil, errors.Errorf("received nil response from %v", subUrl.String())
   189  	}
   190  	defer resp.Body.Close()
   191  
   192  	if resp.StatusCode != http.StatusCreated {
   193  		return nil, errors.Errorf("(%v) received unexpected status code from crowd",
   194  			resp.StatusCode)
   195  	}
   196  	session := &Session{}
   197  	body, err := ioutil.ReadAll(resp.Body)
   198  	if err != nil {
   199  		return nil, errors.Wrapf(err, "(%v) error occurred reading data from response",
   200  			resp.StatusCode)
   201  	}
   202  
   203  	if err = json.Unmarshal(body, session); err != nil {
   204  		return nil, errors.Wrap(err, "error parsing json from crowd")
   205  	}
   206  	return session, nil
   207  }