Skip to content

Commit

Permalink
Merge pull request #318 from linkernetworks/johnlin/update-password
Browse files Browse the repository at this point in the history
[Task] update password
  • Loading branch information
John-Lin authored Sep 20, 2018
2 parents 51d30ca + d28b144 commit 5a6b86d
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 10 deletions.
36 changes: 27 additions & 9 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- [Signup](#signup)
- [Verify Token](#verify-token)
- [Signin](#signin)
- [Update Password](#update-password)
- [Create User](#create-user)
- [List User](#list-user)
- [Get User](#get-user)
Expand Down Expand Up @@ -59,14 +60,13 @@
- [Delete Namespace](#delete-namespace)
- [OVS](#ovs)
- [Get PortInfos](#get-portinfos)



## User

### Signup

**POST /v1/user/signup**
**POST /v1/users/signup**

No need to give a role, server will assign a "user" role.

Expand Down Expand Up @@ -105,7 +105,7 @@ Response Data:

### Verify Token

**GET /v1/user/verify/auth**
**GET /v1/users/verify/auth**

with a authorization JWT key

Expand Down Expand Up @@ -141,6 +141,28 @@ Response Data:
}
```

### Update Password

**PUT /v1/users/password**

Example:

```json
{
"username":"[email protected]",
"password":"password"
}
```

Response Data:

```json
{
"error": false,
"message": "password successfully changed"
}
```


### Create User

Expand Down Expand Up @@ -185,9 +207,7 @@ Response Data:

Request

```
GET /v1/users
```
**GET /v1/users**


Response Data:
Expand Down Expand Up @@ -262,9 +282,7 @@ Response

Request

```
DELETE /v1/users/5b5aba2d7a3172bca6f1e280
```
**DELETE /v1/users/5b5aba2d7a3172bca6f1e280**

Response Data

Expand Down
60 changes: 60 additions & 0 deletions src/server/handler_user.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,66 @@ func verifyTokenHandler(ctx *web.Context) {
http.Redirect(resp.ResponseWriter, req.Request, "/v1/users/"+userID, 303)
}

func patchPasswordHandler(ctx *web.Context) {
sp, req, resp := ctx.ServiceProvider, ctx.Request, ctx.Response

session := sp.Mongo.NewSession()
defer session.Close()

userID, ok := req.Attribute("UserID").(string)
if !ok {
response.Unauthorized(req.Request, resp.ResponseWriter, fmt.Errorf("Unauthorized: User ID is empty"))
return
}

user, err := backend.FindUserByID(session, bson.ObjectIdHex(userID))
if err != nil {
response.Unauthorized(req.Request, resp.ResponseWriter, fmt.Errorf("Unauthorized: User ID not found"))
return
}

var newCred entity.LoginCredential
if err := req.ReadEntity(&newCred); err != nil {
response.BadRequest(req.Request, resp.ResponseWriter, err)
return
}

// To verify that users only can change themselves password
if user.LoginCredential.Username != newCred.Username {
response.Unauthorized(req.Request, resp.ResponseWriter, fmt.Errorf("Unauthorized: User ID match"))
return
}

if err := sp.Validator.Struct(newCred); err != nil {
response.BadRequest(req.Request, resp.ResponseWriter, err)
return
}

hashedPassword, err := utils.HashPassword(newCred.Password)
if err != nil {
response.BadRequest(req.Request, resp.ResponseWriter, err)
return
}
newCred.Password = hashedPassword

user.LoginCredential = newCred
modifier := bson.M{
"$set": user,
}
query := bson.M{
"_id": user.ID,
}
if err := session.Update(entity.UserCollectionName, query, modifier); err != nil {
response.InternalServerError(req.Request, resp, err)
return
}

resp.WriteEntity(response.ActionResponse{
Error: false,
Message: "password successfully changed",
})
}

func signInUserHandler(ctx *web.Context) {
sp, req, resp := ctx.ServiceProvider, ctx.Request, ctx.Response

Expand Down
76 changes: 76 additions & 0 deletions src/server/handler_user_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ import (
"github.com/linkernetworks/mongo"
"github.com/linkernetworks/vortex/src/config"
"github.com/linkernetworks/vortex/src/entity"
response "github.com/linkernetworks/vortex/src/net/http"
"github.com/linkernetworks/vortex/src/serviceprovider"
"github.com/linkernetworks/vortex/src/utils"
"github.com/moby/moby/pkg/namesgenerator"
"github.com/stretchr/testify/suite"
"gopkg.in/mgo.v2/bson"
Expand Down Expand Up @@ -115,6 +117,80 @@ func (suite *UserTestSuite) TestVerifyInvalidToken() {
assertResponseCode(suite.T(), http.StatusUnauthorized, httpWriter)
}

func (suite *UserTestSuite) TestPatchPassword() {
var resp response.ActionResponse
testUsername := namesgenerator.GetRandomName(0) + "@linkernetworks.com"
// given a user already signup
user := entity.User{
ID: bson.NewObjectId(),
LoginCredential: entity.LoginCredential{
Username: testUsername,
Password: "password",
},
DisplayName: "John Doe",
FirstName: "John",
LastName: "Doe",
PhoneNumber: "0999999999",
}

bodyBytes, err := json.MarshalIndent(user, "", " ")
suite.NoError(err)

bodyReader := strings.NewReader(string(bodyBytes))
httpRequest, err := http.NewRequest("POST", "http://localhost:7890/v1/users/signup", bodyReader)
suite.NoError(err)

httpRequest.Header.Add("Content-Type", "application/json")
httpWriter := httptest.NewRecorder()
suite.wc.Dispatch(httpWriter, httpRequest)
assertResponseCode(suite.T(), http.StatusCreated, httpWriter)
defer suite.session.Remove(entity.UserCollectionName, "loginCredential.username", user.LoginCredential.Username)

// do signin to get a jwt bearer
bodyBytesSignIn, err := json.MarshalIndent(user.LoginCredential, "", " ")
suite.NoError(err)

bodyReaderSignIn := strings.NewReader(string(bodyBytesSignIn))
httpRequestSignIn, err := http.NewRequest("POST", "http://localhost:7890/v1/users/signin", bodyReaderSignIn)
suite.NoError(err)

httpRequestSignIn.Header.Add("Content-Type", "application/json")
httpWriterSignIn := httptest.NewRecorder()
suite.wc.Dispatch(httpWriterSignIn, httpRequestSignIn)
assertResponseCode(suite.T(), http.StatusOK, httpWriterSignIn)

decoder := json.NewDecoder(httpWriterSignIn.Body)
decoder.Decode(&resp)
JWTBearer := "Bearer " + resp.Message

// start to change the password
newCred := entity.LoginCredential{
Username: testUsername,
Password: "p@ssw0rd",
}

newCredBodyBytes, err := json.MarshalIndent(newCred, "", " ")
suite.NoError(err)

newCredBodyReader := strings.NewReader(string(newCredBodyBytes))
patchPasswordHTTPRequest, err := http.NewRequest("PUT", "http://localhost:7890/v1/users/password", newCredBodyReader)
suite.NoError(err)

patchPasswordHTTPRequest.Header.Add("Content-Type", "application/json")
patchPasswordHTTPRequest.Header.Add("Authorization", JWTBearer)
patchPasswordHTTPWriter := httptest.NewRecorder()
suite.wc.Dispatch(patchPasswordHTTPWriter, patchPasswordHTTPRequest)
assertResponseCode(suite.T(), http.StatusOK, patchPasswordHTTPWriter)

// load data to check
retUser := entity.User{}
err = suite.session.FindOne(entity.UserCollectionName, bson.M{"loginCredential.username": testUsername}, &retUser)
suite.NoError(err)

isCorrect := utils.CheckPasswordHash(newCred.Password, retUser.LoginCredential.Password)
suite.True(isCorrect)
}

func (suite *UserTestSuite) TestSignUpFailedUser() {
sameUsername := namesgenerator.GetRandomName(0) + "@linkernetworks.com"
// given a user already in mongodb
Expand Down
3 changes: 2 additions & 1 deletion src/server/route.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ func newUserService(sp *serviceprovider.Container) *restful.WebService {
// user role can access
webService.Route(webService.GET("/{id}").Filter(validateTokenMiddleware).To(handler.RESTfulServiceHandler(sp, getUserHandler)))
webService.Route(webService.GET("/verify/auth").Filter(validateTokenMiddleware).To(handler.RESTfulServiceHandler(sp, verifyTokenHandler)))
webService.Route(webService.PUT("/password").Filter(validateTokenMiddleware).To(handler.RESTfulServiceHandler(sp, patchPasswordHandler)))
return webService
}

Expand Down Expand Up @@ -175,7 +176,7 @@ func newMonitoringService(sp *serviceprovider.Container) *restful.WebService {
// pod
webService.Route(webService.GET("/pods").To(handler.RESTfulServiceHandler(sp, listPodMetricsHandler)))
webService.Route(webService.GET("/pods/{pod}").To(handler.RESTfulServiceHandler(sp, getPodMetricsHandler)))
//container
// container
webService.Route(webService.GET("/pods/{pod}/{container}").To(handler.RESTfulServiceHandler(sp, getContainerMetricsHandler)))
// service
webService.Route(webService.GET("/services").To(handler.RESTfulServiceHandler(sp, listServiceMetricsHandler)))
Expand Down

0 comments on commit 5a6b86d

Please sign in to comment.