diff options
Diffstat (limited to 'model')
-rw-r--r-- | model/authorize.go | 56 | ||||
-rw-r--r-- | model/authorize_test.go | 11 | ||||
-rw-r--r-- | model/client4.go | 113 | ||||
-rw-r--r-- | model/oauth.go | 23 |
4 files changed, 187 insertions, 16 deletions
diff --git a/model/authorize.go b/model/authorize.go index 2f290fab2..460b70823 100644 --- a/model/authorize.go +++ b/model/authorize.go @@ -6,6 +6,7 @@ package model import ( "encoding/json" "io" + "net/http" ) const ( @@ -25,6 +26,14 @@ type AuthData struct { Scope string `json:"scope"` } +type AuthorizeRequest struct { + ResponseType string `json:"response_type"` + ClientId string `json:"client_id"` + RedirectUri string `json:"redirect_uri"` + Scope string `json:"scope"` + State string `json:"state"` +} + // IsValid validates the AuthData and returns an error if it isn't configured // correctly. func (ad *AuthData) IsValid() *AppError { @@ -64,6 +73,33 @@ func (ad *AuthData) IsValid() *AppError { return nil } +// IsValid validates the AuthorizeRequest and returns an error if it isn't configured +// correctly. +func (ar *AuthorizeRequest) IsValid() *AppError { + + if len(ar.ClientId) != 26 { + return NewAppError("AuthData.IsValid", "model.authorize.is_valid.client_id.app_error", nil, "", http.StatusBadRequest) + } + + if len(ar.ResponseType) == 0 { + return NewAppError("AuthData.IsValid", "model.authorize.is_valid.response_type.app_error", nil, "", http.StatusBadRequest) + } + + if len(ar.RedirectUri) == 0 || len(ar.RedirectUri) > 256 || !IsValidHttpUrl(ar.RedirectUri) { + return NewAppError("AuthData.IsValid", "model.authorize.is_valid.redirect_uri.app_error", nil, "client_id="+ar.ClientId, http.StatusBadRequest) + } + + if len(ar.State) > 128 { + return NewAppError("AuthData.IsValid", "model.authorize.is_valid.state.app_error", nil, "client_id="+ar.ClientId, http.StatusBadRequest) + } + + if len(ar.Scope) > 128 { + return NewAppError("AuthData.IsValid", "model.authorize.is_valid.scope.app_error", nil, "client_id="+ar.ClientId, http.StatusBadRequest) + } + + return nil +} + func (ad *AuthData) PreSave() { if ad.ExpiresIn == 0 { ad.ExpiresIn = AUTHCODE_EXPIRE_TIME @@ -98,6 +134,26 @@ func AuthDataFromJson(data io.Reader) *AuthData { } } +func (ar *AuthorizeRequest) ToJson() string { + b, err := json.Marshal(ar) + if err != nil { + return "" + } else { + return string(b) + } +} + +func AuthorizeRequestFromJson(data io.Reader) *AuthorizeRequest { + decoder := json.NewDecoder(data) + var ar AuthorizeRequest + err := decoder.Decode(&ar) + if err == nil { + return &ar + } else { + return nil + } +} + func (ad *AuthData) IsExpired() bool { if GetMillis() > ad.CreateAt+int64(ad.ExpiresIn*1000) { diff --git a/model/authorize_test.go b/model/authorize_test.go index cbb57d54c..3f43a4fc3 100644 --- a/model/authorize_test.go +++ b/model/authorize_test.go @@ -20,6 +20,17 @@ func TestAuthJson(t *testing.T) { if a1.Code != ra1.Code { t.Fatal("codes didn't match") } + + a2 := AuthorizeRequest{} + a2.ClientId = NewId() + a2.Scope = NewId() + + json = a2.ToJson() + ra2 := AuthorizeRequestFromJson(strings.NewReader(json)) + + if a2.ClientId != ra2.ClientId { + t.Fatal("client ids didn't match") + } } func TestAuthPreSave(t *testing.T) { diff --git a/model/client4.go b/model/client4.go index a7a3607e6..6f8b43c39 100644 --- a/model/client4.go +++ b/model/client4.go @@ -242,24 +242,32 @@ func (c *Client4) GetReactionsRoute() string { return fmt.Sprintf("/reactions") } +func (c *Client4) GetOAuthAppsRoute() string { + return fmt.Sprintf("/oauth/apps") +} + +func (c *Client4) GetOAuthAppRoute(appId string) string { + return fmt.Sprintf("/oauth/apps/%v", appId) +} + func (c *Client4) DoApiGet(url string, etag string) (*http.Response, *AppError) { - return c.DoApiRequest(http.MethodGet, url, "", etag) + return c.DoApiRequest(http.MethodGet, c.ApiUrl+url, "", etag) } func (c *Client4) DoApiPost(url string, data string) (*http.Response, *AppError) { - return c.DoApiRequest(http.MethodPost, url, data, "") + return c.DoApiRequest(http.MethodPost, c.ApiUrl+url, data, "") } func (c *Client4) DoApiPut(url string, data string) (*http.Response, *AppError) { - return c.DoApiRequest(http.MethodPut, url, data, "") + return c.DoApiRequest(http.MethodPut, c.ApiUrl+url, data, "") } func (c *Client4) DoApiDelete(url string) (*http.Response, *AppError) { - return c.DoApiRequest(http.MethodDelete, url, "", "") + return c.DoApiRequest(http.MethodDelete, c.ApiUrl+url, "", "") } func (c *Client4) DoApiRequest(method, url, data, etag string) (*http.Response, *AppError) { - rq, _ := http.NewRequest(method, c.ApiUrl+url, strings.NewReader(data)) + rq, _ := http.NewRequest(method, url, strings.NewReader(data)) rq.Close = true if len(etag) > 0 { @@ -2211,6 +2219,101 @@ func (c *Client4) GetLogs(page, perPage int) ([]string, *Response) { } } +// OAuth Section + +// CreateOAuthApp will register a new OAuth 2.0 client application with Mattermost acting as an OAuth 2.0 service provider. +func (c *Client4) CreateOAuthApp(app *OAuthApp) (*OAuthApp, *Response) { + if r, err := c.DoApiPost(c.GetOAuthAppsRoute(), app.ToJson()); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return OAuthAppFromJson(r.Body), BuildResponse(r) + } +} + +// GetOAuthApps gets a page of registered OAuth 2.0 client applications with Mattermost acting as an OAuth 2.0 service provider. +func (c *Client4) GetOAuthApps(page, perPage int) ([]*OAuthApp, *Response) { + query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) + if r, err := c.DoApiGet(c.GetOAuthAppsRoute()+query, ""); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return OAuthAppListFromJson(r.Body), BuildResponse(r) + } +} + +// GetOAuthApp gets a registered OAuth 2.0 client application with Mattermost acting as an OAuth 2.0 service provider. +func (c *Client4) GetOAuthApp(appId string) (*OAuthApp, *Response) { + if r, err := c.DoApiGet(c.GetOAuthAppRoute(appId), ""); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return OAuthAppFromJson(r.Body), BuildResponse(r) + } +} + +// GetOAuthAppInfo gets a sanitized version of a registered OAuth 2.0 client application with Mattermost acting as an OAuth 2.0 service provider. +func (c *Client4) GetOAuthAppInfo(appId string) (*OAuthApp, *Response) { + if r, err := c.DoApiGet(c.GetOAuthAppRoute(appId)+"/info", ""); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return OAuthAppFromJson(r.Body), BuildResponse(r) + } +} + +// DeleteOAuthApp deletes a registered OAuth 2.0 client application. +func (c *Client4) DeleteOAuthApp(appId string) (bool, *Response) { + if r, err := c.DoApiDelete(c.GetOAuthAppRoute(appId)); err != nil { + return false, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + +// RegenerateOAuthAppSecret regenerates the client secret for a registered OAuth 2.0 client application. +func (c *Client4) RegenerateOAuthAppSecret(appId string) (*OAuthApp, *Response) { + if r, err := c.DoApiPost(c.GetOAuthAppRoute(appId)+"/regen_secret", ""); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return OAuthAppFromJson(r.Body), BuildResponse(r) + } +} + +// GetAuthorizedOAuthAppsForUser gets a page of OAuth 2.0 client applications the user has authorized to use access their account. +func (c *Client4) GetAuthorizedOAuthAppsForUser(userId string, page, perPage int) ([]*OAuthApp, *Response) { + query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) + if r, err := c.DoApiGet(c.GetUserRoute(userId)+"/oauth/apps/authorized"+query, ""); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return OAuthAppListFromJson(r.Body), BuildResponse(r) + } +} + +// AuthorizeOAuthApp will authorize an OAuth 2.0 client application to access a user's account and provide a redirect link to follow. +func (c *Client4) AuthorizeOAuthApp(authRequest *AuthorizeRequest) (string, *Response) { + if r, err := c.DoApiRequest(http.MethodPost, c.Url+"/oauth/authorize", authRequest.ToJson(), ""); err != nil { + return "", &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return MapFromJson(r.Body)["redirect"], BuildResponse(r) + } +} + +// DeauthorizeOAuthApp will deauthorize an OAuth 2.0 client application from accessing a user's account. +func (c *Client4) DeauthorizeOAuthApp(appId string) (bool, *Response) { + requestData := map[string]string{"client_id": appId} + if r, err := c.DoApiRequest(http.MethodPost, c.Url+"/oauth/deauthorize", MapToJson(requestData), ""); err != nil { + return false, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + // Commands Section // CreateCommand will create a new command if the user have the right permissions. diff --git a/model/oauth.go b/model/oauth.go index a8aca0ca0..6a3561ed9 100644 --- a/model/oauth.go +++ b/model/oauth.go @@ -7,6 +7,7 @@ import ( "encoding/json" "fmt" "io" + "net/http" "unicode/utf8" ) @@ -36,50 +37,50 @@ type OAuthApp struct { func (a *OAuthApp) IsValid() *AppError { if len(a.Id) != 26 { - return NewLocAppError("OAuthApp.IsValid", "model.oauth.is_valid.app_id.app_error", nil, "") + return NewAppError("OAuthApp.IsValid", "model.oauth.is_valid.app_id.app_error", nil, "", http.StatusBadRequest) } if a.CreateAt == 0 { - return NewLocAppError("OAuthApp.IsValid", "model.oauth.is_valid.create_at.app_error", nil, "app_id="+a.Id) + return NewAppError("OAuthApp.IsValid", "model.oauth.is_valid.create_at.app_error", nil, "app_id="+a.Id, http.StatusBadRequest) } if a.UpdateAt == 0 { - return NewLocAppError("OAuthApp.IsValid", "model.oauth.is_valid.update_at.app_error", nil, "app_id="+a.Id) + return NewAppError("OAuthApp.IsValid", "model.oauth.is_valid.update_at.app_error", nil, "app_id="+a.Id, http.StatusBadRequest) } if len(a.CreatorId) != 26 { - return NewLocAppError("OAuthApp.IsValid", "model.oauth.is_valid.creator_id.app_error", nil, "app_id="+a.Id) + return NewAppError("OAuthApp.IsValid", "model.oauth.is_valid.creator_id.app_error", nil, "app_id="+a.Id, http.StatusBadRequest) } if len(a.ClientSecret) == 0 || len(a.ClientSecret) > 128 { - return NewLocAppError("OAuthApp.IsValid", "model.oauth.is_valid.client_secret.app_error", nil, "app_id="+a.Id) + return NewAppError("OAuthApp.IsValid", "model.oauth.is_valid.client_secret.app_error", nil, "app_id="+a.Id, http.StatusBadRequest) } if len(a.Name) == 0 || len(a.Name) > 64 { - return NewLocAppError("OAuthApp.IsValid", "model.oauth.is_valid.name.app_error", nil, "app_id="+a.Id) + return NewAppError("OAuthApp.IsValid", "model.oauth.is_valid.name.app_error", nil, "app_id="+a.Id, http.StatusBadRequest) } if len(a.CallbackUrls) == 0 || len(fmt.Sprintf("%s", a.CallbackUrls)) > 1024 { - return NewLocAppError("OAuthApp.IsValid", "model.oauth.is_valid.callback.app_error", nil, "app_id="+a.Id) + return NewAppError("OAuthApp.IsValid", "model.oauth.is_valid.callback.app_error", nil, "app_id="+a.Id, http.StatusBadRequest) } for _, callback := range a.CallbackUrls { if !IsValidHttpUrl(callback) { - return NewLocAppError("OAuthApp.IsValid", "model.oauth.is_valid.callback.app_error", nil, "") + return NewAppError("OAuthApp.IsValid", "model.oauth.is_valid.callback.app_error", nil, "", http.StatusBadRequest) } } if len(a.Homepage) == 0 || len(a.Homepage) > 256 || !IsValidHttpUrl(a.Homepage) { - return NewLocAppError("OAuthApp.IsValid", "model.oauth.is_valid.homepage.app_error", nil, "app_id="+a.Id) + return NewAppError("OAuthApp.IsValid", "model.oauth.is_valid.homepage.app_error", nil, "app_id="+a.Id, http.StatusBadRequest) } if utf8.RuneCountInString(a.Description) > 512 { - return NewLocAppError("OAuthApp.IsValid", "model.oauth.is_valid.description.app_error", nil, "app_id="+a.Id) + return NewAppError("OAuthApp.IsValid", "model.oauth.is_valid.description.app_error", nil, "app_id="+a.Id, http.StatusBadRequest) } if len(a.IconURL) > 0 { if len(a.IconURL) > 512 || !IsValidHttpUrl(a.IconURL) { - return NewLocAppError("OAuthApp.IsValid", "model.oauth.is_valid.icon_url.app_error", nil, "app_id="+a.Id) + return NewAppError("OAuthApp.IsValid", "model.oauth.is_valid.icon_url.app_error", nil, "app_id="+a.Id, http.StatusBadRequest) } } |