web/satellite: auth graphql api replaced with REST API (#3396)
This commit is contained in:
parent
b0a9438137
commit
6354b38849
@ -62,17 +62,13 @@ func (a *Auth) Token(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
var tokenResponse struct {
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
tokenResponse.Token, err = a.service.Token(ctx, tokenRequest.Email, tokenRequest.Password)
|
||||
token, err := a.service.Token(ctx, tokenRequest.Email, tokenRequest.Password)
|
||||
if err != nil {
|
||||
a.serveJSONError(w, http.StatusUnauthorized, err)
|
||||
return
|
||||
}
|
||||
|
||||
err = json.NewEncoder(w).Encode(tokenResponse)
|
||||
err = json.NewEncoder(w).Encode(token)
|
||||
if err != nil {
|
||||
a.log.Error("token handler could not encode token response", zap.Error(ErrAuthAPI.Wrap(err)))
|
||||
return
|
||||
@ -85,25 +81,39 @@ func (a *Auth) Register(w http.ResponseWriter, r *http.Request) {
|
||||
var err error
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
var request struct {
|
||||
UserInfo console.CreateUser `json:"userInfo"`
|
||||
SecretInput string `json:"secret"`
|
||||
ReferrerUserID string `json:"referrerUserID"`
|
||||
var registerData struct {
|
||||
FullName string `json:"fullName"`
|
||||
ShortName string `json:"shortName"`
|
||||
Email string `json:"email"`
|
||||
PartnerID string `json:"partnerId"`
|
||||
Password string `json:"password"`
|
||||
SecretInput string `json:"secret"`
|
||||
ReferrerUserID string `json:"referrerUserID"`
|
||||
}
|
||||
|
||||
err = json.NewDecoder(r.Body).Decode(&request)
|
||||
err = json.NewDecoder(r.Body).Decode(®isterData)
|
||||
if err != nil {
|
||||
a.serveJSONError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
secret, err := console.RegistrationSecretFromBase64(request.SecretInput)
|
||||
secret, err := console.RegistrationSecretFromBase64(registerData.SecretInput)
|
||||
if err != nil {
|
||||
a.serveJSONError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
user, err := a.service.CreateUser(ctx, request.UserInfo, secret, request.ReferrerUserID)
|
||||
user, err := a.service.CreateUser(ctx,
|
||||
console.CreateUser{
|
||||
FullName: registerData.FullName,
|
||||
ShortName: registerData.ShortName,
|
||||
Email: registerData.Email,
|
||||
PartnerID: registerData.PartnerID,
|
||||
Password: registerData.Password,
|
||||
},
|
||||
secret,
|
||||
registerData.ReferrerUserID,
|
||||
)
|
||||
if err != nil {
|
||||
a.serveJSONError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
@ -115,7 +125,7 @@ func (a *Auth) Register(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
link := a.ExternalAddress + consoleql.ActivationPath + token
|
||||
link := a.ExternalAddress + "activation/?token=" + token
|
||||
userName := user.ShortName
|
||||
if user.ShortName == "" {
|
||||
userName = user.FullName
|
||||
@ -137,8 +147,8 @@ func (a *Auth) Register(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
// Update updates user's full name and short name.
|
||||
func (a *Auth) Update(w http.ResponseWriter, r *http.Request) {
|
||||
// UpdateAccount updates user's full name and short name.
|
||||
func (a *Auth) UpdateAccount(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
var err error
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
@ -160,8 +170,8 @@ func (a *Auth) Update(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
// Get gets authorized user and take it's params.
|
||||
func (a *Auth) Get(w http.ResponseWriter, r *http.Request) {
|
||||
// GetAccount gets authorized user and take it's params.
|
||||
func (a *Auth) GetAccount(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
var err error
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
@ -193,23 +203,23 @@ func (a *Auth) Get(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
// Delete - authorizes user and deletes account by password.
|
||||
func (a *Auth) Delete(w http.ResponseWriter, r *http.Request) {
|
||||
// DeleteAccount - authorizes user and deletes account by password.
|
||||
func (a *Auth) DeleteAccount(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
var err error
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
var request struct {
|
||||
var deleteRequest struct {
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
err = json.NewDecoder(r.Body).Decode(&request)
|
||||
err = json.NewDecoder(r.Body).Decode(&deleteRequest)
|
||||
if err != nil {
|
||||
a.serveJSONError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
err = a.service.DeleteAccount(ctx, request.Password)
|
||||
err = a.service.DeleteAccount(ctx, deleteRequest.Password)
|
||||
if err != nil {
|
||||
if console.ErrUnauthorized.Has(err) {
|
||||
a.serveJSONError(w, http.StatusUnauthorized, err)
|
||||
@ -276,8 +286,8 @@ func (a *Auth) ForgotPassword(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
passwordRecoveryLink := a.ExternalAddress + consoleql.CancelPasswordRecoveryPath + recoveryToken
|
||||
cancelPasswordRecoveryLink := a.ExternalAddress + consoleql.CancelPasswordRecoveryPath + recoveryToken
|
||||
passwordRecoveryLink := a.ExternalAddress + "password-recovery/?token=" + recoveryToken
|
||||
cancelPasswordRecoveryLink := a.ExternalAddress + "cancel-password-recovery/?token=" + recoveryToken
|
||||
userName := user.ShortName
|
||||
if user.ShortName == "" {
|
||||
userName = user.FullName
|
||||
@ -309,13 +319,13 @@ func (a *Auth) ResendEmail(w http.ResponseWriter, r *http.Request) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
params := mux.Vars(r)
|
||||
val, ok := params["id"]
|
||||
id, ok := params["id"]
|
||||
if !ok {
|
||||
a.serveJSONError(w, http.StatusBadRequest, errs.New("id expected"))
|
||||
return
|
||||
}
|
||||
|
||||
userID, err := uuid.Parse(val)
|
||||
userID, err := uuid.Parse(id)
|
||||
if err != nil {
|
||||
a.serveJSONError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
@ -333,7 +343,7 @@ func (a *Auth) ResendEmail(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
link := a.ExternalAddress + consoleql.ActivationPath + token
|
||||
link := a.ExternalAddress + "activation/?token=" + token
|
||||
userName := user.ShortName
|
||||
if user.ShortName == "" {
|
||||
userName = user.FullName
|
||||
|
@ -79,13 +79,11 @@ func TestGrapqhlMutation(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
createUser := console.CreateUser{
|
||||
UserInfo: console.UserInfo{
|
||||
FullName: "John Roll",
|
||||
ShortName: "Roll",
|
||||
Email: "test@mail.test",
|
||||
PartnerID: "120bf202-8252-437e-ac12-0e364bee852e",
|
||||
},
|
||||
Password: "123a123",
|
||||
FullName: "John Roll",
|
||||
ShortName: "Roll",
|
||||
Email: "test@mail.test",
|
||||
PartnerID: "120bf202-8252-437e-ac12-0e364bee852e",
|
||||
Password: "123a123",
|
||||
}
|
||||
refUserID := ""
|
||||
|
||||
@ -126,13 +124,11 @@ func TestGrapqhlMutation(t *testing.T) {
|
||||
|
||||
t.Run("Create user mutation with partner id", func(t *testing.T) {
|
||||
newUser := console.CreateUser{
|
||||
UserInfo: console.UserInfo{
|
||||
FullName: "Green Mickey",
|
||||
ShortName: "Green",
|
||||
Email: "u1@mail.test",
|
||||
PartnerID: "120bf202-8252-437e-ac12-0e364bee852e",
|
||||
},
|
||||
Password: "123a123",
|
||||
FullName: "Green Mickey",
|
||||
ShortName: "Green",
|
||||
Email: "u1@mail.test",
|
||||
PartnerID: "120bf202-8252-437e-ac12-0e364bee852e",
|
||||
Password: "123a123",
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
@ -178,13 +174,11 @@ func TestGrapqhlMutation(t *testing.T) {
|
||||
|
||||
t.Run("Create user mutation without partner id", func(t *testing.T) {
|
||||
newUser := console.CreateUser{
|
||||
UserInfo: console.UserInfo{
|
||||
FullName: "Red Mickey",
|
||||
ShortName: "Red",
|
||||
Email: "u2@mail.test",
|
||||
PartnerID: "",
|
||||
},
|
||||
Password: "123a123",
|
||||
FullName: "Red Mickey",
|
||||
ShortName: "Red",
|
||||
Email: "u2@mail.test",
|
||||
PartnerID: "",
|
||||
Password: "123a123",
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
@ -243,24 +237,6 @@ func TestGrapqhlMutation(t *testing.T) {
|
||||
return result.Data
|
||||
}
|
||||
|
||||
t.Run("Update account mutation email only", func(t *testing.T) {
|
||||
email := "new@mail.test"
|
||||
query := fmt.Sprintf(
|
||||
"mutation {updateAccount(input:{email:\"%s\"}){id,email,fullName,shortName,createdAt}}",
|
||||
email,
|
||||
)
|
||||
|
||||
result := testQuery(t, query)
|
||||
|
||||
data := result.(map[string]interface{})
|
||||
user := data[consoleql.UpdateAccountMutation].(map[string]interface{})
|
||||
|
||||
assert.Equal(t, rootUser.ID.String(), user[consoleql.FieldID])
|
||||
assert.Equal(t, email, user[consoleql.FieldEmail])
|
||||
assert.Equal(t, rootUser.FullName, user[consoleql.FieldFullName])
|
||||
assert.Equal(t, rootUser.ShortName, user[consoleql.FieldShortName])
|
||||
})
|
||||
|
||||
t.Run("Update account mutation fullName only", func(t *testing.T) {
|
||||
fullName := "George"
|
||||
query := fmt.Sprintf(
|
||||
@ -298,13 +274,11 @@ func TestGrapqhlMutation(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("Update account mutation all info", func(t *testing.T) {
|
||||
email := "test@newmail.com"
|
||||
fullName := "Fill Goal"
|
||||
shortName := "Goal"
|
||||
|
||||
query := fmt.Sprintf(
|
||||
"mutation {updateAccount(input:{email:\"%s\",fullName:\"%s\",shortName:\"%s\"}){id,email,fullName,shortName,createdAt}}",
|
||||
email,
|
||||
"mutation {updateAccount(input:{,fullName:\"%s\",shortName:\"%s\"}){id,fullName,shortName,createdAt}}",
|
||||
fullName,
|
||||
shortName,
|
||||
)
|
||||
@ -315,7 +289,6 @@ func TestGrapqhlMutation(t *testing.T) {
|
||||
user := data[consoleql.UpdateAccountMutation].(map[string]interface{})
|
||||
|
||||
assert.Equal(t, rootUser.ID.String(), user[consoleql.FieldID])
|
||||
assert.Equal(t, email, user[consoleql.FieldEmail])
|
||||
assert.Equal(t, fullName, user[consoleql.FieldFullName])
|
||||
assert.Equal(t, shortName, user[consoleql.FieldShortName])
|
||||
|
||||
@ -421,10 +394,8 @@ func TestGrapqhlMutation(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
user1, err := service.CreateUser(authCtx, console.CreateUser{
|
||||
UserInfo: console.UserInfo{
|
||||
FullName: "User1",
|
||||
Email: "u1@mail.test",
|
||||
},
|
||||
FullName: "User1",
|
||||
Email: "u1@mail.test",
|
||||
Password: "123a123",
|
||||
}, regTokenUser1.Secret, refUserID)
|
||||
require.NoError(t, err)
|
||||
@ -447,10 +418,8 @@ func TestGrapqhlMutation(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
user2, err := service.CreateUser(authCtx, console.CreateUser{
|
||||
UserInfo: console.UserInfo{
|
||||
FullName: "User1",
|
||||
Email: "u2@mail.test",
|
||||
},
|
||||
FullName: "User1",
|
||||
Email: "u2@mail.test",
|
||||
Password: "123a123",
|
||||
}, regTokenUser2.Secret, refUserID)
|
||||
require.NoError(t, err)
|
||||
|
@ -66,12 +66,10 @@ func TestGraphqlQuery(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
createUser := console.CreateUser{
|
||||
UserInfo: console.UserInfo{
|
||||
FullName: "John",
|
||||
ShortName: "",
|
||||
Email: "mtest@mail.test",
|
||||
},
|
||||
Password: "123a123",
|
||||
FullName: "John",
|
||||
ShortName: "",
|
||||
Email: "mtest@mail.test",
|
||||
Password: "123a123",
|
||||
}
|
||||
refUserID := ""
|
||||
|
||||
@ -189,12 +187,10 @@ func TestGraphqlQuery(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
user1, err := service.CreateUser(authCtx, console.CreateUser{
|
||||
UserInfo: console.UserInfo{
|
||||
FullName: "Mickey Last",
|
||||
ShortName: "Last",
|
||||
Email: "muu1@mail.test",
|
||||
},
|
||||
Password: "123a123",
|
||||
FullName: "Mickey Last",
|
||||
ShortName: "Last",
|
||||
Password: "123a123",
|
||||
Email: "muu1@mail.test",
|
||||
}, regTokenUser1.Secret, refUserID)
|
||||
require.NoError(t, err)
|
||||
|
||||
@ -215,12 +211,10 @@ func TestGraphqlQuery(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
user2, err := service.CreateUser(authCtx, console.CreateUser{
|
||||
UserInfo: console.UserInfo{
|
||||
FullName: "Dubas Name",
|
||||
ShortName: "Name",
|
||||
Email: "muu2@mail.test",
|
||||
},
|
||||
Password: "123a123",
|
||||
FullName: "Dubas Name",
|
||||
ShortName: "Name",
|
||||
Email: "muu2@mail.test",
|
||||
Password: "123a123",
|
||||
}, regTokenUser2.Secret, refUserID)
|
||||
require.NoError(t, err)
|
||||
|
||||
@ -437,12 +431,10 @@ func TestGraphqlQuery(t *testing.T) {
|
||||
regToken, err := service.CreateRegToken(ctx, 2)
|
||||
require.NoError(t, err)
|
||||
user, err := service.CreateUser(authCtx, console.CreateUser{
|
||||
UserInfo: console.UserInfo{
|
||||
FullName: "Example User",
|
||||
ShortName: "Example",
|
||||
Email: "user@mail.test",
|
||||
},
|
||||
Password: "123a123",
|
||||
FullName: "Example User",
|
||||
ShortName: "Example",
|
||||
Email: "user@mail.test",
|
||||
Password: "123a123",
|
||||
}, regToken.Secret, refUserID)
|
||||
|
||||
require.NoError(t, err)
|
||||
@ -477,12 +469,10 @@ func TestGraphqlQuery(t *testing.T) {
|
||||
regToken, err := service.CreateRegToken(ctx, 2)
|
||||
require.NoError(t, err)
|
||||
user, err := service.CreateUser(authCtx, console.CreateUser{
|
||||
UserInfo: console.UserInfo{
|
||||
FullName: "Example User",
|
||||
ShortName: "Example",
|
||||
Email: "user1@mail.test",
|
||||
},
|
||||
Password: "123a123",
|
||||
FullName: "Example User",
|
||||
ShortName: "Example",
|
||||
Email: "user1@mail.test",
|
||||
Password: "123a123",
|
||||
}, regToken.Secret, refUserID)
|
||||
|
||||
require.NoError(t, err)
|
||||
|
@ -5,7 +5,6 @@ package consoleql
|
||||
|
||||
import (
|
||||
"github.com/graphql-go/graphql"
|
||||
"github.com/skyrings/skyring-common/tools/uuid"
|
||||
|
||||
"storj.io/storj/satellite/console"
|
||||
)
|
||||
@ -88,31 +87,21 @@ func graphqlUserInput() *graphql.InputObject {
|
||||
})
|
||||
}
|
||||
|
||||
// fromMapUserInfo creates UserInput from input args
|
||||
func fromMapUserInfo(args map[string]interface{}) (user console.UserInfo) {
|
||||
func fromMapCreateUser(args map[string]interface{}) (user console.CreateUser) {
|
||||
user.Email, _ = args[FieldEmail].(string)
|
||||
user.FullName, _ = args[FieldFullName].(string)
|
||||
user.ShortName, _ = args[FieldShortName].(string)
|
||||
user.PartnerID, _ = args[FieldPartnerID].(string)
|
||||
return
|
||||
}
|
||||
|
||||
func fromMapCreateUser(args map[string]interface{}) (user console.CreateUser) {
|
||||
user.UserInfo = fromMapUserInfo(args)
|
||||
user.Password, _ = args[FieldPassword].(string)
|
||||
user.PartnerID, _ = args[FieldPartnerID].(string)
|
||||
return
|
||||
}
|
||||
|
||||
// fillUserInfo fills satellite.UserInfo from satellite.User and input args
|
||||
func fillUserInfo(user *console.User, args map[string]interface{}) console.UserInfo {
|
||||
info := console.UserInfo{
|
||||
Email: user.Email,
|
||||
FullName: user.FullName,
|
||||
ShortName: user.ShortName,
|
||||
}
|
||||
if !user.PartnerID.IsZero() {
|
||||
info.PartnerID = user.PartnerID.String()
|
||||
}
|
||||
|
||||
for fieldName, fieldValue := range args {
|
||||
value, ok := fieldValue.(string)
|
||||
@ -121,21 +110,12 @@ func fillUserInfo(user *console.User, args map[string]interface{}) console.UserI
|
||||
}
|
||||
|
||||
switch fieldName {
|
||||
case FieldEmail:
|
||||
info.Email = value
|
||||
user.Email = value
|
||||
case FieldFullName:
|
||||
info.FullName = value
|
||||
user.FullName = value
|
||||
case FieldShortName:
|
||||
info.ShortName = value
|
||||
user.ShortName = value
|
||||
case FieldPartnerID:
|
||||
info.PartnerID = value
|
||||
partnerID, err := uuid.Parse(value)
|
||||
if err == nil {
|
||||
user.PartnerID = *partnerID
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -120,19 +120,18 @@ func NewServer(logger *zap.Logger, config Config, service *console.Service, mail
|
||||
router.HandleFunc("/api/v0/graphql", server.grapqlHandler)
|
||||
router.HandleFunc("/registrationToken/", server.createRegistrationTokenHandler)
|
||||
router.HandleFunc("/robots.txt", server.seoHandler)
|
||||
router.HandleFunc("/satelliteName", server.satelliteNameHandler).Methods(http.MethodGet)
|
||||
router.HandleFunc("/satellite-name", server.satelliteNameHandler).Methods(http.MethodGet)
|
||||
|
||||
authController := consoleapi.NewAuth(logger, service, mailService, config.ExternalAddress, config.LetUsKnowURL, config.TermsAndConditionsURL, config.ContactInfoURL)
|
||||
authController := consoleapi.NewAuth(logger, service, mailService, server.config.ExternalAddress, config.LetUsKnowURL, config.TermsAndConditionsURL, config.ContactInfoURL)
|
||||
authRouter := router.PathPrefix("/api/v0/auth").Subrouter()
|
||||
authRouter.Handle("/account", server.withAuth(http.HandlerFunc(authController.GetAccount))).Methods(http.MethodGet)
|
||||
authRouter.Handle("/account", server.withAuth(http.HandlerFunc(authController.UpdateAccount))).Methods(http.MethodPatch)
|
||||
authRouter.Handle("/account/change-password", server.withAuth(http.HandlerFunc(authController.ChangePassword))).Methods(http.MethodPost)
|
||||
authRouter.Handle("/account/delete", server.withAuth(http.HandlerFunc(authController.DeleteAccount))).Methods(http.MethodPost)
|
||||
authRouter.HandleFunc("/token", authController.Token).Methods(http.MethodPost)
|
||||
authRouter.HandleFunc("/register", authController.Register).Methods(http.MethodPost)
|
||||
authRouter.Handle("/changePassword", server.withAuth(http.HandlerFunc(authController.ChangePassword))).Methods(http.MethodPost)
|
||||
authRouter.Handle("/delete", server.withAuth(http.HandlerFunc(authController.Delete))).Methods(http.MethodDelete)
|
||||
authRouter.Handle("/", server.withAuth(http.HandlerFunc(authController.Get))).Methods(http.MethodGet)
|
||||
authRouter.Handle("/update", server.withAuth(http.HandlerFunc(authController.Update))).Methods(http.MethodPut)
|
||||
authRouter.HandleFunc("/changePassword", authController.ChangePassword).Methods(http.MethodPost)
|
||||
authRouter.HandleFunc("/forgotPassword", authController.ForgotPassword).Methods(http.MethodPost)
|
||||
authRouter.HandleFunc("/resendEmail", authController.ResendEmail).Methods(http.MethodPost)
|
||||
authRouter.HandleFunc("/forgot-password/{email}", authController.ForgotPassword).Methods(http.MethodPost)
|
||||
authRouter.HandleFunc("/resend-email/{id}", authController.ResendEmail).Methods(http.MethodPost)
|
||||
|
||||
paymentController := consoleapi.NewPayments(logger, service)
|
||||
paymentsRouter := router.PathPrefix("/api/v0/payments").Subrouter()
|
||||
|
@ -31,21 +31,15 @@ type Users interface {
|
||||
type UserInfo struct {
|
||||
FullName string `json:"fullName"`
|
||||
ShortName string `json:"shortName"`
|
||||
Email string `json:"email"`
|
||||
PartnerID string `json:"partnerId"`
|
||||
}
|
||||
|
||||
// IsValid checks UserInfo validity and returns error describing whats wrong.
|
||||
func (user *UserInfo) IsValid() error {
|
||||
var errs validationErrors
|
||||
|
||||
// validate email
|
||||
_, err := mail.ParseAddress(user.Email)
|
||||
errs.AddWrap(err)
|
||||
|
||||
// validate fullName
|
||||
if user.FullName == "" {
|
||||
errs.Add("fullName can't be empty")
|
||||
if err := validateFullName(user.FullName); err != nil {
|
||||
errs.AddWrap(err)
|
||||
}
|
||||
|
||||
return errs.Combine()
|
||||
@ -53,17 +47,24 @@ func (user *UserInfo) IsValid() error {
|
||||
|
||||
// CreateUser struct holds info for User creation.
|
||||
type CreateUser struct {
|
||||
UserInfo
|
||||
Password string `json:"password"`
|
||||
FullName string `json:"fullName"`
|
||||
ShortName string `json:"shortName"`
|
||||
Email string `json:"email"`
|
||||
PartnerID string `json:"partnerId"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
// IsValid checks CreateUser validity and returns error describing whats wrong.
|
||||
func (user *CreateUser) IsValid() error {
|
||||
var errs validationErrors
|
||||
|
||||
errs.AddWrap(user.UserInfo.IsValid())
|
||||
errs.AddWrap(validateFullName(user.FullName))
|
||||
errs.AddWrap(validatePassword(user.Password))
|
||||
|
||||
// validate email
|
||||
_, err := mail.ParseAddress(user.Email)
|
||||
errs.AddWrap(err)
|
||||
|
||||
if user.PartnerID != "" {
|
||||
_, err := uuid.Parse(user.PartnerID)
|
||||
if err != nil {
|
||||
|
@ -42,3 +42,12 @@ func validatePassword(pass string) error {
|
||||
|
||||
return errs.Combine()
|
||||
}
|
||||
|
||||
// validateFullName validates full name.
|
||||
func validateFullName(name string) error {
|
||||
if name == "" {
|
||||
return errs.New("full name can not be empty")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -54,6 +54,9 @@ func (clicker *LinkClicker) SendEmail(ctx context.Context, msg *post.Message) (e
|
||||
var sendError error
|
||||
for _, link := range links {
|
||||
response, err := http.Get(link)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
sendError = errs.Combine(sendError, err, response.Body.Close())
|
||||
}
|
||||
|
||||
|
@ -1,14 +1,17 @@
|
||||
// Copyright (C) 2019 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
import { BaseGql } from '@/api/baseGql';
|
||||
import { User } from '@/types/users';
|
||||
import { ErrorUnauthorized } from '@/api/errors/ErrorUnauthorized';
|
||||
import { UpdatedUser, User } from '@/types/users';
|
||||
import { HttpClient } from '@/utils/httpClient';
|
||||
|
||||
/**
|
||||
* AuthApiGql is a graphql implementation of Auth API.
|
||||
* AuthHttpApi is a console Auth API.
|
||||
* Exposes all auth-related functionality
|
||||
*/
|
||||
export class AuthApi extends BaseGql {
|
||||
export class AuthHttpApi {
|
||||
private readonly http: HttpClient = new HttpClient();
|
||||
private readonly ROOT_PATH: string = '/api/v0/auth';
|
||||
/**
|
||||
* Used to resend an registration confirmation email
|
||||
*
|
||||
@ -16,16 +19,13 @@ export class AuthApi extends BaseGql {
|
||||
* @throws Error
|
||||
*/
|
||||
public async resendEmail(userId: string): Promise<void> {
|
||||
const query =
|
||||
`query ($userId: String!){
|
||||
resendAccountActivationEmail(id: $userId)
|
||||
}`;
|
||||
const path = `${this.ROOT_PATH}/resend-email/${userId}`;
|
||||
const response = await this.http.post(path, userId, false);
|
||||
if (response.ok) {
|
||||
return;
|
||||
}
|
||||
|
||||
const variables = {
|
||||
userId,
|
||||
};
|
||||
|
||||
await this.query(query, variables);
|
||||
throw new Error('can not resend Email');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -36,21 +36,17 @@ export class AuthApi extends BaseGql {
|
||||
* @throws Error
|
||||
*/
|
||||
public async token(email: string, password: string): Promise<string> {
|
||||
const query =
|
||||
` query ($email: String!, $password: String!) {
|
||||
token(email: $email, password: $password) {
|
||||
token
|
||||
}
|
||||
}`;
|
||||
|
||||
const variables = {
|
||||
email,
|
||||
password,
|
||||
const path = `${this.ROOT_PATH}/token`;
|
||||
const body = {
|
||||
email: email,
|
||||
password: password,
|
||||
};
|
||||
const response = await this.http.post(path, JSON.stringify(body), false);
|
||||
if (response.ok) {
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
const response = await this.query(query, variables);
|
||||
|
||||
return response.data.token.token;
|
||||
throw new Error('can not receive authentication token');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -60,16 +56,48 @@ export class AuthApi extends BaseGql {
|
||||
* @throws Error
|
||||
*/
|
||||
public async forgotPassword(email: string): Promise<void> {
|
||||
const query =
|
||||
`query($email: String!) {
|
||||
forgotPassword(email: $email)
|
||||
}`;
|
||||
const path = `${this.ROOT_PATH}/forgot-password/${email}`;
|
||||
const response = await this.http.post(path, email, false);
|
||||
if (response.ok) {
|
||||
return;
|
||||
}
|
||||
|
||||
const variables = {
|
||||
email,
|
||||
throw new Error('can not resend password');
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to update user full and short name
|
||||
*
|
||||
* @param userInfo - full name and short name of the user
|
||||
* @throws Error
|
||||
*/
|
||||
public async update(userInfo: UpdatedUser): Promise<void> {
|
||||
const path = `${this.ROOT_PATH}/account`;
|
||||
const body = {
|
||||
fullName: userInfo.fullName,
|
||||
shortName: userInfo.shortName,
|
||||
};
|
||||
const response = await this.http.patch(path, JSON.stringify(body), true);
|
||||
if (response.ok) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.query(query, variables);
|
||||
throw new Error('can not update user data');
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to get user data
|
||||
*
|
||||
* @throws Error
|
||||
*/
|
||||
public async get(): Promise<User> {
|
||||
const path = `${this.ROOT_PATH}/account`;
|
||||
const response = await this.http.get(path, true);
|
||||
if (response.ok) {
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
throw new Error('can not get user data');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -80,22 +108,21 @@ export class AuthApi extends BaseGql {
|
||||
* @throws Error
|
||||
*/
|
||||
public async changePassword(password: string, newPassword: string): Promise<void> {
|
||||
const query =
|
||||
`mutation($password: String!, $newPassword: String!) {
|
||||
changePassword (
|
||||
password: $password,
|
||||
newPassword: $newPassword
|
||||
) {
|
||||
email
|
||||
}
|
||||
}`;
|
||||
|
||||
const variables = {
|
||||
password,
|
||||
newPassword,
|
||||
const path = `${this.ROOT_PATH}/account/change-password`;
|
||||
const body = {
|
||||
password: password,
|
||||
newPassword: newPassword,
|
||||
};
|
||||
const response = await this.http.post(path, JSON.stringify(body), true);
|
||||
if (response.ok) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.mutate(query, variables);
|
||||
if (response.status === 401) {
|
||||
throw new ErrorUnauthorized();
|
||||
}
|
||||
|
||||
throw new Error('can not change password');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -105,59 +132,49 @@ export class AuthApi extends BaseGql {
|
||||
* @throws Error
|
||||
*/
|
||||
public async delete(password: string): Promise<void> {
|
||||
const query =
|
||||
`mutation ($password: String!){
|
||||
deleteAccount(password: $password) {
|
||||
email
|
||||
}
|
||||
}`;
|
||||
|
||||
const variables = {
|
||||
password,
|
||||
const path = `${this.ROOT_PATH}/account/delete`;
|
||||
const body = {
|
||||
password: password,
|
||||
};
|
||||
const response = await this.http.post(path, JSON.stringify(body), true);
|
||||
if (response.ok) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.mutate(query, variables);
|
||||
if (response.status === 401) {
|
||||
throw new ErrorUnauthorized();
|
||||
}
|
||||
|
||||
throw new Error('can not delete user');
|
||||
}
|
||||
|
||||
// TODO: remove secret after Vanguard release
|
||||
/**
|
||||
* Used to create account
|
||||
* Used to register account
|
||||
*
|
||||
* @param user - stores user information
|
||||
* @param secret - registration token used in Vanguard release
|
||||
* @param refUserId - referral id to participate in bonus program
|
||||
* @param referrerUserId - referral id to participate in bonus program
|
||||
* @returns id of created user
|
||||
* @throws Error
|
||||
*/
|
||||
public async create(user: User, password: string, secret: string, referrerUserId: string = ''): Promise<string> {
|
||||
const query =
|
||||
`mutation($email: String!, $password: String!, $fullName: String!, $shortName: String!,
|
||||
$partnerID: String!, $referrerUserId: String!, $secret: String!) {
|
||||
createUser(
|
||||
input: {
|
||||
email: $email,
|
||||
password: $password,
|
||||
fullName: $fullName,
|
||||
shortName: $shortName,
|
||||
partnerId: $partnerID
|
||||
},
|
||||
referrerUserId: $referrerUserId,
|
||||
secret: $secret,
|
||||
) {email, id}
|
||||
}`;
|
||||
|
||||
const variables = {
|
||||
email: user.email,
|
||||
public async register(user: {fullName: string; shortName: string; email: string; partnerId: string; password: string}, secret: string, referrerUserId: string): Promise<string> {
|
||||
const path = `${this.ROOT_PATH}/register`;
|
||||
const body = {
|
||||
secret: secret,
|
||||
referrerUserId: referrerUserId ? referrerUserId : '',
|
||||
password: user.password,
|
||||
fullName: user.fullName,
|
||||
shortName: user.shortName,
|
||||
partnerID: user.partnerId ? user.partnerId : '',
|
||||
referrerUserId: referrerUserId ? referrerUserId : '',
|
||||
password,
|
||||
secret,
|
||||
email: user.email,
|
||||
partnerId: user.partnerId ? user.partnerId : '',
|
||||
};
|
||||
|
||||
const response = await this.mutate(query, variables);
|
||||
const response = await this.http.post(path, JSON.stringify(body), false);
|
||||
if (!response.ok) {
|
||||
throw new Error('can not register user');
|
||||
}
|
||||
|
||||
return response.data.createUser.id;
|
||||
return await response.json();
|
||||
}
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ export class PaymentsHttpApi implements PaymentsApi {
|
||||
const path = `${this.ROOT_PATH}/cards`;
|
||||
const response = await this.client.post(path, token);
|
||||
|
||||
if (!response.ok) {
|
||||
if (response.ok) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -71,8 +71,8 @@ import VButton from '@/components/common/VButton.vue';
|
||||
import ChangePasswordIcon from '@/../static/images/account/changePasswordPopup/changePassword.svg';
|
||||
import CloseCrossIcon from '@/../static/images/common/closeCross.svg';
|
||||
|
||||
import { AuthApi } from '@/api/auth';
|
||||
import { APP_STATE_ACTIONS } from '@/utils/constants/actionNames';
|
||||
import { AuthHttpApi } from '@/api/auth';
|
||||
import { APP_STATE_ACTIONS, NOTIFICATION_ACTIONS } from '@/utils/constants/actionNames';
|
||||
import { validatePassword } from '@/utils/validation';
|
||||
|
||||
@Component({
|
||||
@ -91,7 +91,7 @@ export default class ChangePasswordPopup extends Vue {
|
||||
private newPasswordError: string = '';
|
||||
private confirmationPasswordError: string = '';
|
||||
|
||||
private readonly auth: AuthApi = new AuthApi();
|
||||
private readonly auth: AuthHttpApi = new AuthHttpApi();
|
||||
|
||||
public setOldPassword(value: string): void {
|
||||
this.oldPassword = value;
|
||||
|
@ -51,7 +51,7 @@ import VButton from '@/components/common/VButton.vue';
|
||||
import DeleteAccountIcon from '@/../static/images/account/deleteAccountPopup/deleteAccount.svg';
|
||||
import CloseCrossIcon from '@/../static/images/common/closeCross.svg';
|
||||
|
||||
import { AuthApi } from '@/api/auth';
|
||||
import { AuthHttpApi } from '@/api/auth';
|
||||
import { RouteConfig } from '@/router';
|
||||
import { AuthToken } from '@/utils/authToken';
|
||||
import { APP_STATE_ACTIONS } from '@/utils/constants/actionNames';
|
||||
@ -70,7 +70,7 @@ export default class DeleteAccountPopup extends Vue {
|
||||
private password: string = '';
|
||||
private isLoading: boolean = false;
|
||||
|
||||
private readonly auth: AuthApi = new AuthApi();
|
||||
private readonly auth: AuthHttpApi = new AuthHttpApi();
|
||||
|
||||
public setPassword(value: string): void {
|
||||
this.password = value;
|
||||
|
@ -11,7 +11,7 @@ import VButton from '@/components/common/VButton.vue';
|
||||
import CloseCrossIcon from '@/../static/images/common/closeCross.svg';
|
||||
import RegistrationSuccessIcon from '@/../static/images/register/registerSuccess.svg';
|
||||
|
||||
import { AuthApi } from '@/api/auth';
|
||||
import { AuthHttpApi } from '@/api/auth';
|
||||
import { RouteConfig } from '@/router';
|
||||
import { getUserId } from '@/utils/consoleLocalStorage';
|
||||
import { APP_STATE_ACTIONS } from '@/utils/constants/actionNames';
|
||||
@ -28,7 +28,7 @@ export default class RegistrationSuccessPopup extends Vue {
|
||||
private timeToEnableResendEmailButton: string = '00:30';
|
||||
private intervalID: any = null;
|
||||
|
||||
private readonly auth: AuthApi = new AuthApi();
|
||||
private readonly auth: AuthHttpApi = new AuthHttpApi();
|
||||
|
||||
public beforeDestroy(): void {
|
||||
if (this.intervalID) {
|
||||
|
@ -5,13 +5,13 @@ import Vue from 'vue';
|
||||
import Vuex from 'vuex';
|
||||
|
||||
import { ApiKeysApiGql } from '@/api/apiKeys';
|
||||
import { AuthHttpApi } from '@/api/auth';
|
||||
import { BucketsApiGql } from '@/api/buckets';
|
||||
import { CreditsApiGql } from '@/api/credits';
|
||||
import { PaymentsHttpApi } from '@/api/payments';
|
||||
import { ProjectMembersApiGql } from '@/api/projectMembers';
|
||||
import { ProjectsApiGql } from '@/api/projects';
|
||||
import { ProjectUsageApiGql } from '@/api/usage';
|
||||
import { UsersApiGql } from '@/api/users';
|
||||
import { makeApiKeysModule } from '@/store/modules/apiKeys';
|
||||
import { appStateModule } from '@/store/modules/appState';
|
||||
import { makeBucketsModule } from '@/store/modules/buckets';
|
||||
@ -33,7 +33,7 @@ export class StoreModule<S> {
|
||||
}
|
||||
|
||||
// TODO: remove it after we will use modules as classes and use some DI framework
|
||||
const usersApi = new UsersApiGql();
|
||||
const authApi = new AuthHttpApi();
|
||||
const apiKeysApi = new ApiKeysApiGql();
|
||||
const creditsApi = new CreditsApiGql();
|
||||
const bucketsApi = new BucketsApiGql();
|
||||
@ -51,7 +51,7 @@ export const store = new Vuex.Store({
|
||||
creditsModule: makeCreditsModule(creditsApi),
|
||||
projectMembersModule: makeProjectMembersModule(projectMembersApi),
|
||||
paymentsModule: makePaymentsModule(paymentsApi),
|
||||
usersModule: makeUsersModule(usersApi),
|
||||
usersModule: makeUsersModule(authApi),
|
||||
projectsModule: makeProjectsModule(projectsApi),
|
||||
usageModule: makeUsageModule(projectUsageApi),
|
||||
bucketUsageModule: makeBucketsModule(bucketsApi),
|
||||
|
@ -30,13 +30,15 @@ export class User {
|
||||
public shortName: string;
|
||||
public email: string;
|
||||
public partnerId: string;
|
||||
public password: string;
|
||||
|
||||
public constructor(id: string = '', fullName: string = '', shortName: string = '', email: string = '', partnerId: string = '') {
|
||||
public constructor(id: string = '', fullName: string = '', shortName: string = '', email: string = '', partnerId: string = '', password: string = '') {
|
||||
this.id = id;
|
||||
this.fullName = fullName;
|
||||
this.shortName = shortName;
|
||||
this.email = email;
|
||||
this.partnerId = partnerId;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public getFullName(): string {
|
||||
|
@ -54,6 +54,16 @@ export class HttpClient {
|
||||
return this.sendJSON('PATCH', path, body, auth);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs PUT http request with JSON body.
|
||||
* @param path
|
||||
* @param body serialized JSON
|
||||
* @param auth indicates if authentication is needed
|
||||
*/
|
||||
public async put(path: string, body: string | null, auth: boolean = true): Promise<Response> {
|
||||
return this.sendJSON('PUT', path, body, auth);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs GET http request.
|
||||
* @param path
|
||||
|
@ -11,7 +11,7 @@ import HeaderlessInput from '@/components/common/HeaderlessInput.vue';
|
||||
import AuthIcon from '@/../static/images/AuthImage.svg';
|
||||
import LogoIcon from '@/../static/images/Logo.svg';
|
||||
|
||||
import { AuthApi } from '@/api/auth';
|
||||
import { AuthHttpApi } from '@/api/auth';
|
||||
import { RouteConfig } from '@/router';
|
||||
import { LOADING_CLASSES } from '@/utils/constants/classConstants';
|
||||
import { validateEmail } from '@/utils/validation';
|
||||
@ -28,7 +28,7 @@ export default class ForgotPassword extends Vue {
|
||||
private email: string = '';
|
||||
private emailError: string = '';
|
||||
|
||||
private readonly auth: AuthApi = new AuthApi();
|
||||
private readonly auth: AuthHttpApi = new AuthHttpApi();
|
||||
|
||||
public setEmail(value: string): void {
|
||||
this.email = value;
|
||||
|
@ -12,7 +12,7 @@ import AuthIcon from '@/../static/images/AuthImage.svg';
|
||||
import LogoIcon from '@/../static/images/Logo.svg';
|
||||
import LoadingLogoIcon from '@/../static/images/LogoWhite.svg';
|
||||
|
||||
import { AuthApi } from '@/api/auth';
|
||||
import { AuthHttpApi } from '@/api/auth';
|
||||
import { RouteConfig } from '@/router';
|
||||
import { AuthToken } from '@/utils/authToken';
|
||||
import { APP_STATE_ACTIONS } from '@/utils/constants/actionNames';
|
||||
@ -40,7 +40,7 @@ export default class Login extends Vue {
|
||||
private emailError: string = '';
|
||||
private passwordError: string = '';
|
||||
|
||||
private readonly auth: AuthApi = new AuthApi();
|
||||
private readonly auth: AuthHttpApi = new AuthHttpApi();
|
||||
|
||||
public onLogoClick(): void {
|
||||
location.reload();
|
||||
@ -77,6 +77,7 @@ export default class Login extends Vue {
|
||||
|
||||
try {
|
||||
this.authToken = await this.auth.token(this.email, this.password);
|
||||
AuthToken.set(this.authToken);
|
||||
} catch (error) {
|
||||
await this.$notify.error(error.message);
|
||||
this.isLoading = false;
|
||||
|
@ -14,7 +14,7 @@ import AuthIcon from '@/../static/images/AuthImage.svg';
|
||||
import InfoIcon from '@/../static/images/info.svg';
|
||||
import LogoIcon from '@/../static/images/Logo.svg';
|
||||
|
||||
import { AuthApi } from '@/api/auth';
|
||||
import { AuthHttpApi } from '@/api/auth';
|
||||
import { RouteConfig } from '@/router';
|
||||
import { User } from '@/types/users';
|
||||
import { setUserId } from '@/utils/consoleLocalStorage';
|
||||
@ -53,7 +53,7 @@ export default class RegisterArea extends Vue {
|
||||
|
||||
private loadingClassName: string = LOADING_CLASSES.LOADING_OVERLAY;
|
||||
|
||||
private readonly auth: AuthApi = new AuthApi();
|
||||
private readonly auth: AuthHttpApi = new AuthHttpApi();
|
||||
|
||||
async mounted(): Promise<void> {
|
||||
if (this.$route.query.token) {
|
||||
@ -116,6 +116,7 @@ export default class RegisterArea extends Vue {
|
||||
this.user.shortName = value.trim();
|
||||
}
|
||||
public setPassword(value: string): void {
|
||||
this.user.password = value.trim();
|
||||
this.password = value;
|
||||
this.passwordError = '';
|
||||
}
|
||||
@ -157,7 +158,7 @@ export default class RegisterArea extends Vue {
|
||||
|
||||
private async createUser(): Promise<void> {
|
||||
try {
|
||||
this.userId = await this.auth.create(this.user, this.password , this.secret, this.refUserId);
|
||||
this.userId = await this.auth.register(this.user, this.secret, this.refUserId);
|
||||
|
||||
setUserId(this.userId);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user