diff --git a/github/authorizations.go b/github/authorizations.go new file mode 100644 index 0000000..52b996f --- /dev/null +++ b/github/authorizations.go @@ -0,0 +1,250 @@ +// Copyright 2015 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import "fmt" + +// AuthorizationsService handles communication with the OAuth related +// methods of the GitHub API. +// +// This service requires HTTP Basic Authentication; it cannot be accessed using +// an OAuth token. +// +// GitHub API docs: http://developer.github.com/v3/oauth_authorizations/ +type AuthorizationsService struct { + client *Client +} + +// Authorization represents a GitHub authorized infomaration. +type Authorization struct { + ID *int `json:"id,omitempty"` + URL *string `json:"url,omitempty"` + Scopes []string `json:"scopes,omitempty"` + Token *string `json:"token,omitempty"` + TokenLastEight *string `json:"token_last_eight,omitempty"` + HashedToken *string `json:"hashed_token,omitempty"` + App *AuthorizationApp `json:"app,omitempty"` + Note *string `json:"note,omitempty"` + NoteURL *string `json:"note_url,omitempty"` + UpdateAt *Timestamp `json:"updated_at,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + Fingerprint *string `json:"fingerprint,omitempty"` +} + +// AuthorizationApp represents application name that github allows app to use api +type AuthorizationApp struct { + URL *string `json:"url,omitempty"` + Name *string `json:"name,omitempty"` + ClientID *string `json:"client_id,omitempty"` +} + +// AuthorizationRequest represents a request to create an authorization. +type AuthorizationRequest struct { + Scopes []string `json:"scopes,omitempty"` + Note *string `json:"note,omitempty"` + NoteURL *string `json:"note_url,omitempty"` + ClientID *string `json:"client_id,omitempty"` + ClientSecret *string `json:"client_secret,omitempty"` + Fingerprint *string `json:"fingerprint,omitempty"` +} + +// AuthorizationUpdateRequest represents a request to update an authorization. +type AuthorizationUpdateRequest struct { + Scopes []string `json:"scopes,omitempty"` + AddScopes []string `json:"add_scopes,omitempty"` + RemoveScopes []string `json:"remove_scopes,omitempty"` + Note *string `json:"note,omitempty"` + NoteURL *string `json:"note_url,omitempty"` + Fingerprint *string `json:"fingerprint,omitempty"` +} + +// List the authorizations info the specified OAuth application. +// +// GitHub API Docs: https://developer.github.com/v3/oauth_authorizations/#list-your-authorizations +func (s *AuthorizationsService) List(opt *ListOptions) ([]Authorization, *Response, error) { + u := "authorizations" + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + auths := new([]Authorization) + resp, err := s.client.Do(req, auths) + if err != nil { + return nil, resp, err + } + return *auths, resp, err +} + +// Get a single authorization. +// +// GitHub API Docs: https://developer.github.com/v3/oauth_authorizations/#get-a-single-authorization +func (s *AuthorizationsService) Get(id int) (*Authorization, *Response, error) { + u := fmt.Sprintf("authorizations/%d", id) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + a := new(Authorization) + resp, err := s.client.Do(req, a) + if err != nil { + return nil, resp, err + } + return a, resp, err +} + +// Create a new authorization for the specified OAuth application. +// +// GitHub API Docs: https://developer.github.com/v3/oauth_authorizations/#create-a-new-authorization +func (s *AuthorizationsService) Create(auth *AuthorizationRequest) (*Authorization, *Response, error) { + u := "authorizations" + + req, err := s.client.NewRequest("POST", u, auth) + if err != nil { + return nil, nil, err + } + + a := new(Authorization) + resp, err := s.client.Do(req, a) + if err != nil { + return nil, resp, err + } + return a, resp, err +} + +// GetOrCreateForApp creates a new authorization for the specified OAuth +// application, only if an authorization for that application doesn’t already +// exist for the user. +// +// clientID is the OAuth Client ID with which to create the token. +// +// GitHub API Docs: https://developer.github.com/v3/oauth_authorizations/#get-or-create-an-authorization-for-a-specific-app +func (s *AuthorizationsService) GetOrCreateForApp(clientID string, auth *AuthorizationRequest) (*Authorization, *Response, error) { + var u string + if auth.Fingerprint == nil || *auth.Fingerprint == "" { + u = fmt.Sprintf("authorizations/clients/%v", clientID) + } else { + u = fmt.Sprintf("authorizations/clients/%v/%v", clientID, *auth.Fingerprint) + } + + req, err := s.client.NewRequest("PUT", u, auth) + if err != nil { + return nil, nil, err + } + + a := new(Authorization) + resp, err := s.client.Do(req, a) + if err != nil { + return nil, resp, err + } + + return a, resp, err +} + +// Edit a single authorization. +// +// GitHub API Docs: https://developer.github.com/v3/oauth_authorizations/#update-an-existing-authorization +func (s *AuthorizationsService) Edit(id int, auth *AuthorizationUpdateRequest) (*Authorization, *Response, error) { + u := fmt.Sprintf("authorizations/%d", id) + + req, err := s.client.NewRequest("PATCH", u, auth) + if err != nil { + return nil, nil, err + } + + a := new(Authorization) + resp, err := s.client.Do(req, a) + if err != nil { + return nil, resp, err + } + + return a, resp, err +} + +// Delete a single authorization. +// +// GitHub API Docs: https://developer.github.com/v3/oauth_authorizations/#delete-an-authorization +func (s *AuthorizationsService) Delete(id int) (*Response, error) { + u := fmt.Sprintf("authorizations/%d", id) + + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// Check a single authorization. +// +// OAuth applications can use this API method for checking OAuth token validity +// without running afoul of normal rate limits for failed login attempts. +// Authentication works differently with this particular endpoint. You must +// use Basic Authentication when accessing it, where the username is the OAuth +// application clientID and the password is its client_secret. +// +// GitHub API Docs: https://developer.github.com/v3/oauth_authorizations/#check-an-authorization +func (s *AuthorizationsService) Check(clientID string, token string) (*Authorization, *Response, error) { + u := fmt.Sprintf("applications/%s/tokens/%s", clientID, token) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + a := new(Authorization) + resp, err := s.client.Do(req, a) + if err != nil { + return nil, resp, err + } + + return a, resp, err +} + +// Reset a single authorization. +// +// OAuth applications can use this API method to reset a valid OAuth token +// without end user involvement. Applications must save the "token" property +// in the response, because changes take effect immediately. +// +// GitHub API Docs: https://developer.github.com/v3/oauth_authorizations/#reset-an-authorization +func (s *AuthorizationsService) Reset(clientID string, token string) (*Authorization, *Response, error) { + u := fmt.Sprintf("applications/%s/tokens/%s", clientID, token) + + req, err := s.client.NewRequest("POST", u, nil) + if err != nil { + return nil, nil, err + } + + a := new(Authorization) + resp, err := s.client.Do(req, a) + if err != nil { + return nil, resp, err + } + + return a, resp, err +} + +// Revoke an authorization for an application. +// +// GitHub API Docs: https://developer.github.com/v3/oauth_authorizations/#revoke-an-authorization-for-an-application +func (s *AuthorizationsService) Revoke(clientID string, token string) (*Response, error) { + u := fmt.Sprintf("applications/%s/tokens/%s", clientID, token) + + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} diff --git a/github/authorizations_test.go b/github/authorizations_test.go new file mode 100644 index 0000000..813762a --- /dev/null +++ b/github/authorizations_test.go @@ -0,0 +1,251 @@ +// Copyright 2015 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "encoding/json" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestAuthorizationsService_List(t *testing.T) { + setup() + defer teardown() + + mux.HandleFunc("/authorizations", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "1", "per_page": "2"}) + fmt.Fprint(w, `[{"id":1}]`) + }) + + opt := &ListOptions{Page: 1, PerPage: 2} + got, _, err := client.Authorizations.List(opt) + if err != nil { + t.Errorf("Authorizations.List returned error: %v", err) + } + + want := []Authorization{{ID: Int(1)}} + if !reflect.DeepEqual(got, want) { + t.Errorf("Authorizations.List returned %+v, want %+v", *got[0].ID, *want[0].ID) + } +} + +func TestAuthorizationsService_Get(t *testing.T) { + setup() + defer teardown() + + mux.HandleFunc("/authorizations/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"id":1}`) + }) + + got, _, err := client.Authorizations.Get(1) + if err != nil { + t.Errorf("Authorizations.Get returned error: %v", err) + } + + want := &Authorization{ID: Int(1)} + if !reflect.DeepEqual(got, want) { + t.Errorf("Authorizations.Get returned auth %+v, want %+v", got, want) + } +} + +func TestAuthorizationsService_Create(t *testing.T) { + setup() + defer teardown() + + input := &AuthorizationRequest{ + Note: String("test"), + } + + mux.HandleFunc("/authorizations", func(w http.ResponseWriter, r *http.Request) { + v := new(AuthorizationRequest) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "POST") + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"ID":1}`) + }) + + got, _, err := client.Authorizations.Create(input) + if err != nil { + t.Errorf("Authorizations.Create returned error: %v", err) + } + + want := &Authorization{ID: Int(1)} + if !reflect.DeepEqual(got, want) { + t.Errorf("Authorization.Create returned %+v, want %+v", got, want) + } +} + +func TestAuthorizationsService_GetOrCreateForApp(t *testing.T) { + setup() + defer teardown() + + input := &AuthorizationRequest{ + Note: String("test"), + } + + mux.HandleFunc("/authorizations/clients/id", func(w http.ResponseWriter, r *http.Request) { + v := new(AuthorizationRequest) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "PUT") + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"ID":1}`) + }) + + got, _, err := client.Authorizations.GetOrCreateForApp("id", input) + if err != nil { + t.Errorf("Authorizations.GetOrCreateForApp returned error: %v", err) + } + + want := &Authorization{ID: Int(1)} + if !reflect.DeepEqual(got, want) { + t.Errorf("Authorization.GetOrCreateForApp returned %+v, want %+v", got, want) + } +} + +func TestAuthorizationsService_GetOrCreateForApp_Fingerprint(t *testing.T) { + setup() + defer teardown() + + input := &AuthorizationRequest{ + Note: String("test"), + Fingerprint: String("fp"), + } + + mux.HandleFunc("/authorizations/clients/id/fp", func(w http.ResponseWriter, r *http.Request) { + v := new(AuthorizationRequest) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "PUT") + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"ID":1}`) + }) + + got, _, err := client.Authorizations.GetOrCreateForApp("id", input) + if err != nil { + t.Errorf("Authorizations.GetOrCreateForApp returned error: %v", err) + } + + want := &Authorization{ID: Int(1)} + if !reflect.DeepEqual(got, want) { + t.Errorf("Authorization.GetOrCreateForApp returned %+v, want %+v", got, want) + } +} + +func TestAuthorizationsService_Edit(t *testing.T) { + setup() + defer teardown() + + input := &AuthorizationUpdateRequest{ + Note: String("test"), + } + + mux.HandleFunc("/authorizations/1", func(w http.ResponseWriter, r *http.Request) { + v := new(AuthorizationUpdateRequest) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "PATCH") + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"ID":1}`) + }) + + got, _, err := client.Authorizations.Edit(1, input) + if err != nil { + t.Errorf("Authorizations.Edit returned error: %v", err) + } + + want := &Authorization{ID: Int(1)} + if !reflect.DeepEqual(got, want) { + t.Errorf("Authorization.Update returned %+v, want %+v", got, want) + } +} + +func TestAuthorizationsService_Delete(t *testing.T) { + setup() + defer teardown() + + mux.HandleFunc("/authorizations/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + _, err := client.Authorizations.Delete(1) + if err != nil { + t.Errorf("Authorizations.Delete returned error: %v", err) + } +} + +func TestAuthorizationsService_Check(t *testing.T) { + setup() + defer teardown() + + mux.HandleFunc("/applications/id/tokens/t", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"id":1}`) + }) + + got, _, err := client.Authorizations.Check("id", "t") + if err != nil { + t.Errorf("Authorizations.Check returned error: %v", err) + } + + want := &Authorization{ID: Int(1)} + if !reflect.DeepEqual(got, want) { + t.Errorf("Authorizations.Check returned auth %+v, want %+v", got, want) + } +} + +func TestAuthorizationsService_Reset(t *testing.T) { + setup() + defer teardown() + + mux.HandleFunc("/applications/id/tokens/t", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + fmt.Fprint(w, `{"ID":1}`) + }) + + got, _, err := client.Authorizations.Reset("id", "t") + if err != nil { + t.Errorf("Authorizations.Reset returned error: %v", err) + } + + want := &Authorization{ID: Int(1)} + if !reflect.DeepEqual(got, want) { + t.Errorf("Authorizations.Reset returned auth %+v, want %+v", got, want) + } +} + +func TestAuthorizationsService_Revoke(t *testing.T) { + setup() + defer teardown() + + mux.HandleFunc("/applications/id/tokens/t", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + _, err := client.Authorizations.Revoke("id", "t") + if err != nil { + t.Errorf("Authorizations.Revoke returned error: %v", err) + } +} diff --git a/github/github.go b/github/github.go index 0c2a8db..9db2920 100644 --- a/github/github.go +++ b/github/github.go @@ -89,18 +89,19 @@ type Client struct { rate Rate // Rate limit for the client as determined by the most recent API call. // Services used for talking to different parts of the GitHub API. - Activity *ActivityService - Gists *GistsService - Git *GitService - Gitignores *GitignoresService - Issues *IssuesService - Organizations *OrganizationsService - PullRequests *PullRequestsService - Repositories *RepositoriesService - Search *SearchService - Users *UsersService - Licenses *LicensesService - Migrations *MigrationService + Activity *ActivityService + Authorizations *AuthorizationsService + Gists *GistsService + Git *GitService + Gitignores *GitignoresService + Issues *IssuesService + Organizations *OrganizationsService + PullRequests *PullRequestsService + Repositories *RepositoriesService + Search *SearchService + Users *UsersService + Licenses *LicensesService + Migrations *MigrationService } // ListOptions specifies the optional parameters to various List methods that @@ -153,6 +154,7 @@ func NewClient(httpClient *http.Client) *Client { c := &Client{client: httpClient, BaseURL: baseURL, UserAgent: userAgent, UploadURL: uploadURL} c.Activity = &ActivityService{client: c} + c.Authorizations = &AuthorizationsService{client: c} c.Gists = &GistsService{client: c} c.Git = &GitService{client: c} c.Gitignores = &GitignoresService{client: c} @@ -365,6 +367,7 @@ func (c *Client) Do(req *http.Request, v interface{}) (*Response, error) { } } } + return response, err }