Add an API to reset the password
This commit is contained in:
		@@ -4,24 +4,34 @@ import (
 | 
			
		||||
	"context"
 | 
			
		||||
 | 
			
		||||
	"git.badhouseplants.net/softplayer/softplayer-backend/internal/controllers"
 | 
			
		||||
	"git.badhouseplants.net/softplayer/softplayer-backend/internal/helpers/email"
 | 
			
		||||
	"git.badhouseplants.net/softplayer/softplayer-go-proto/pkg/accounts"
 | 
			
		||||
	"github.com/golang/protobuf/ptypes/empty"
 | 
			
		||||
	"google.golang.org/grpc"
 | 
			
		||||
	"google.golang.org/grpc/metadata"
 | 
			
		||||
	"google.golang.org/protobuf/types/known/emptypb"
 | 
			
		||||
 | 
			
		||||
	ctrl "sigs.k8s.io/controller-runtime"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func NewAccountRPCImpl(contoller ctrl.Manager, hashCost int16) *AccountsServer {
 | 
			
		||||
func NewAccountRPCImpl(contoller ctrl.Manager, hashCost int16, email *email.EmailConf, devMode bool) *AccountsServer {
 | 
			
		||||
	return &AccountsServer{
 | 
			
		||||
		Controller: contoller,
 | 
			
		||||
		Params: &controllers.AccountParams{
 | 
			
		||||
			HashCost: hashCost,
 | 
			
		||||
		},
 | 
			
		||||
		emailConfig: *email,
 | 
			
		||||
		devMode:     devMode,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type AccountsServer struct {
 | 
			
		||||
	accounts.UnimplementedAccountsServer
 | 
			
		||||
	Controller ctrl.Manager
 | 
			
		||||
	Params     *controllers.AccountParams
 | 
			
		||||
	Controller  ctrl.Manager
 | 
			
		||||
	Params      *controllers.AccountParams
 | 
			
		||||
	emailConfig email.EmailConf
 | 
			
		||||
	// When dev mode is enabled, email won't be sent, instead the code will be returned in metadata
 | 
			
		||||
	devMode bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (a *AccountsServer) SignUp(ctx context.Context, in *accounts.AccountWithPassword) (*accounts.AccountFullWithToken, error) {
 | 
			
		||||
@@ -64,6 +74,24 @@ func (a *AccountsServer) SignIn(ctx context.Context, in *accounts.AccountWithPas
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (a *AccountsServer) ResetPassword(ctx context.Context, in *accounts.AccountData) (*empty.Empty, error) {
 | 
			
		||||
	data := populateData(in.GetName(), "", in.GetEmail())
 | 
			
		||||
	acc := populateAccount(data, a.Controller)
 | 
			
		||||
	code, err := acc.ResetPassword(ctx, a.emailConfig)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if a.devMode {
 | 
			
		||||
		header := metadata.Pairs("code", code)
 | 
			
		||||
		if err := grpc.SendHeader(ctx, header); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &emptypb.Empty{}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func populateData(username, password, email string) *controllers.AccountData {
 | 
			
		||||
	return &controllers.AccountData{
 | 
			
		||||
		Username: username,
 | 
			
		||||
 
 | 
			
		||||
@@ -2,15 +2,22 @@ package controllers
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"git.badhouseplants.net/softplayer/softplayer-backend/internal/consts"
 | 
			
		||||
	"git.badhouseplants.net/softplayer/softplayer-backend/internal/helpers/email"
 | 
			
		||||
	"git.badhouseplants.net/softplayer/softplayer-backend/internal/helpers/hash"
 | 
			
		||||
	"git.badhouseplants.net/softplayer/softplayer-backend/internal/helpers/kube"
 | 
			
		||||
	"github.com/google/uuid"
 | 
			
		||||
	"github.com/sirupsen/logrus"
 | 
			
		||||
	corev1 "k8s.io/api/core/v1"
 | 
			
		||||
	rbacv1 "k8s.io/api/rbac/v1"
 | 
			
		||||
	k8serrors "k8s.io/apimachinery/pkg/api/errors"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	"k8s.io/client-go/kubernetes"
 | 
			
		||||
	"k8s.io/client-go/rest"
 | 
			
		||||
 | 
			
		||||
	"k8s.io/apimachinery/pkg/types"
 | 
			
		||||
	ctrl "sigs.k8s.io/controller-runtime"
 | 
			
		||||
@@ -21,6 +28,7 @@ type Account struct {
 | 
			
		||||
	Params     AccountParams
 | 
			
		||||
	Data       *AccountData
 | 
			
		||||
	Token      string
 | 
			
		||||
	DevMode    bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type AccountParams struct {
 | 
			
		||||
@@ -207,6 +215,68 @@ func (acc *Account) Login(ctx context.Context) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (acc *Account) ResetPassword(ctx context.Context, emailConfig email.EmailConf) (string, error) {
 | 
			
		||||
	clientset, err := kubernetes.NewForConfig(acc.Controller.GetConfig())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	userdata, err := clientset.CoreV1().Secrets("softplayer-accounts").Get(ctx, acc.Data.Username, metav1.GetOptions{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	if string(userdata.Data["email"]) != acc.Data.Email {
 | 
			
		||||
		return "", errors.New("user or email not found")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	acc.Data.UUID = string(userdata.Data["uuid"])
 | 
			
		||||
 | 
			
		||||
	conf := &rest.Config{
 | 
			
		||||
		Host:        "https://kubernetes.default.svc.cluster.local:443",
 | 
			
		||||
		BearerToken: acc.Token,
 | 
			
		||||
		TLSClientConfig: rest.TLSClientConfig{
 | 
			
		||||
			Insecure: true,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	clientset, err = kubernetes.NewForConfig(conf)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logrus.Error(err, "Couldn't create a new clientset")
 | 
			
		||||
		return "", consts.ErrSystemError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	secretName := "password-reset-code"
 | 
			
		||||
	number := encodeToString(6)
 | 
			
		||||
	secret := corev1.Secret{
 | 
			
		||||
		ObjectMeta: metav1.ObjectMeta{
 | 
			
		||||
			Name: secretName,
 | 
			
		||||
		},
 | 
			
		||||
		StringData: map[string]string{
 | 
			
		||||
			"code": number,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	sec, err := clientset.CoreV1().Secrets(acc.Data.UUID).Create(ctx, &secret, metav1.CreateOptions{})
 | 
			
		||||
	if !k8serrors.IsAlreadyExists(err) {
 | 
			
		||||
		return "", err
 | 
			
		||||
	} else if k8serrors.IsAlreadyExists(err) {
 | 
			
		||||
		timestamp := sec.CreationTimestamp.Time
 | 
			
		||||
		now := time.Now()
 | 
			
		||||
		if timestamp.Add(time.Minute).Before(now) {
 | 
			
		||||
			return "", errors.New("You can send an email once per minute, please wait")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !acc.DevMode {
 | 
			
		||||
		emailContent := "Subject: Softplayer verification code\r\n" + "\r\n" + fmt.Sprintf("Your verification code is %s", number)
 | 
			
		||||
		email := string(userdata.Data["email"])
 | 
			
		||||
		if err := emailConfig.SendEmail(email, emailContent); err != nil {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return number, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (acc *Account) getToken(ctx context.Context, saSec *corev1.Secret) (string, error) {
 | 
			
		||||
	client := acc.Controller.GetClient()
 | 
			
		||||
	if err := client.Get(ctx, types.NamespacedName{
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								main.go
									
									
									
									
									
								
							@@ -98,7 +98,7 @@ func server(params Serve) error {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	environments.RegisterEnvironmentsServer(grpcServer, v1.NewapiGrpcImpl(controller, log))
 | 
			
		||||
	accounts.RegisterAccountsServer(grpcServer, v1.NewAccountRPCImpl(controller, params.HashCost))
 | 
			
		||||
	accounts.RegisterAccountsServer(grpcServer, v1.NewAccountRPCImpl(controller, params.HashCost, &emailConfig, params.DevMode))
 | 
			
		||||
	email_proto.RegisterEmailValidationServer(grpcServer, v1.InitEmailServer(controller, &emailConfig, params.DevMode))
 | 
			
		||||
	applications_proto.RegisterApplicationsServer(grpcServer, v1.NewApplicationsGrpcImpl(controller, log))
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user