diff --git a/src/github.com/matrix-org/dendrite/clientapi/auth/authtypes/account.go b/src/github.com/matrix-org/dendrite/clientapi/auth/authtypes/account.go index ed33d0b5e..1a03590e5 100644 --- a/src/github.com/matrix-org/dendrite/clientapi/auth/authtypes/account.go +++ b/src/github.com/matrix-org/dendrite/clientapi/auth/authtypes/account.go @@ -23,6 +23,7 @@ type Account struct { UserID string Localpart string ServerName gomatrixserverlib.ServerName + Profile *Profile // TODO: Other flags like IsAdmin, IsGuest // TODO: Devices // TODO: Associations (e.g. with application services) diff --git a/src/github.com/matrix-org/dendrite/clientapi/auth/authtypes/profile.go b/src/github.com/matrix-org/dendrite/clientapi/auth/authtypes/profile.go new file mode 100644 index 000000000..6cf508f4f --- /dev/null +++ b/src/github.com/matrix-org/dendrite/clientapi/auth/authtypes/profile.go @@ -0,0 +1,22 @@ +// Copyright 2017 Vector Creations Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package authtypes + +// Profile represents the profile for a Matrix account on this home server. +type Profile struct { + Localpart string + DisplayName string + AvatarURL string +} diff --git a/src/github.com/matrix-org/dendrite/clientapi/auth/storage/accounts/profile_table.go b/src/github.com/matrix-org/dendrite/clientapi/auth/storage/accounts/profile_table.go new file mode 100644 index 000000000..30097b433 --- /dev/null +++ b/src/github.com/matrix-org/dendrite/clientapi/auth/storage/accounts/profile_table.go @@ -0,0 +1,81 @@ +// Copyright 2017 Vector Creations Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package accounts + +import ( + "database/sql" + + "github.com/matrix-org/dendrite/clientapi/auth/authtypes" +) + +const profilesSchema = ` +-- Stores data about accounts profiles. +CREATE TABLE IF NOT EXISTS profiles ( + -- The Matrix user ID localpart for this account + localpart TEXT NOT NULL PRIMARY KEY, + -- The display name for this account + display_name TEXT, + -- The URL of the avatar for this account + avatar_url TEXT +); +` + +const insertProfileSQL = "" + + "INSERT INTO profiles(localpart, display_name, avatar_url) VALUES ($1, $2, $3)" + +const selectProfileByLocalpartSQL = "" + + "SELECT localpart, display_name, avatar_url FROM profiles WHERE localpart = $1" + +const setAvatarURLSQL = "" + + "UPDATE profiles SET avatar_url = $1 WHERE localpart = $2" + +type profilesStatements struct { + insertProfileStmt *sql.Stmt + selectProfileByLocalpartStmt *sql.Stmt + setAvatarURLStmt *sql.Stmt +} + +func (s *profilesStatements) prepare(db *sql.DB) (err error) { + _, err = db.Exec(profilesSchema) + if err != nil { + return + } + if s.insertProfileStmt, err = db.Prepare(insertProfileSQL); err != nil { + return + } + if s.selectProfileByLocalpartStmt, err = db.Prepare(selectProfileByLocalpartSQL); err != nil { + return + } + if s.setAvatarURLStmt, err = db.Prepare(setAvatarURLSQL); err != nil { + return + } + return +} + +func (s *profilesStatements) insertProfile(localpart string) (err error) { + _, err = s.insertProfileStmt.Exec(localpart, "", "") + return +} + +func (s *profilesStatements) selectProfileByLocalpart(localpart string) (*authtypes.Profile, error) { + var profile authtypes.Profile + err := s.selectProfileByLocalpartStmt.QueryRow(localpart).Scan(&profile.Localpart, &profile.DisplayName, &profile.AvatarURL) + return &profile, err +} + +func (s *profilesStatements) setAvatarURL(localpart string, avatarURL string) (err error) { + _, err = s.setAvatarURLStmt.Exec(avatarURL, localpart) + return +} diff --git a/src/github.com/matrix-org/dendrite/clientapi/auth/storage/accounts/storage.go b/src/github.com/matrix-org/dendrite/clientapi/auth/storage/accounts/storage.go index 1f1499bbd..6c6fde9e8 100644 --- a/src/github.com/matrix-org/dendrite/clientapi/auth/storage/accounts/storage.go +++ b/src/github.com/matrix-org/dendrite/clientapi/auth/storage/accounts/storage.go @@ -28,6 +28,7 @@ import ( type Database struct { db *sql.DB accounts accountsStatements + profiles profilesStatements } // NewDatabase creates a new accounts database @@ -41,7 +42,11 @@ func NewDatabase(dataSourceName string, serverName gomatrixserverlib.ServerName) if err = a.prepare(db, serverName); err != nil { return nil, err } - return &Database{db, a}, nil + p := profilesStatements{} + if err = p.prepare(db); err != nil { + return nil, err + } + return &Database{db, a, p}, nil } // GetAccountByPassword returns the account associated with the given localpart and password. @@ -57,6 +62,10 @@ func (d *Database) GetAccountByPassword(localpart, plaintextPassword string) (*a return d.accounts.selectAccountByLocalpart(localpart) } +func (d *Database) GetProfileByLocalpart(localpart string) (*authtypes.Profile, error) { + return d.profiles.selectProfileByLocalpart(localpart) +} + // CreateAccount makes a new account with the given login name and password. If no password is supplied, // the account will be a passwordless account. func (d *Database) CreateAccount(localpart, plaintextPassword string) (*authtypes.Account, error) { @@ -64,6 +73,9 @@ func (d *Database) CreateAccount(localpart, plaintextPassword string) (*authtype if err != nil { return nil, err } + if err := d.profiles.insertProfile(localpart); err != nil { + return nil, err + } return d.accounts.insertAccount(localpart, hash) } diff --git a/src/github.com/matrix-org/dendrite/clientapi/readers/profile.go b/src/github.com/matrix-org/dendrite/clientapi/readers/profile.go new file mode 100644 index 000000000..693bf860b --- /dev/null +++ b/src/github.com/matrix-org/dendrite/clientapi/readers/profile.go @@ -0,0 +1,70 @@ +// Copyright 2017 Vector Creations Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readers + +import ( + "fmt" + "net/http" + "strings" + + "github.com/matrix-org/dendrite/clientapi/auth/storage/accounts" + // "github.com/matrix-org/dendrite/clientapi/httputil" + "github.com/matrix-org/dendrite/clientapi/jsonerror" + // "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/util" +) + +type profileResponse struct { + AvatarURL string `json:"avatar_url"` + DisplayName string `json:"displayname"` +} + +func Profile( + req *http.Request, accountDB *accounts.Database, userID string, +) util.JSONResponse { + if req.Method == "GET" { + localpart := getLocalPart(userID) + profile, err := accountDB.GetProfileByLocalpart(localpart) + if err == nil { + res := profileResponse{ + AvatarURL: profile.AvatarURL, + DisplayName: profile.DisplayName, + } + return util.JSONResponse{ + Code: 200, + JSON: res, + } + } + return util.JSONResponse{ + Code: 500, + JSON: jsonerror.Unknown("Failed to load user profile"), + } + } + return util.JSONResponse{ + Code: 405, + JSON: jsonerror.NotFound("Bad method"), + } +} + +func getLocalPart(userID string) string { + if !strings.HasPrefix(userID, "@") { + panic(fmt.Errorf("Invalid user ID")) + } + + // Get the part before ":" + username := strings.Split(userID, ":")[0] + // Return the part after the "@" + return strings.Split(username, "@")[1] +} diff --git a/src/github.com/matrix-org/dendrite/clientapi/routing/routing.go b/src/github.com/matrix-org/dendrite/clientapi/routing/routing.go index ce895c4bc..f1146840a 100644 --- a/src/github.com/matrix-org/dendrite/clientapi/routing/routing.go +++ b/src/github.com/matrix-org/dendrite/clientapi/routing/routing.go @@ -164,10 +164,8 @@ func Setup( r0mux.Handle("/profile/{userID}", common.MakeAPI("profile", func(req *http.Request) util.JSONResponse { // TODO: Get profile data for user ID - return util.JSONResponse{ - Code: 200, - JSON: struct{}{}, - } + vars := mux.Vars(req) + return readers.Profile(req, accountDB, vars["userID"]) }), ) @@ -237,13 +235,6 @@ func Setup( }), ) - r0mux.Handle("/profile/{userID}/displayname", - common.MakeAPI("profile_displayname", func(req *http.Request) util.JSONResponse { - // TODO: Set and get the displayname - return util.JSONResponse{Code: 200, JSON: struct{}{}} - }), - ) - r0mux.Handle("/user/{userID}/account_data/{type}", common.MakeAPI("user_account_data", func(req *http.Request) util.JSONResponse { // TODO: Set and get the account_data