satellite/console: new passwordChange API endpoint (#3308)
This commit is contained in:
parent
f468816f13
commit
1814fbfa89
185
satellite/console/consoleweb/consoleapi/auth.go
Normal file
185
satellite/console/consoleweb/consoleapi/auth.go
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
// Copyright (C) 2019 Storj Labs, Inc.
|
||||||
|
// See LICENSE for copying information.
|
||||||
|
|
||||||
|
package consoleapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"go.uber.org/zap"
|
||||||
|
|
||||||
|
"storj.io/storj/internal/post"
|
||||||
|
"storj.io/storj/pkg/auth"
|
||||||
|
"storj.io/storj/satellite/console"
|
||||||
|
"storj.io/storj/satellite/console/consoleweb/consoleql"
|
||||||
|
"storj.io/storj/satellite/mailservice"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Auth is an api controller that exposes all auth functionality.
|
||||||
|
type Auth struct {
|
||||||
|
log *zap.Logger
|
||||||
|
service *console.Service
|
||||||
|
mailService *mailservice.Service
|
||||||
|
|
||||||
|
ExternalAddress string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAuth is a constructor for api auth controller.
|
||||||
|
func NewAuth(log *zap.Logger, service *console.Service, mailService *mailservice.Service, externalAddress string) *Auth {
|
||||||
|
return &Auth{
|
||||||
|
log: log,
|
||||||
|
service: service,
|
||||||
|
mailService: mailService,
|
||||||
|
ExternalAddress: externalAddress,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Token authenticates User by credentials and returns auth token.
|
||||||
|
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 {
|
||||||
|
a.serveJSONError(w, http.StatusBadRequest, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var tokenResponse struct {
|
||||||
|
Token string `json:"token"`
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenResponse.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)
|
||||||
|
if err != nil {
|
||||||
|
a.log.Error("token handler could not encode token response", zap.Error(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register creates new User, sends activation e-mail.
|
||||||
|
func (a *Auth) Register(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
var err error
|
||||||
|
defer mon.Task()(&ctx)(&err)
|
||||||
|
|
||||||
|
var request struct {
|
||||||
|
UserInfo console.CreateUser `json:"userInfo"`
|
||||||
|
SecretInput string `json:"secret"`
|
||||||
|
ReferrerUserID string `json:"referrerUserID"`
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.NewDecoder(r.Body).Decode(&request)
|
||||||
|
if err != nil {
|
||||||
|
a.serveJSONError(w, http.StatusBadRequest, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
secret, err := console.RegistrationSecretFromBase64(request.SecretInput)
|
||||||
|
if err != nil {
|
||||||
|
a.serveJSONError(w, http.StatusBadRequest, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := a.service.CreateUser(ctx, request.UserInfo, secret, request.ReferrerUserID)
|
||||||
|
if err != nil {
|
||||||
|
a.serveJSONError(w, http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
token, err := a.service.GenerateActivationToken(ctx, user.ID, user.Email)
|
||||||
|
if err != nil {
|
||||||
|
a.serveJSONError(w, http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
link := a.ExternalAddress + consoleql.ActivationPath + token
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
err = json.NewEncoder(w).Encode(&user.ID)
|
||||||
|
if err != nil {
|
||||||
|
a.log.Error("registration handler could not encode error", zap.Error(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PasswordChange auth user, changes users password for a new one.
|
||||||
|
func (a *Auth) PasswordChange(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
var err error
|
||||||
|
defer mon.Task()(&ctx)(&err)
|
||||||
|
|
||||||
|
var passwordChange struct {
|
||||||
|
CurrentPassword string `json:"password"`
|
||||||
|
NewPassword string `json:"newPassword"`
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = a.authorize(ctx, r)
|
||||||
|
|
||||||
|
err = json.NewDecoder(r.Body).Decode(&passwordChange)
|
||||||
|
if err != nil {
|
||||||
|
a.serveJSONError(w, http.StatusBadRequest, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = a.service.ChangePassword(ctx, passwordChange.CurrentPassword, passwordChange.NewPassword)
|
||||||
|
if err != nil {
|
||||||
|
a.serveJSONError(w, http.StatusNotFound, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// serveJSONError writes JSON error to response output stream.
|
||||||
|
func (a *Auth) serveJSONError(w http.ResponseWriter, status int, err error) {
|
||||||
|
w.WriteHeader(status)
|
||||||
|
|
||||||
|
var response struct {
|
||||||
|
Error string `json:"error"`
|
||||||
|
}
|
||||||
|
|
||||||
|
response.Error = err.Error()
|
||||||
|
|
||||||
|
err = json.NewEncoder(w).Encode(response)
|
||||||
|
if err != nil {
|
||||||
|
a.log.Error("failed to write json error response", zap.Error(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// authorize checks request for authorization token, validates it and updates context with auth data.
|
||||||
|
func (a *Auth) authorize(ctx context.Context, r *http.Request) context.Context {
|
||||||
|
authHeaderValue := r.Header.Get("Authorization")
|
||||||
|
token := strings.TrimPrefix(authHeaderValue, "Bearer ")
|
||||||
|
|
||||||
|
auth, err := a.service.Authorize(auth.WithAPIKey(ctx, []byte(token)))
|
||||||
|
if err != nil {
|
||||||
|
return console.WithAuthFailure(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return console.WithAuth(ctx, auth)
|
||||||
|
}
|
@ -24,7 +24,6 @@ import (
|
|||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
monkit "gopkg.in/spacemonkeygo/monkit.v2"
|
monkit "gopkg.in/spacemonkeygo/monkit.v2"
|
||||||
|
|
||||||
"storj.io/storj/internal/post"
|
|
||||||
"storj.io/storj/pkg/auth"
|
"storj.io/storj/pkg/auth"
|
||||||
"storj.io/storj/satellite/console"
|
"storj.io/storj/satellite/console"
|
||||||
"storj.io/storj/satellite/console/consoleweb/consoleapi"
|
"storj.io/storj/satellite/console/consoleweb/consoleapi"
|
||||||
@ -118,13 +117,17 @@ func NewServer(logger *zap.Logger, config Config, service *console.Service, mail
|
|||||||
fs := http.FileServer(http.Dir(server.config.StaticDir))
|
fs := http.FileServer(http.Dir(server.config.StaticDir))
|
||||||
|
|
||||||
paymentController := consoleapi.NewPayments(logger, service)
|
paymentController := consoleapi.NewPayments(logger, service)
|
||||||
|
authController := consoleapi.NewAuth(logger, service, mailService, config.ExternalAddress)
|
||||||
|
|
||||||
router.Handle("/api/v0/payments/cards", http.HandlerFunc(paymentController.AddCreditCard))
|
router.Handle("/api/v0/payments/cards", http.HandlerFunc(paymentController.AddCreditCard))
|
||||||
router.Handle("/api/v0/payments/account/balance", http.HandlerFunc(paymentController.AccountBalance))
|
router.Handle("/api/v0/payments/account/balance", http.HandlerFunc(paymentController.AccountBalance))
|
||||||
router.Handle("/api/v0/payments/account", http.HandlerFunc(paymentController.SetupAccount))
|
router.Handle("/api/v0/payments/account", http.HandlerFunc(paymentController.SetupAccount))
|
||||||
|
|
||||||
|
router.Handle("/api/v0/register", http.HandlerFunc(authController.Register))
|
||||||
|
router.Handle("/api/v0/token", http.HandlerFunc(authController.Token))
|
||||||
|
router.Handle("/api/v0/passwordChange", http.HandlerFunc(authController.PasswordChange))
|
||||||
|
|
||||||
router.Handle("/api/v0/graphql", http.HandlerFunc(server.grapqlHandler))
|
router.Handle("/api/v0/graphql", http.HandlerFunc(server.grapqlHandler))
|
||||||
router.Handle("/api/v0/token", http.HandlerFunc(server.tokenHandler))
|
|
||||||
router.Handle("/api/v0/register", http.HandlerFunc(server.registerHandler))
|
|
||||||
router.Handle("/registrationToken/", http.HandlerFunc(server.createRegistrationTokenHandler))
|
router.Handle("/registrationToken/", http.HandlerFunc(server.createRegistrationTokenHandler))
|
||||||
router.Handle("/robots.txt", http.HandlerFunc(server.seoHandler))
|
router.Handle("/robots.txt", http.HandlerFunc(server.seoHandler))
|
||||||
|
|
||||||
@ -201,94 +204,6 @@ func (server *Server) appHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// tokenRequestHandler authenticates User by credentials and returns auth token.
|
|
||||||
func (server *Server) tokenHandler(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 {
|
|
||||||
server.serveJSONError(w, http.StatusBadRequest, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var tokenResponse struct {
|
|
||||||
Token string `json:"token"`
|
|
||||||
}
|
|
||||||
|
|
||||||
tokenResponse.Token, err = server.service.Token(ctx, tokenRequest.Email, tokenRequest.Password)
|
|
||||||
if err != nil {
|
|
||||||
server.serveJSONError(w, http.StatusUnauthorized, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = json.NewEncoder(w).Encode(tokenResponse)
|
|
||||||
if err != nil {
|
|
||||||
server.log.Error("token handler could not encode token response", zap.Error(err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// registerHandler registers new User.
|
|
||||||
func (server *Server) registerHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
ctx := r.Context()
|
|
||||||
var err error
|
|
||||||
defer mon.Task()(&ctx)(&err)
|
|
||||||
|
|
||||||
var request struct {
|
|
||||||
UserInfo console.CreateUser `json:"userInfo"`
|
|
||||||
SecretInput string `json:"secret"`
|
|
||||||
ReferrerUserID string `json:"referrerUserID"`
|
|
||||||
}
|
|
||||||
|
|
||||||
err = json.NewDecoder(r.Body).Decode(&request)
|
|
||||||
if err != nil {
|
|
||||||
server.serveJSONError(w, http.StatusBadRequest, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
secret, err := console.RegistrationSecretFromBase64(request.SecretInput)
|
|
||||||
if err != nil {
|
|
||||||
server.serveJSONError(w, http.StatusBadRequest, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
user, err := server.service.CreateUser(ctx, request.UserInfo, secret, request.ReferrerUserID)
|
|
||||||
if err != nil {
|
|
||||||
server.serveJSONError(w, http.StatusInternalServerError, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
token, err := server.service.GenerateActivationToken(ctx, user.ID, user.Email)
|
|
||||||
if err != nil {
|
|
||||||
server.serveJSONError(w, http.StatusInternalServerError, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
link := server.config.ExternalAddress + consoleql.ActivationPath + token
|
|
||||||
|
|
||||||
server.mailService.SendRenderedAsync(
|
|
||||||
ctx,
|
|
||||||
[]post.Address{{Address: user.Email, Name: user.FullName}},
|
|
||||||
&consoleql.AccountActivationEmail{
|
|
||||||
ActivationLink: link,
|
|
||||||
Origin: server.config.ExternalAddress,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
err = json.NewEncoder(w).Encode(&user.ID)
|
|
||||||
if err != nil {
|
|
||||||
server.log.Error("registration handler could not encode error", zap.Error(err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// bucketUsageReportHandler generate bucket usage report page for project
|
// bucketUsageReportHandler generate bucket usage report page for project
|
||||||
func (server *Server) bucketUsageReportHandler(w http.ResponseWriter, r *http.Request) {
|
func (server *Server) bucketUsageReportHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
|
Loading…
Reference in New Issue
Block a user