// 1. Generate token and verify it is OK
// 2. As a root: Update the user privilege, verify the results
// 3. As a root: Update the user privilege to undefined previlege, verify the results are StatusBadRequest
// 4. As a root: Update the root privilege, verify that it is not allowed
// 5. As the user: Update the user privilege, verify that it is not allowed
func TestUpdatePrivilege(t *testing.T) {
	userName := usersName[0]

	initAListOfUsers(t, usersName)

	url := listener + servicePath + fmt.Sprintf(cr.ConvertCommandToRequest(urlCommands[handleAuthenticateCommand]), verifyPath)
	data := cr.StringMessage{Str: cr.GetMessageStr}
	exeCommandCheckRes(t, cr.HTTPGetStr, url, http.StatusOK, "", data)

	url = listener + servicePath + fmt.Sprintf(cr.ConvertCommandToRequest(urlCommands[handleUserPwdCommand]), usersPath, userName, privilegePath)
	okURLJ := cr.URL{URL: fmt.Sprintf("%v/%v", servicePath, userName)}
	privilege, _ := json.Marshal(privilegePwd{Privilege: am.SuperUserPermission})
	exeCommandCheckRes(t, cr.HTTPPatchStr, url, http.StatusCreated, string(privilege), okURLJ)

	privilege, _ = json.Marshal(privilegePwd{Privilege: "no p"})
	exeCommandCheckRes(t, cr.HTTPPatchStr, url, http.StatusBadRequest, string(privilege), cr.Error{Code: http.StatusBadRequest})

	url = listener + servicePath + fmt.Sprintf(cr.ConvertCommandToRequest(urlCommands[handleUserPwdCommand]), usersPath, defs.RootUserName, privilegePath)
	okURLJ = cr.URL{URL: fmt.Sprintf("%v/%v", servicePath, defs.RootUserName)}
	privilege, _ = json.Marshal(privilegePwd{Privilege: am.SuperUserPermission})
	exeCommandCheckRes(t, cr.HTTPPatchStr, url, http.StatusBadRequest, string(privilege), cr.Error{Code: http.StatusBadRequest})

	cookieStr, _ := app.GenerateToken(userName, am.UserPermission, false, clientIP, stRestful.SignKey)
	cr.TestSetCookie(cookieStr)
	exeCommandCheckRes(t, cr.HTTPPatchStr, url, http.StatusMethodNotAllowed, string(privilege), cr.Error{Code: http.StatusMethodNotAllowed})
}
func init() {
	logger.Init(ioutil.Discard, ioutil.Discard, ioutil.Discard, ioutil.Discard)
	privateKeyFilePath := flag.String("rsa-private", "./dist/key.private", "RSA private key file path")
	secureKeyFilePath := flag.String("secure-key", "./dist/secureKey", "password to encrypt the secure storage")
	usersDataPath := flag.String("data-file", "./dist/data.txt", "Login information file")
	flag.Parse()

	servicePath = cr.ServicePathPrefix + cr.Version + amPrefix
	resourcePath = listener + servicePath + usersPath

	usersList := en.New()
	signKey, verifyKey := app.SetupAToken(*privateKeyFilePath)
	loginKey := ss.GetSecureKey(*secureKeyFilePath)
	en.LoadInfo(*usersDataPath, loginKey, usersList)

	stRestful = libsecurityRestful.NewLibsecurityRestful()
	stRestful.SetData(usersList, loginKey, verifyKey, signKey, nil)

	rootCookieStr, _ := app.GenerateToken(defs.RootUserName, am.SuperUserPermission, false, clientIP, signKey)
	cr.TestSetCookie(rootCookieStr)

	for _, name := range usersName {
		stRestful.UsersList.AddUser(name)
	}

	go runServer()
	time.Sleep(100 * time.Millisecond)
}
func (l AmRestful) restUpdatePwd(request *restful.Request, response *restful.Response) {
	var secrets cr.UpdateSecret

	err := request.ReadEntity(&secrets)
	if err != nil {
		l.setError(response, http.StatusBadRequest, err)
		return
	}
	userName := request.PathParameter(userIDParam)
	data := l.getAM(request, response, userName)
	if data == nil {
		return
	}
	tPwd, err := salt.GenerateSaltedPassword([]byte(secrets.OldPassword), password.MinPasswordLength, password.MaxPasswordLength, data.Pwd.Salt, -1)
	oldPwd := password.GetHashedPwd(tPwd)
	err = data.UpdateUserPwd(userName, oldPwd, []byte(secrets.NewPassword), false)
	if err != nil {
		l.setError(response, http.StatusBadRequest, err)
		return
	}
	// each time the password is updated, the token is extanded
	tokenStr, err := app.GenerateToken(userName, data.Privilege, false, getIPAddress(request), l.st.SignKey)
	if err != nil {
		l.setError(response, http.StatusInternalServerError, err)
		return
	}
	addLoginCookie(response, tokenStr)
	response.WriteHeaderAndEntity(http.StatusCreated, l.getURLPath(request, userName))
}
// TODO should I force to do logout first
func (l AmRestful) restAm(request *restful.Request, response *restful.Response) {
	var tUserInfo pUserData

	err := request.ReadEntity(&tUserInfo)
	if err != nil {
		l.setError(response, http.StatusNotFound, err)
		return
	}
	userInfo := userData{tUserInfo.Name, []byte(tUserInfo.Password)}

	data, err := l.st.UsersList.GetEntityAccount(userInfo.Name, []byte(userInfo.Password))
	if err != nil {
		l.setError(response, http.StatusMethodNotAllowed, err)
		return
	}
	temporaryPwd := false
	if time.Now().After(data.Pwd.Expiration) {
		temporaryPwd = true
	}
	tokenStr, err := app.GenerateToken(userInfo.Name, data.Privilege, temporaryPwd, getIPAddress(request), l.st.SignKey)
	if err != nil {
		l.setError(response, http.StatusInternalServerError, err)
		return
	}
	logger.Info.Println("User:"******"is authenticated")
	addLoginCookie(response, tokenStr)
	response.WriteHeaderAndEntity(http.StatusOK, cr.Match{Match: true, Message: fmt.Sprintf("User '%v' is authenticated", userInfo.Name)})
}
// 1. As a root:
//	1.1 Reset user password
//	1.2 Success get user account info
//	1.3 Fail to reset root user password
// 2. As a user:
//	2.1 login with the new password
//	2.2 Fail to get account info
//	2.3 Change account password
//	2.4 Success get account info
func TestResetPassword(t *testing.T) {
	userName := usersName[0]
	updatedPwd := secretCode + "1"

	initAListOfUsers(t, usersName)
	// reset user password
	url := listener + servicePath + fmt.Sprintf(cr.ConvertCommandToRequest(urlCommands[handleUserCommand]), usersPath, userName)
	newPwd := exeCommandCheckRes(t, cr.HTTPPatchStr, url, http.StatusCreated, cr.GetMessageStr, cr.StringMessage{Str: cr.GetMessageStr})

	// fail to reset root user password
	url = listener + servicePath + fmt.Sprintf(cr.ConvertCommandToRequest(urlCommands[handleUserCommand]), usersPath, defs.RootUserName)
	exeCommandCheckRes(t, cr.HTTPPatchStr, url, http.StatusBadRequest, "", cr.Match{Match: false})

	// get user info
	url = resourcePath + "/" + userName
	data, _ := stRestful.UsersList.GetPropertyAttachedToEntity(userName, propertyName)
	exeCommandCheckRes(t, cr.HTTPGetStr, url, http.StatusOK, "", data.(*am.AmUserInfo))

	// login as user and fail to get user info
	d := data.(*am.AmUserInfo)
	cookieStr, _ := app.GenerateToken(userName, am.UserPermission, d.Pwd.TemporaryPwd, clientIP, stRestful.SignKey)
	cr.TestSetCookie(cookieStr)
	exeCommandCheckRes(t, cr.HTTPGetStr, url, http.StatusMethodNotAllowed, "", cr.Match{Match: false})

	var newPwdStr cr.Secret
	json.Unmarshal([]byte(newPwd), &newPwdStr)
	// update user account password
	url = listener + servicePath + fmt.Sprintf(cr.ConvertCommandToRequest(urlCommands[handleUserPwdCommand]), usersPath, userName, pwdPath)
	okURLJ := cr.URL{URL: fmt.Sprintf("%v/%v", servicePath, userName)}
	pwd, _ := json.Marshal(cr.UpdateSecret{OldPassword: newPwdStr.Secret, NewPassword: updatedPwd})
	exeCommandCheckRes(t, cr.HTTPPatchStr, url, http.StatusCreated, string(pwd), okURLJ)

	// login as root and get user info
	cookieStr, _ = app.GenerateToken(defs.RootUserName, am.SuperUserPermission, false, clientIP, stRestful.SignKey)
	cr.TestSetCookie(cookieStr)
	url = resourcePath + "/" + userName
	data, _ = stRestful.UsersList.GetPropertyAttachedToEntity(userName, propertyName)
	exeCommandCheckRes(t, cr.HTTPGetStr, url, http.StatusOK, "", data.(*am.AmUserInfo))

	// login as user and get user info
	d = data.(*am.AmUserInfo)
	cookieStr, _ = app.GenerateToken(userName, am.UserPermission, d.Pwd.TemporaryPwd, clientIP, stRestful.SignKey)
	cr.TestSetCookie(cookieStr)
	url = resourcePath + "/" + userName
	exeCommandCheckRes(t, cr.HTTPGetStr, url, http.StatusOK, "", data.(*am.AmUserInfo))
}
func initAListOfUsers(t *testing.T, usersList []string) string {
	cookieStr, _ := app.GenerateToken(defs.RootUserName, am.SuperUserPermission, false, clientIP, stRestful.SignKey)
	cr.TestSetCookie(cookieStr)

	for _, name := range usersList {
		okURLJ := cr.URL{URL: fmt.Sprintf("%v/%v", servicePath, name)}
		url := resourcePath + "/" + name
		exeCommandCheckRes(t, cr.HTTPPutStr, url, http.StatusCreated, string(uData), okURLJ)
		data, _ := stRestful.UsersList.GetPropertyAttachedToEntity(name, propertyName)
		exeCommandCheckRes(t, cr.HTTPGetStr, url, http.StatusOK, "", data.(*am.AmUserInfo))
	}
	return string(uData)
}
// 1. As a user: Update the password, verify the results
// 2. As a root: Fail to update the password to the root
// 3. As a root: Update the password, verify that it is allowed
func TestUpdatePassword(t *testing.T) {
	userName := usersName[0]

	//	initAListOfUsers(t, usersName)
	cookieStr, _ := app.GenerateToken(userName, am.UserPermission, false, clientIP, stRestful.SignKey)
	cr.TestSetCookie(cookieStr)

	url := listener + servicePath + fmt.Sprintf(cr.ConvertCommandToRequest(urlCommands[handleUserPwdCommand]), usersPath, userName, pwdPath)
	okURLJ := cr.URL{URL: fmt.Sprintf("%v/%v", servicePath, userName)}
	pwd, _ := json.Marshal(cr.UpdateSecret{OldPassword: secretCode, NewPassword: secretCode + "1"})
	exeCommandCheckRes(t, cr.HTTPPatchStr, url, http.StatusCreated, string(pwd), okURLJ)

	url = listener + servicePath + fmt.Sprintf(cr.ConvertCommandToRequest(urlCommands[handleUserPwdCommand]), usersPath, defs.RootUserName, pwdPath)
	okURLJ = cr.URL{URL: fmt.Sprintf("%v/%v", servicePath, defs.RootUserName)}
	pwd, _ = json.Marshal(cr.UpdateSecret{OldPassword: rootPwd, NewPassword: secretCode + "2"})
	exeCommandCheckRes(t, cr.HTTPPatchStr, url, http.StatusMethodNotAllowed, string(pwd), cr.Error{Code: http.StatusMethodNotAllowed})

	cookieStr, _ = app.GenerateToken(defs.RootUserName, am.SuperUserPermission, false, clientIP, stRestful.SignKey)
	cr.TestSetCookie(cookieStr)
	url = listener + servicePath + fmt.Sprintf(cr.ConvertCommandToRequest(urlCommands[handleUserPwdCommand]), usersPath, defs.RootUserName, pwdPath)
	okURLJ = cr.URL{URL: fmt.Sprintf("%v/%v", servicePath, defs.RootUserName)}
	pwd, _ = json.Marshal(cr.UpdateSecret{OldPassword: rootPwd, NewPassword: secretCode + "1"})
	exeCommandCheckRes(t, cr.HTTPPatchStr, url, http.StatusCreated, string(pwd), okURLJ)
}