diff --git a/Gopkg.lock b/Gopkg.lock index 2fe22e22..afb479bb 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -41,7 +41,7 @@ branch = "master" name = "github.com/miniflux/miniflux-go" packages = ["."] - revision = "60d72460e62282aa90cb43fa3a87596900b87678" + revision = "3d654932d84b6afdbd5e66b34b08392f62229e61" [[projects]] name = "github.com/tdewolff/minify" diff --git a/integration_test.go b/integration_test.go index a76d8c76..0a53bef7 100644 --- a/integration_test.go +++ b/integration_test.go @@ -17,7 +17,7 @@ import ( ) const ( - testBaseURL = "http://127.0.0.1:8080" + testBaseURL = "http://127.0.0.1:8080/" testAdminUsername = "admin" testAdminPassword = "test123" testStandardPassword = "secret" @@ -136,7 +136,7 @@ func TestRemoveUser(t *testing.T) { } } -func TestGetUser(t *testing.T) { +func TestGetUserByID(t *testing.T) { username := getRandomUsername() client := miniflux.NewClient(testBaseURL, testAdminUsername, testAdminPassword) user, err := client.CreateUser(username, testStandardPassword, false) @@ -144,7 +144,63 @@ func TestGetUser(t *testing.T) { t.Fatal(err) } - user, err = client.User(user.ID) + _, err = client.UserByID(99999) + if err == nil { + t.Fatal(`Should returns a 404`) + } + + user, err = client.UserByID(user.ID) + if err != nil { + t.Fatal(err) + } + + if user.ID == 0 { + t.Fatalf(`Invalid userID, got "%v"`, user.ID) + } + + if user.Username != username { + t.Fatalf(`Invalid username, got "%v" instead of "%v"`, user.Username, username) + } + + if user.Password != "" { + t.Fatalf(`Invalid password, got "%v"`, user.Password) + } + + if user.Language != "en_US" { + t.Fatalf(`Invalid language, got "%v"`, user.Language) + } + + if user.Theme != "default" { + t.Fatalf(`Invalid theme, got "%v"`, user.Theme) + } + + if user.Timezone != "UTC" { + t.Fatalf(`Invalid timezone, got "%v"`, user.Timezone) + } + + if user.IsAdmin { + t.Fatalf(`Invalid role, got "%v"`, user.IsAdmin) + } + + if user.LastLoginAt != nil { + t.Fatalf(`Invalid last login date, got "%v"`, user.LastLoginAt) + } +} + +func TestGetUserByUsername(t *testing.T) { + username := getRandomUsername() + client := miniflux.NewClient(testBaseURL, testAdminUsername, testAdminPassword) + user, err := client.CreateUser(username, testStandardPassword, false) + if err != nil { + t.Fatal(err) + } + + _, err = client.UserByUsername("missinguser") + if err == nil { + t.Fatal(`Should returns a 404`) + } + + user, err = client.UserByUsername(username) if err != nil { t.Fatal(err) } @@ -256,7 +312,7 @@ func TestCannotGetUserAsNonAdmin(t *testing.T) { } client = miniflux.NewClient(testBaseURL, username, testStandardPassword) - _, err = client.User(user.ID) + _, err = client.UserByID(user.ID) if err == nil { t.Fatal(`Standard users should not be able to get any users`) } diff --git a/server/api/controller/user.go b/server/api/controller/user.go index 8ca7fd2c..a9259081 100644 --- a/server/api/controller/user.go +++ b/server/api/controller/user.go @@ -88,8 +88,8 @@ func (c *Controller) UpdateUser(ctx *core.Context, request *core.Request, respon response.JSON().Created(originalUser) } -// GetUsers is the API handler to get the list of users. -func (c *Controller) GetUsers(ctx *core.Context, request *core.Request, response *core.Response) { +// Users is the API handler to get the list of users. +func (c *Controller) Users(ctx *core.Context, request *core.Request, response *core.Response) { if !ctx.IsAdminUser() { response.JSON().Forbidden() return @@ -104,8 +104,8 @@ func (c *Controller) GetUsers(ctx *core.Context, request *core.Request, response response.JSON().Standard(users) } -// GetUser is the API handler to fetch the given user. -func (c *Controller) GetUser(ctx *core.Context, request *core.Request, response *core.Response) { +// UserByID is the API handler to fetch the given user by the ID. +func (c *Controller) UserByID(ctx *core.Context, request *core.Request, response *core.Response) { if !ctx.IsAdminUser() { response.JSON().Forbidden() return @@ -131,6 +131,28 @@ func (c *Controller) GetUser(ctx *core.Context, request *core.Request, response response.JSON().Standard(user) } +// UserByUsername is the API handler to fetch the given user by the username. +func (c *Controller) UserByUsername(ctx *core.Context, request *core.Request, response *core.Response) { + if !ctx.IsAdminUser() { + response.JSON().Forbidden() + return + } + + username := request.StringParam("username", "") + user, err := c.store.UserByUsername(username) + if err != nil { + response.JSON().BadRequest(errors.New("Unable to fetch this user from the database")) + return + } + + if user == nil { + response.JSON().NotFound(errors.New("User not found")) + return + } + + response.JSON().Standard(user) +} + // RemoveUser is the API handler to remove an existing user. func (c *Controller) RemoveUser(ctx *core.Context, request *core.Request, response *core.Response) { if !ctx.IsAdminUser() { diff --git a/server/routes.go b/server/routes.go index ffcdc940..33d6dece 100644 --- a/server/routes.go +++ b/server/routes.go @@ -49,10 +49,11 @@ func getRoutes(cfg *config.Config, store *storage.Storage, feedHandler *feed.Han router.Handle("/fever/", feverHandler.Use(feverController.Handler)) router.Handle("/v1/users", apiHandler.Use(apiController.CreateUser)).Methods("POST") - router.Handle("/v1/users", apiHandler.Use(apiController.GetUsers)).Methods("GET") - router.Handle("/v1/users/{userID}", apiHandler.Use(apiController.GetUser)).Methods("GET") - router.Handle("/v1/users/{userID}", apiHandler.Use(apiController.UpdateUser)).Methods("PUT") - router.Handle("/v1/users/{userID}", apiHandler.Use(apiController.RemoveUser)).Methods("DELETE") + router.Handle("/v1/users", apiHandler.Use(apiController.Users)).Methods("GET") + router.Handle("/v1/users/{userID:[0-9]+}", apiHandler.Use(apiController.UserByID)).Methods("GET") + router.Handle("/v1/users/{userID:[0-9]+}", apiHandler.Use(apiController.UpdateUser)).Methods("PUT") + router.Handle("/v1/users/{userID:[0-9]+}", apiHandler.Use(apiController.RemoveUser)).Methods("DELETE") + router.Handle("/v1/users/{username}", apiHandler.Use(apiController.UserByUsername)).Methods("GET") router.Handle("/v1/categories", apiHandler.Use(apiController.CreateCategory)).Methods("POST") router.Handle("/v1/categories", apiHandler.Use(apiController.GetCategories)).Methods("GET") diff --git a/vendor/github.com/miniflux/miniflux-go/client.go b/vendor/github.com/miniflux/miniflux-go/client.go index 7350a704..8440a311 100644 --- a/vendor/github.com/miniflux/miniflux-go/client.go +++ b/vendor/github.com/miniflux/miniflux-go/client.go @@ -33,8 +33,8 @@ func (c *Client) Users() (Users, error) { return users, nil } -// User returns a single user. -func (c *Client) User(userID int64) (*User, error) { +// UserByID returns a single user. +func (c *Client) UserByID(userID int64) (*User, error) { body, err := c.request.Get(fmt.Sprintf("/v1/users/%d", userID)) if err != nil { return nil, err @@ -50,6 +50,23 @@ func (c *Client) User(userID int64) (*User, error) { return &user, nil } +// UserByUsername returns a single user. +func (c *Client) UserByUsername(username string) (*User, error) { + body, err := c.request.Get(fmt.Sprintf("/v1/users/%s", username)) + if err != nil { + return nil, err + } + defer body.Close() + + var user User + decoder := json.NewDecoder(body) + if err := decoder.Decode(&user); err != nil { + return nil, fmt.Errorf("miniflux: response error (%v)", err) + } + + return &user, nil +} + // CreateUser creates a new user in the system. func (c *Client) CreateUser(username, password string, isAdmin bool) (*User, error) { body, err := c.request.Post("/v1/users", &User{Username: username, Password: password, IsAdmin: isAdmin}) diff --git a/vendor/github.com/miniflux/miniflux-go/request.go b/vendor/github.com/miniflux/miniflux-go/request.go index 17baf4e4..a24a4254 100644 --- a/vendor/github.com/miniflux/miniflux-go/request.go +++ b/vendor/github.com/miniflux/miniflux-go/request.go @@ -55,6 +55,10 @@ func (r *request) Delete(path string) (io.ReadCloser, error) { } func (r *request) execute(method, path string, data interface{}) (io.ReadCloser, error) { + if r.endpoint[len(r.endpoint)-1:] == "/" { + r.endpoint = r.endpoint[:len(r.endpoint)-1] + } + u, err := url.Parse(r.endpoint + path) if err != nil { return nil, err @@ -126,11 +130,3 @@ func (r *request) toJSON(v interface{}) []byte { return b } - -func newRequest(endpoint, username, password string) *request { - return &request{ - endpoint: endpoint, - username: username, - password: password, - } -}