2019-10-21 13:48:29 +01:00
|
|
|
// Copyright (C) 2019 Storj Labs, Inc.
|
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
|
|
|
package consoleapi
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
2020-10-21 11:52:24 +01:00
|
|
|
"errors"
|
2019-10-21 13:48:29 +01:00
|
|
|
"net/http"
|
|
|
|
|
2019-10-21 17:42:49 +01:00
|
|
|
"github.com/gorilla/mux"
|
|
|
|
"github.com/zeebo/errs"
|
2019-10-21 13:48:29 +01:00
|
|
|
"go.uber.org/zap"
|
|
|
|
|
2020-03-30 10:08:50 +01:00
|
|
|
"storj.io/common/uuid"
|
2019-11-14 19:46:15 +00:00
|
|
|
"storj.io/storj/private/post"
|
2021-03-23 15:52:34 +00:00
|
|
|
"storj.io/storj/satellite/analytics"
|
2019-10-21 13:48:29 +01:00
|
|
|
"storj.io/storj/satellite/console"
|
|
|
|
"storj.io/storj/satellite/console/consoleweb/consoleql"
|
2020-01-20 18:57:14 +00:00
|
|
|
"storj.io/storj/satellite/console/consoleweb/consolewebauth"
|
2019-10-21 13:48:29 +01:00
|
|
|
"storj.io/storj/satellite/mailservice"
|
2020-07-28 15:23:17 +01:00
|
|
|
"storj.io/storj/satellite/rewards"
|
2019-10-21 13:48:29 +01:00
|
|
|
)
|
|
|
|
|
2020-10-21 11:52:24 +01:00
|
|
|
var (
|
|
|
|
// ErrAuthAPI - console auth api error type.
|
|
|
|
ErrAuthAPI = errs.Class("console auth api error")
|
|
|
|
|
|
|
|
// errNotImplemented is the error value used by handlers of this package to
|
|
|
|
// response with status Not Implemented.
|
|
|
|
errNotImplemented = errs.New("not implemented")
|
|
|
|
)
|
2019-10-21 17:42:49 +01:00
|
|
|
|
2019-10-21 13:48:29 +01:00
|
|
|
// Auth is an api controller that exposes all auth functionality.
|
|
|
|
type Auth struct {
|
2019-10-21 17:42:49 +01:00
|
|
|
log *zap.Logger
|
|
|
|
ExternalAddress string
|
|
|
|
LetUsKnowURL string
|
|
|
|
TermsAndConditionsURL string
|
|
|
|
ContactInfoURL string
|
2020-01-20 18:57:14 +00:00
|
|
|
service *console.Service
|
2021-03-23 15:52:34 +00:00
|
|
|
analytics *analytics.Service
|
2020-01-20 18:57:14 +00:00
|
|
|
mailService *mailservice.Service
|
|
|
|
cookieAuth *consolewebauth.CookieAuth
|
2020-07-28 15:23:17 +01:00
|
|
|
partners *rewards.PartnersService
|
2019-10-21 13:48:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewAuth is a constructor for api auth controller.
|
2021-03-23 15:52:34 +00:00
|
|
|
func NewAuth(log *zap.Logger, service *console.Service, mailService *mailservice.Service, cookieAuth *consolewebauth.CookieAuth, partners *rewards.PartnersService, analytics *analytics.Service, externalAddress string, letUsKnowURL string, termsAndConditionsURL string, contactInfoURL string) *Auth {
|
2019-10-21 13:48:29 +01:00
|
|
|
return &Auth{
|
2019-10-21 17:42:49 +01:00
|
|
|
log: log,
|
|
|
|
ExternalAddress: externalAddress,
|
|
|
|
LetUsKnowURL: letUsKnowURL,
|
|
|
|
TermsAndConditionsURL: termsAndConditionsURL,
|
|
|
|
ContactInfoURL: contactInfoURL,
|
2020-01-20 18:57:14 +00:00
|
|
|
service: service,
|
|
|
|
mailService: mailService,
|
|
|
|
cookieAuth: cookieAuth,
|
2020-07-28 15:23:17 +01:00
|
|
|
partners: partners,
|
2021-03-23 15:52:34 +00:00
|
|
|
analytics: analytics,
|
2019-10-21 13:48:29 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-21 17:42:49 +01:00
|
|
|
// Token authenticates user by credentials and returns auth token.
|
2019-10-21 13:48:29 +01:00
|
|
|
func (a *Auth) Token(w http.ResponseWriter, r *http.Request) {
|
|
|
|
ctx := r.Context()
|
|
|
|
var err error
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
|
|
|
|
var tokenRequest struct {
|
|
|
|
Email string `json:"email"`
|
|
|
|
Password string `json:"password"`
|
|
|
|
}
|
|
|
|
|
|
|
|
err = json.NewDecoder(r.Body).Decode(&tokenRequest)
|
|
|
|
if err != nil {
|
2019-11-12 11:53:00 +00:00
|
|
|
a.serveJSONError(w, err)
|
2019-10-21 13:48:29 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-10-29 14:24:16 +00:00
|
|
|
token, err := a.service.Token(ctx, tokenRequest.Email, tokenRequest.Password)
|
2019-10-21 13:48:29 +01:00
|
|
|
if err != nil {
|
2020-04-08 20:40:49 +01:00
|
|
|
a.log.Info("Error authenticating token request", zap.String("email", tokenRequest.Email), zap.Error(ErrAuthAPI.Wrap(err)))
|
2019-11-12 11:53:00 +00:00
|
|
|
a.serveJSONError(w, err)
|
2019-10-21 13:48:29 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-01-20 18:57:14 +00:00
|
|
|
a.cookieAuth.SetTokenCookie(w, token)
|
|
|
|
|
2019-11-12 13:05:35 +00:00
|
|
|
w.Header().Set("Content-Type", "application/json")
|
2019-10-29 14:24:16 +00:00
|
|
|
err = json.NewEncoder(w).Encode(token)
|
2019-10-21 13:48:29 +01:00
|
|
|
if err != nil {
|
2019-10-23 18:33:24 +01:00
|
|
|
a.log.Error("token handler could not encode token response", zap.Error(ErrAuthAPI.Wrap(err)))
|
2019-10-21 13:48:29 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-29 13:10:13 +00:00
|
|
|
// Logout removes auth cookie.
|
|
|
|
func (a *Auth) Logout(w http.ResponseWriter, r *http.Request) {
|
|
|
|
ctx := r.Context()
|
|
|
|
defer mon.Task()(&ctx)(nil)
|
|
|
|
|
|
|
|
a.cookieAuth.RemoveTokenCookie(w)
|
|
|
|
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
}
|
|
|
|
|
2019-10-21 17:42:49 +01:00
|
|
|
// Register creates new user, sends activation e-mail.
|
2019-10-21 13:48:29 +01:00
|
|
|
func (a *Auth) Register(w http.ResponseWriter, r *http.Request) {
|
|
|
|
ctx := r.Context()
|
|
|
|
var err error
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
|
2019-10-29 14:24:16 +00:00
|
|
|
var registerData struct {
|
|
|
|
FullName string `json:"fullName"`
|
|
|
|
ShortName string `json:"shortName"`
|
|
|
|
Email string `json:"email"`
|
2020-07-28 15:23:17 +01:00
|
|
|
Partner string `json:"partner"`
|
2019-10-29 14:24:16 +00:00
|
|
|
PartnerID string `json:"partnerId"`
|
|
|
|
Password string `json:"password"`
|
|
|
|
SecretInput string `json:"secret"`
|
2019-11-25 21:36:36 +00:00
|
|
|
ReferrerUserID string `json:"referrerUserId"`
|
2021-02-10 15:55:38 +00:00
|
|
|
IsProfessional bool `json:"isProfessional"`
|
|
|
|
Position string `json:"position"`
|
|
|
|
CompanyName string `json:"companyName"`
|
|
|
|
EmployeeCount string `json:"employeeCount"`
|
2019-10-21 13:48:29 +01:00
|
|
|
}
|
|
|
|
|
2019-10-29 14:24:16 +00:00
|
|
|
err = json.NewDecoder(r.Body).Decode(®isterData)
|
2019-10-21 13:48:29 +01:00
|
|
|
if err != nil {
|
2019-11-12 11:53:00 +00:00
|
|
|
a.serveJSONError(w, err)
|
2019-10-21 13:48:29 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-10-29 14:24:16 +00:00
|
|
|
secret, err := console.RegistrationSecretFromBase64(registerData.SecretInput)
|
2019-10-21 13:48:29 +01:00
|
|
|
if err != nil {
|
2019-11-12 11:53:00 +00:00
|
|
|
a.serveJSONError(w, err)
|
2019-10-21 13:48:29 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-07-28 15:23:17 +01:00
|
|
|
if registerData.Partner != "" {
|
|
|
|
info, err := a.partners.ByName(ctx, registerData.Partner)
|
|
|
|
if err != nil {
|
|
|
|
a.log.Warn("Invalid partner name", zap.String("Partner name", registerData.Partner), zap.String("User email", registerData.Email), zap.Error(err))
|
|
|
|
} else {
|
|
|
|
registerData.PartnerID = info.ID
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-29 14:24:16 +00:00
|
|
|
user, err := a.service.CreateUser(ctx,
|
|
|
|
console.CreateUser{
|
2021-02-10 15:55:38 +00:00
|
|
|
FullName: registerData.FullName,
|
|
|
|
ShortName: registerData.ShortName,
|
|
|
|
Email: registerData.Email,
|
|
|
|
PartnerID: registerData.PartnerID,
|
|
|
|
Password: registerData.Password,
|
|
|
|
IsProfessional: registerData.IsProfessional,
|
|
|
|
Position: registerData.Position,
|
|
|
|
CompanyName: registerData.CompanyName,
|
|
|
|
EmployeeCount: registerData.EmployeeCount,
|
2019-10-29 14:24:16 +00:00
|
|
|
},
|
|
|
|
secret,
|
|
|
|
)
|
2019-10-21 13:48:29 +01:00
|
|
|
if err != nil {
|
2019-11-12 11:53:00 +00:00
|
|
|
a.serveJSONError(w, err)
|
2019-11-22 19:44:57 +00:00
|
|
|
return
|
2019-10-21 13:48:29 +01:00
|
|
|
}
|
|
|
|
|
2021-03-23 15:52:34 +00:00
|
|
|
trackCreateUserFields := analytics.TrackCreateUserFields{
|
|
|
|
ID: user.ID,
|
|
|
|
FullName: user.FullName,
|
|
|
|
Email: user.Email,
|
|
|
|
Type: analytics.Personal,
|
|
|
|
}
|
|
|
|
if user.IsProfessional {
|
|
|
|
trackCreateUserFields.Type = analytics.Professional
|
|
|
|
trackCreateUserFields.EmployeeCount = user.EmployeeCount
|
|
|
|
trackCreateUserFields.CompanyName = user.CompanyName
|
|
|
|
trackCreateUserFields.JobTitle = user.Position
|
|
|
|
}
|
|
|
|
a.analytics.TrackCreateUser(trackCreateUserFields)
|
|
|
|
|
2019-10-21 13:48:29 +01:00
|
|
|
token, err := a.service.GenerateActivationToken(ctx, user.ID, user.Email)
|
|
|
|
if err != nil {
|
2019-11-12 11:53:00 +00:00
|
|
|
a.serveJSONError(w, err)
|
2019-10-21 13:48:29 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-10-29 14:24:16 +00:00
|
|
|
link := a.ExternalAddress + "activation/?token=" + token
|
2019-10-21 13:48:29 +01:00
|
|
|
userName := user.ShortName
|
|
|
|
if user.ShortName == "" {
|
|
|
|
userName = user.FullName
|
|
|
|
}
|
|
|
|
|
|
|
|
a.mailService.SendRenderedAsync(
|
|
|
|
ctx,
|
|
|
|
[]post.Address{{Address: user.Email, Name: userName}},
|
|
|
|
&consoleql.AccountActivationEmail{
|
|
|
|
ActivationLink: link,
|
|
|
|
Origin: a.ExternalAddress,
|
2020-01-24 12:56:33 +00:00
|
|
|
UserName: userName,
|
2019-10-21 13:48:29 +01:00
|
|
|
},
|
|
|
|
)
|
|
|
|
|
2019-11-12 13:05:35 +00:00
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
err = json.NewEncoder(w).Encode(user.ID)
|
2019-10-21 13:48:29 +01:00
|
|
|
if err != nil {
|
2019-11-12 13:05:35 +00:00
|
|
|
a.log.Error("registration handler could not encode userID", zap.Error(ErrAuthAPI.Wrap(err)))
|
2019-10-21 13:48:29 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-29 14:24:16 +00:00
|
|
|
// UpdateAccount updates user's full name and short name.
|
|
|
|
func (a *Auth) UpdateAccount(w http.ResponseWriter, r *http.Request) {
|
2019-10-25 13:07:17 +01:00
|
|
|
ctx := r.Context()
|
|
|
|
var err error
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
|
|
|
|
var updatedInfo struct {
|
|
|
|
FullName string `json:"fullName"`
|
|
|
|
ShortName string `json:"shortName"`
|
|
|
|
}
|
|
|
|
|
|
|
|
err = json.NewDecoder(r.Body).Decode(&updatedInfo)
|
|
|
|
if err != nil {
|
2019-11-12 11:53:00 +00:00
|
|
|
a.serveJSONError(w, err)
|
2019-10-25 13:07:17 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = a.service.UpdateAccount(ctx, updatedInfo.FullName, updatedInfo.ShortName); err != nil {
|
2019-11-12 11:53:00 +00:00
|
|
|
a.serveJSONError(w, err)
|
2019-10-25 13:07:17 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-29 14:24:16 +00:00
|
|
|
// GetAccount gets authorized user and take it's params.
|
|
|
|
func (a *Auth) GetAccount(w http.ResponseWriter, r *http.Request) {
|
2019-10-25 13:07:17 +01:00
|
|
|
ctx := r.Context()
|
|
|
|
var err error
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
|
|
|
|
var user struct {
|
2021-02-10 15:55:38 +00:00
|
|
|
ID uuid.UUID `json:"id"`
|
|
|
|
FullName string `json:"fullName"`
|
|
|
|
ShortName string `json:"shortName"`
|
|
|
|
Email string `json:"email"`
|
|
|
|
PartnerID uuid.UUID `json:"partnerId"`
|
|
|
|
ProjectLimit int `json:"projectLimit"`
|
|
|
|
IsProfessional bool `json:"isProfessional"`
|
|
|
|
Position string `json:"position"`
|
|
|
|
CompanyName string `json:"companyName"`
|
|
|
|
EmployeeCount string `json:"employeeCount"`
|
2019-10-25 13:07:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
auth, err := console.GetAuth(ctx)
|
|
|
|
if err != nil {
|
2019-11-12 11:53:00 +00:00
|
|
|
a.serveJSONError(w, err)
|
2019-10-25 13:07:17 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
user.ShortName = auth.User.ShortName
|
|
|
|
user.FullName = auth.User.FullName
|
|
|
|
user.Email = auth.User.Email
|
|
|
|
user.ID = auth.User.ID
|
|
|
|
user.PartnerID = auth.User.PartnerID
|
2020-10-05 18:33:16 +01:00
|
|
|
user.ProjectLimit = auth.User.ProjectLimit
|
2021-02-10 15:55:38 +00:00
|
|
|
user.IsProfessional = auth.User.IsProfessional
|
|
|
|
user.CompanyName = auth.User.CompanyName
|
|
|
|
user.Position = auth.User.Position
|
|
|
|
user.EmployeeCount = auth.User.EmployeeCount
|
2019-10-25 13:07:17 +01:00
|
|
|
|
2019-11-12 13:05:35 +00:00
|
|
|
w.Header().Set("Content-Type", "application/json")
|
2019-10-25 13:07:17 +01:00
|
|
|
err = json.NewEncoder(w).Encode(&user)
|
|
|
|
if err != nil {
|
|
|
|
a.log.Error("could not encode user info", zap.Error(ErrAuthAPI.Wrap(err)))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-06 11:25:12 +01:00
|
|
|
// DeleteAccount authorizes user and deletes account by password.
|
2019-10-29 14:24:16 +00:00
|
|
|
func (a *Auth) DeleteAccount(w http.ResponseWriter, r *http.Request) {
|
2019-10-22 17:17:09 +01:00
|
|
|
ctx := r.Context()
|
2020-10-21 11:52:24 +01:00
|
|
|
defer mon.Task()(&ctx)(&errNotImplemented)
|
2019-10-22 17:17:09 +01:00
|
|
|
|
2020-10-06 11:25:12 +01:00
|
|
|
// We do not want to allow account deletion via API currently.
|
2020-10-21 11:52:24 +01:00
|
|
|
a.serveJSONError(w, errNotImplemented)
|
2019-10-22 17:17:09 +01:00
|
|
|
}
|
|
|
|
|
2020-11-05 16:16:55 +00:00
|
|
|
// ChangeEmail auth user, changes users email for a new one.
|
|
|
|
func (a *Auth) ChangeEmail(w http.ResponseWriter, r *http.Request) {
|
|
|
|
ctx := r.Context()
|
|
|
|
var err error
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
|
|
|
|
var emailChange struct {
|
|
|
|
NewEmail string `json:"newEmail"`
|
|
|
|
}
|
|
|
|
|
|
|
|
err = json.NewDecoder(r.Body).Decode(&emailChange)
|
|
|
|
if err != nil {
|
|
|
|
a.serveJSONError(w, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
err = a.service.ChangeEmail(ctx, emailChange.NewEmail)
|
|
|
|
if err != nil {
|
|
|
|
a.serveJSONError(w, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-21 17:42:49 +01:00
|
|
|
// ChangePassword auth user, changes users password for a new one.
|
|
|
|
func (a *Auth) ChangePassword(w http.ResponseWriter, r *http.Request) {
|
2019-10-21 13:48:29 +01:00
|
|
|
ctx := r.Context()
|
|
|
|
var err error
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
|
|
|
|
var passwordChange struct {
|
|
|
|
CurrentPassword string `json:"password"`
|
|
|
|
NewPassword string `json:"newPassword"`
|
|
|
|
}
|
|
|
|
|
|
|
|
err = json.NewDecoder(r.Body).Decode(&passwordChange)
|
|
|
|
if err != nil {
|
2019-11-12 11:53:00 +00:00
|
|
|
a.serveJSONError(w, err)
|
2019-10-21 13:48:29 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
err = a.service.ChangePassword(ctx, passwordChange.CurrentPassword, passwordChange.NewPassword)
|
|
|
|
if err != nil {
|
2019-11-12 11:53:00 +00:00
|
|
|
a.serveJSONError(w, err)
|
2019-10-21 13:48:29 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-21 17:42:49 +01:00
|
|
|
// ForgotPassword creates password-reset token and sends email to user.
|
|
|
|
func (a *Auth) ForgotPassword(w http.ResponseWriter, r *http.Request) {
|
|
|
|
ctx := r.Context()
|
|
|
|
var err error
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
|
|
|
|
params := mux.Vars(r)
|
|
|
|
email, ok := params["email"]
|
|
|
|
if !ok {
|
|
|
|
err = errs.New("email expected")
|
2019-11-12 11:53:00 +00:00
|
|
|
a.serveJSONError(w, err)
|
2019-10-21 17:42:49 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
user, err := a.service.GetUserByEmail(ctx, email)
|
|
|
|
if err != nil {
|
2019-11-12 11:53:00 +00:00
|
|
|
a.serveJSONError(w, err)
|
2019-10-21 17:42:49 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
recoveryToken, err := a.service.GeneratePasswordRecoveryToken(ctx, user.ID)
|
|
|
|
if err != nil {
|
2019-11-12 11:53:00 +00:00
|
|
|
a.serveJSONError(w, err)
|
2019-10-21 17:42:49 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-10-29 14:24:16 +00:00
|
|
|
passwordRecoveryLink := a.ExternalAddress + "password-recovery/?token=" + recoveryToken
|
|
|
|
cancelPasswordRecoveryLink := a.ExternalAddress + "cancel-password-recovery/?token=" + recoveryToken
|
2019-10-21 17:42:49 +01:00
|
|
|
userName := user.ShortName
|
|
|
|
if user.ShortName == "" {
|
|
|
|
userName = user.FullName
|
|
|
|
}
|
|
|
|
|
|
|
|
contactInfoURL := a.ContactInfoURL
|
|
|
|
letUsKnowURL := a.LetUsKnowURL
|
|
|
|
termsAndConditionsURL := a.TermsAndConditionsURL
|
|
|
|
|
|
|
|
a.mailService.SendRenderedAsync(
|
|
|
|
ctx,
|
|
|
|
[]post.Address{{Address: user.Email, Name: userName}},
|
|
|
|
&consoleql.ForgotPasswordEmail{
|
|
|
|
Origin: a.ExternalAddress,
|
|
|
|
UserName: userName,
|
|
|
|
ResetLink: passwordRecoveryLink,
|
|
|
|
CancelPasswordRecoveryLink: cancelPasswordRecoveryLink,
|
|
|
|
LetUsKnowURL: letUsKnowURL,
|
|
|
|
ContactInfoURL: contactInfoURL,
|
|
|
|
TermsAndConditionsURL: termsAndConditionsURL,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ResendEmail generates activation token by userID and sends email account activation email to user.
|
|
|
|
func (a *Auth) ResendEmail(w http.ResponseWriter, r *http.Request) {
|
|
|
|
ctx := r.Context()
|
|
|
|
var err error
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
|
|
|
|
params := mux.Vars(r)
|
2019-10-29 14:24:16 +00:00
|
|
|
id, ok := params["id"]
|
2019-10-21 17:42:49 +01:00
|
|
|
if !ok {
|
2019-11-12 11:53:00 +00:00
|
|
|
a.serveJSONError(w, err)
|
2019-10-21 17:42:49 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-04-02 13:30:43 +01:00
|
|
|
userID, err := uuid.FromString(id)
|
2019-10-21 17:42:49 +01:00
|
|
|
if err != nil {
|
2019-11-12 11:53:00 +00:00
|
|
|
a.serveJSONError(w, err)
|
2019-10-21 17:42:49 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-04-02 13:30:43 +01:00
|
|
|
user, err := a.service.GetUser(ctx, userID)
|
2019-10-21 17:42:49 +01:00
|
|
|
if err != nil {
|
2019-11-12 11:53:00 +00:00
|
|
|
a.serveJSONError(w, err)
|
2019-10-21 17:42:49 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
token, err := a.service.GenerateActivationToken(ctx, user.ID, user.Email)
|
|
|
|
if err != nil {
|
2019-11-12 11:53:00 +00:00
|
|
|
a.serveJSONError(w, err)
|
2019-10-21 17:42:49 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-10-29 14:24:16 +00:00
|
|
|
link := a.ExternalAddress + "activation/?token=" + token
|
2019-10-21 17:42:49 +01:00
|
|
|
userName := user.ShortName
|
|
|
|
if user.ShortName == "" {
|
|
|
|
userName = user.FullName
|
|
|
|
}
|
|
|
|
|
|
|
|
contactInfoURL := a.ContactInfoURL
|
|
|
|
termsAndConditionsURL := a.TermsAndConditionsURL
|
|
|
|
|
|
|
|
a.mailService.SendRenderedAsync(
|
|
|
|
ctx,
|
|
|
|
[]post.Address{{Address: user.Email, Name: userName}},
|
|
|
|
&consoleql.AccountActivationEmail{
|
|
|
|
Origin: a.ExternalAddress,
|
|
|
|
ActivationLink: link,
|
|
|
|
TermsAndConditionsURL: termsAndConditionsURL,
|
|
|
|
ContactInfoURL: contactInfoURL,
|
2020-01-24 12:56:33 +00:00
|
|
|
UserName: userName,
|
2019-10-21 17:42:49 +01:00
|
|
|
},
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2019-10-21 13:48:29 +01:00
|
|
|
// serveJSONError writes JSON error to response output stream.
|
2019-11-12 11:53:00 +00:00
|
|
|
func (a *Auth) serveJSONError(w http.ResponseWriter, err error) {
|
|
|
|
w.WriteHeader(a.getStatusCode(err))
|
2019-10-21 13:48:29 +01:00
|
|
|
|
|
|
|
var response struct {
|
|
|
|
Error string `json:"error"`
|
|
|
|
}
|
|
|
|
|
|
|
|
response.Error = err.Error()
|
|
|
|
|
|
|
|
err = json.NewEncoder(w).Encode(response)
|
|
|
|
if err != nil {
|
2019-10-23 18:33:24 +01:00
|
|
|
a.log.Error("failed to write json error response", zap.Error(ErrAuthAPI.Wrap(err)))
|
2019-10-21 13:48:29 +01:00
|
|
|
}
|
|
|
|
}
|
2019-11-12 11:53:00 +00:00
|
|
|
|
|
|
|
// getStatusCode returns http.StatusCode depends on console error class.
|
|
|
|
func (a *Auth) getStatusCode(err error) int {
|
|
|
|
switch {
|
|
|
|
case console.ErrValidation.Has(err):
|
|
|
|
return http.StatusBadRequest
|
|
|
|
case console.ErrUnauthorized.Has(err):
|
|
|
|
return http.StatusUnauthorized
|
2020-03-26 15:35:14 +00:00
|
|
|
case console.ErrEmailUsed.Has(err):
|
|
|
|
return http.StatusConflict
|
2020-10-21 11:52:24 +01:00
|
|
|
case errors.Is(err, errNotImplemented):
|
|
|
|
return http.StatusNotImplemented
|
2019-11-12 11:53:00 +00:00
|
|
|
default:
|
|
|
|
return http.StatusInternalServerError
|
|
|
|
}
|
|
|
|
}
|