Merge pull request #4 from matrix-org/kegan/registration

Add Register functions
This commit is contained in:
Kegsay 2016-12-05 16:20:24 +00:00 committed by GitHub
commit 8db775656f
3 changed files with 128 additions and 5 deletions

View File

@ -75,7 +75,9 @@ func (cli *Client) BuildBaseURL(urlPath ...string) string {
parts = append(parts, urlPath...) parts = append(parts, urlPath...)
hsURL.Path = path.Join(parts...) hsURL.Path = path.Join(parts...)
query := hsURL.Query() query := hsURL.Query()
if cli.AccessToken != "" {
query.Set("access_token", cli.AccessToken) query.Set("access_token", cli.AccessToken)
}
hsURL.RawQuery = query.Encode() hsURL.RawQuery = query.Encode()
return hsURL.String() return hsURL.String()
} }
@ -162,9 +164,9 @@ func (cli *Client) StopSync() {
// MakeRequest makes a JSON HTTP request to the given URL. // MakeRequest makes a JSON HTTP request to the given URL.
// If "resBody" is not nil, the response body will be json.Unmarshalled into it. // If "resBody" is not nil, the response body will be json.Unmarshalled into it.
// //
// Returns the HTTP body as bytes on 2xx. Returns an error if the response is not 2xx. This error // Returns the HTTP body as bytes on 2xx with a nil error. Returns an error if the response is not 2xx along
// is an HTTPError which includes the returned HTTP status code and possibly a RespError as the // with the HTTP body bytes if it got that far. This error is an HTTPError which includes the returned
// WrappedError, if the HTTP body could be decoded as a RespError. // HTTP status code and possibly a RespError as the WrappedError, if the HTTP body could be decoded as a RespError.
func (cli *Client) MakeRequest(method string, httpURL string, reqBody interface{}, resBody interface{}) ([]byte, error) { func (cli *Client) MakeRequest(method string, httpURL string, reqBody interface{}, resBody interface{}) ([]byte, error) {
var req *http.Request var req *http.Request
var err error var err error
@ -205,7 +207,7 @@ func (cli *Client) MakeRequest(method string, httpURL string, reqBody interface{
msg = msg + ": " + string(contents) msg = msg + ": " + string(contents)
} }
return nil, HTTPError{ return contents, HTTPError{
Code: res.StatusCode, Code: res.StatusCode,
Message: msg, Message: msg,
WrappedError: wrap, WrappedError: wrap,
@ -253,6 +255,85 @@ func (cli *Client) SyncRequest(timeout int, since, filterID string, fullState bo
return return
} }
func (cli *Client) register(u string, req *ReqRegister) (resp *RespRegister, uiaResp *RespUserInteractive, err error) {
var bodyBytes []byte
bodyBytes, err = cli.MakeRequest("POST", u, req, nil)
if err != nil {
httpErr, ok := err.(HTTPError)
if !ok { // network error
return
}
if httpErr.Code == 401 {
// body should be RespUserInteractive, if it isn't, fail with the error
err = json.Unmarshal(bodyBytes, &uiaResp)
return
}
return
}
// body should be RespRegister
err = json.Unmarshal(bodyBytes, &resp)
return
}
// Register makes an HTTP request according to http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-register
//
// Registers with kind=user. For kind=guest, see RegisterGuest.
func (cli *Client) Register(req *ReqRegister) (*RespRegister, *RespUserInteractive, error) {
u := cli.BuildURL("register")
return cli.register(u, req)
}
// RegisterGuest makes an HTTP request according to http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-register
// with kind=guest.
//
// For kind=user, see Register.
func (cli *Client) RegisterGuest(req *ReqRegister) (*RespRegister, *RespUserInteractive, error) {
query := map[string]string{
"kind": "guest",
}
u := cli.BuildURLWithQuery([]string{"register"}, query)
return cli.register(u, req)
}
// RegisterDummy performs m.login.dummy registration according to https://matrix.org/docs/spec/client_server/r0.2.0.html#dummy-auth
//
// Only a username and password need to be provided on the ReqRegister struct. Most local/developer homeservers will allow registration
// this way. If the homeserver does not, an error is returned. If "setOnClient" is true, the access_token and user_id will be set on
// this client instance.
//
// res, err := cli.RegisterDummy(&gomatrix.ReqRegister{
// Username: "alice",
// Password: "wonderland",
// }, false)
// if err != nil {
// panic(err)
// }
// token := res.AccessToken
func (cli *Client) RegisterDummy(req *ReqRegister, setOnClient bool) (*RespRegister, error) {
res, uia, err := cli.Register(req)
if err != nil && uia == nil {
return nil, err
}
if uia != nil && uia.HasSingleStageFlow("m.login.dummy") {
req.Auth = struct {
Type string `json:"type"`
Session string `json:"session,omitempty"`
}{"m.login.dummy", uia.Session}
res, _, err = cli.Register(req)
if err != nil {
return nil, err
}
}
if res == nil {
return nil, fmt.Errorf("registration failed: does this server support m.login.dummy?")
}
if setOnClient {
cli.UserID = res.UserID
cli.AccessToken = res.AccessToken
}
return res, nil
}
// JoinRoom joins the client to a room ID or alias. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-join-roomidoralias // JoinRoom joins the client to a room ID or alias. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-join-roomidoralias
// //
// If serverName is specified, this will be added as a query param to instruct the homeserver to join via that server. If content is specified, it will // If serverName is specified, this will be added as a query param to instruct the homeserver to join via that server. If content is specified, it will

11
requests.go Normal file
View File

@ -0,0 +1,11 @@
package gomatrix
// ReqRegister is the JSON request for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-register
type ReqRegister struct {
Username string `json:"username,omitempty"`
BindEmail bool `json:"bind_email,omitempty"`
Password string `json:"password,omitempty"`
DeviceID string `json:"device_id,omitempty"`
InitialDeviceDisplayName string `json:"initial_device_display_name"`
Auth interface{} `json:"auth,omitempty"`
}

View File

@ -35,6 +35,37 @@ type RespMediaUpload struct {
ContentURI string `json:"content_uri"` ContentURI string `json:"content_uri"`
} }
// RespUserInteractive is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#user-interactive-authentication-api
type RespUserInteractive struct {
Flows []struct {
Stages []string `json:"stages"`
} `json:"flows"`
Params map[string]interface{} `json:"params"`
Session string `json:"string"`
Completed []string `json:"completed"`
ErrCode string `json:"errcode"`
Error string `json:"error"`
}
// HasSingleStageFlow returns true if there exists at least 1 Flow with a single stage of stageName.
func (r RespUserInteractive) HasSingleStageFlow(stageName string) bool {
for _, f := range r.Flows {
if len(f.Stages) == 1 && f.Stages[0] == stageName {
return true
}
}
return false
}
// RespRegister is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-register
type RespRegister struct {
AccessToken string `json:"access_token"`
DeviceID string `json:"device_id"`
HomeServer string `json:"home_server"`
RefreshToken string `json:"refresh_token"`
UserID string `json:"user_id"`
}
// RespSync is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-sync // RespSync is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-sync
type RespSync struct { type RespSync struct {
NextBatch string `json:"next_batch"` NextBatch string `json:"next_batch"`