WIP: Something is going on

This commit is contained in:
Nikolai Rodionov 2024-03-21 18:39:32 +01:00
parent 58c1b91916
commit 782e762019
Signed by: allanger
GPG Key ID: 0AA46A90E25592AD
7 changed files with 98 additions and 102 deletions

View File

@ -4,36 +4,30 @@ import (
"context" "context"
"git.badhouseplants.net/softplayer/softplayer-backend/internal/controllers" "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" "git.badhouseplants.net/softplayer/softplayer-go-proto/pkg/accounts"
// "google.golang.org/protobuf/types/known/emptypb"
ctrl "sigs.k8s.io/controller-runtime" ctrl "sigs.k8s.io/controller-runtime"
) )
func NewAccountRPCImpl(contoller ctrl.Manager, emailConfig email.EmailConf) *AccountsServer { func NewAccountRPCImpl(contoller ctrl.Manager, hashCost int16) *AccountsServer {
return &AccountsServer{ return &AccountsServer{
Controller: contoller, Controller: contoller,
EmailConfig: emailConfig, Params: &controllers.AccountParams{
HashCost: hashCost,
},
} }
} }
type AccountsServer struct { type AccountsServer struct {
accounts.UnimplementedAccountsServer accounts.UnimplementedAccountsServer
EmailConfig email.EmailConf
Controller ctrl.Manager Controller ctrl.Manager
Params *controllers.AccountParams
} }
func (a *AccountsServer) SignUp(ctx context.Context, in *accounts.AccountWithPassword) (*accounts.AccountFullWithToken, error) { func (a *AccountsServer) SignUp(ctx context.Context, in *accounts.AccountWithPassword) (*accounts.AccountFullWithToken, error) {
data := controllers.AccountData{ data := populateData(in.Data.GetName(), in.Password.GetPassword(), in.Data.GetEmail())
Username: in.Data.GetName(), acc := populateAccount(data, a.Controller)
Password: in.Password.GetPassword(),
Email: in.Data.GetEmail(),
}
acc := controllers.Account{
Controller: a.Controller,
Data: &data,
}
if err := acc.Create(ctx); err != nil { if err := acc.Create(ctx); err != nil {
return nil, err return nil, err
} }
@ -51,23 +45,18 @@ func (a *AccountsServer) SignUp(ctx context.Context, in *accounts.AccountWithPas
} }
func (a *AccountsServer) SignIn(ctx context.Context, in *accounts.AccountWithPassword) (*accounts.AccountFullWithToken, error) { func (a *AccountsServer) SignIn(ctx context.Context, in *accounts.AccountWithPassword) (*accounts.AccountFullWithToken, error) {
data := controllers.AccountData{ data := populateData(in.Data.GetName(), in.Password.GetPassword(), in.Data.GetEmail())
Username: in.Data.GetName(), acc := populateAccount(data, a.Controller)
Password: in.Password.GetPassword(),
Email: in.Data.GetEmail(),
}
acc := controllers.Account{
Controller: a.Controller,
Data: &data,
}
if err := acc.Login(ctx); err != nil { if err := acc.Login(ctx); err != nil {
return nil, err return nil, err
} }
return &accounts.AccountFullWithToken{ return &accounts.AccountFullWithToken{
Id: &accounts.AccountId{ Id: &accounts.AccountId{
Id: acc.Data.UUID, Id: acc.Data.UUID,
}, },
Data: &accounts.AccountData{ Data: &accounts.AccountData{
Name: acc.Data.Username, Name: acc.Data.Username,
Email: acc.Data.Email, Email: acc.Data.Email,
}, },
@ -75,10 +64,18 @@ func (a *AccountsServer) SignIn(ctx context.Context, in *accounts.AccountWithPas
}, nil }, nil
} }
//func (a *AccountsServer) ValidateEmail(ctx context.Context, in *accounts.AccountDataWithEmailCode) (*emptypb.Empty, error) { func populateData(username, password, email string) *controllers.AccountData {
// data := controllers.AccountData { return &controllers.AccountData{
// Username: in.Data.GetName(), Username: username,
// Email: in.Data.GetEmail(), Password: password,
// } Email: email,
// acc := controllers.Account {} }
//} }
func populateAccount(data *controllers.AccountData, controller ctrl.Manager) *controllers.Account {
return &controllers.Account{
Controller: controller,
Data: data,
}
}

View File

@ -7,8 +7,8 @@ import (
"log" "log"
"time" "time"
"git.badhouseplants.net/softplayer/softplayer-backend/internal/helpers/hash"
"github.com/google/uuid" "github.com/google/uuid"
"golang.org/x/crypto/bcrypt"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1" rbacv1 "k8s.io/api/rbac/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -20,14 +20,15 @@ import (
type Account struct { type Account struct {
Controller ctrl.Manager Controller ctrl.Manager
Params AccountParams Params AccountParams
Data *AccountData Data *AccountData
Token string Token string
} }
type AccountParams struct { type AccountParams struct {
HashCost int16 HashCost int16
} }
type AccountData struct { type AccountData struct {
Username string Username string
Password string Password string
@ -35,29 +36,19 @@ type AccountData struct {
UUID string UUID string
} }
func HashPassword(password string) (string, error) { func waitUntilCreated(ctx context.Context, client client.Client, obj client.Object, attemps int, timeout time.Duration) error {
bytes, err := bcrypt.GenerateFromPassword([]byte(password), 1)
return string(bytes), err
}
func CheckPasswordHash(password, hash string) bool {
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
return err == nil
}
func waitUntilCreated(ctx context.Context, client client.Client ,obj client.Object, attemps int, timeout time.Duration) error {
log.Printf("Waiting %d", attemps) log.Printf("Waiting %d", attemps)
if err := client.Get(ctx, types.NamespacedName{ if err := client.Get(ctx, types.NamespacedName{
Namespace: obj.GetNamespace(), Namespace: obj.GetNamespace(),
Name: obj.GetName(), Name: obj.GetName(),
}, obj); err != nil { }, obj); err != nil {
if attemps > 0 { if attemps > 0 {
time.Sleep(timeout) time.Sleep(timeout)
waitUntilCreated(ctx, client, obj, attemps - 1, timeout) waitUntilCreated(ctx, client, obj, attemps-1, timeout)
} else { } else {
return err return err
} }
} }
return nil return nil
} }
@ -66,11 +57,11 @@ func (acc *Account) Create(ctx context.Context) error {
acc.Data.UUID = uuid.New().String() acc.Data.UUID = uuid.New().String()
log.Println(acc.Data.UUID) log.Println(acc.Data.UUID)
passwordHash, err := HashPassword(acc.Data.Password) passwordHash, err := hash.HashPassword(acc.Data.Password, int(acc.Params.HashCost))
if err != nil { if err != nil {
return nil return nil
} }
namespace := corev1.Namespace{ namespace := corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: acc.Data.UUID, Name: acc.Data.UUID,
@ -81,13 +72,12 @@ func (acc *Account) Create(ctx context.Context) error {
return err return err
} }
if err := waitUntilCreated(ctx, client, &namespace, 10, time.Millisecond * 50); err != nil { if err := waitUntilCreated(ctx, client, &namespace, 10, time.Millisecond*50); err != nil {
return err return err
} }
if err := client.Get(ctx, types.NamespacedName{ if err := client.Get(ctx, types.NamespacedName{
Name: acc.Data.UUID, Name: acc.Data.UUID,
}, &namespace); err != nil { }, &namespace); err != nil {
if err := client.Delete(ctx, &namespace); err != nil { if err := client.Delete(ctx, &namespace); err != nil {
return err return err
@ -97,14 +87,14 @@ func (acc *Account) Create(ctx context.Context) error {
// Create a secret with the account data // Create a secret with the account data
secret := corev1.Secret{ secret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: acc.Data.Username, Name: acc.Data.Username,
Namespace: "softplayer-accounts", Namespace: "softplayer-accounts",
OwnerReferences: []metav1.OwnerReference{ OwnerReferences: []metav1.OwnerReference{
metav1.OwnerReference{ metav1.OwnerReference{
APIVersion: "v1", APIVersion: "v1",
Kind: "Namespace", Kind: "Namespace",
Name: acc.Data.UUID, Name: acc.Data.UUID,
UID: namespace.UID, UID: namespace.UID,
}, },
}, },
}, },
@ -132,26 +122,25 @@ func (acc *Account) Create(ctx context.Context) error {
return err return err
} }
sa := &corev1.ServiceAccount{ sa := &corev1.ServiceAccount{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: acc.Data.UUID, Name: acc.Data.UUID,
Namespace: acc.Data.UUID, Namespace: acc.Data.UUID,
}, },
} }
rb := &rbacv1.RoleBinding{ rb := &rbacv1.RoleBinding{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: acc.Data.UUID, Name: acc.Data.UUID,
Namespace: acc.Data.UUID, Namespace: acc.Data.UUID,
}, },
Subjects: []rbacv1.Subject{ Subjects: []rbacv1.Subject{
rbacv1.Subject{ rbacv1.Subject{
Kind: "ServiceAccount", Kind: "ServiceAccount",
Name: acc.Data.UUID, Name: acc.Data.UUID,
Namespace: acc.Data.UUID, Namespace: acc.Data.UUID,
}, },
}, },
RoleRef: rbacv1.RoleRef{ RoleRef: rbacv1.RoleRef{
APIGroup: "rbac.authorization.k8s.io", APIGroup: "rbac.authorization.k8s.io",
Kind: "Role", Kind: "Role",
Name: acc.Data.Username, Name: acc.Data.Username,
@ -175,13 +164,13 @@ func (acc *Account) Create(ctx context.Context) error {
tokenName := fmt.Sprintf("sa-%s", acc.Data.UUID) tokenName := fmt.Sprintf("sa-%s", acc.Data.UUID)
saSec := &corev1.Secret{ saSec := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: tokenName, Name: tokenName,
Namespace: acc.Data.UUID, Namespace: acc.Data.UUID,
Annotations: map[string]string{ Annotations: map[string]string{
"kubernetes.io/service-account.name": acc.Data.UUID, "kubernetes.io/service-account.name": acc.Data.UUID,
}, },
}, },
Type: "kubernetes.io/service-account-token", Type: "kubernetes.io/service-account-token",
} }
if err := client.Create(ctx, saSec); err != nil { if err := client.Create(ctx, saSec); err != nil {
@ -190,7 +179,7 @@ func (acc *Account) Create(ctx context.Context) error {
} }
return err return err
} }
if err := waitUntilCreated(ctx, client, saSec, 10, time.Millisecond * 50); err != nil { if err := waitUntilCreated(ctx, client, saSec, 10, time.Millisecond*50); err != nil {
return err return err
} }
@ -204,7 +193,7 @@ func (acc *Account) Create(ctx context.Context) error {
return nil return nil
} }
func (acc *Account) Login (ctx context.Context) error { func (acc *Account) Login(ctx context.Context) error {
client := acc.Controller.GetClient() client := acc.Controller.GetClient()
sec := &corev1.Secret{} sec := &corev1.Secret{}
if err := client.Get(ctx, types.NamespacedName{ if err := client.Get(ctx, types.NamespacedName{
@ -213,7 +202,7 @@ func (acc *Account) Login (ctx context.Context) error {
}, sec); err != nil { }, sec); err != nil {
return err return err
} }
if !CheckPasswordHash(acc.Data.Password, string(sec.Data["password"])){ if !hash.CheckPasswordHash(acc.Data.Password, string(sec.Data["password"])) {
err := errors.New("wrong password") err := errors.New("wrong password")
return err return err
} }
@ -221,30 +210,29 @@ func (acc *Account) Login (ctx context.Context) error {
tokenName := fmt.Sprintf("sa-%s", acc.Data.UUID) tokenName := fmt.Sprintf("sa-%s", acc.Data.UUID)
saSec := &corev1.Secret{ saSec := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: tokenName, Name: tokenName,
Namespace: acc.Data.UUID, Namespace: acc.Data.UUID,
Annotations: map[string]string{ Annotations: map[string]string{
"kubernetes.io/service-account.name": acc.Data.UUID, "kubernetes.io/service-account.name": acc.Data.UUID,
}, },
}, },
Type: "kubernetes.io/service-account-token", Type: "kubernetes.io/service-account-token",
} }
var err error var err error
acc.Token, err = acc.getToken(ctx, saSec) acc.Token, err = acc.getToken(ctx, saSec)
if err != nil{ if err != nil {
return err return err
} }
return nil return nil
} }
func (acc *Account) getToken (ctx context.Context, saSec *corev1.Secret) (string, error) { func (acc *Account) getToken(ctx context.Context, saSec *corev1.Secret) (string, error) {
client := acc.Controller.GetClient() client := acc.Controller.GetClient()
if err := client.Get(ctx, types.NamespacedName{ if err := client.Get(ctx, types.NamespacedName{
Namespace: acc.Data.UUID, Namespace: acc.Data.UUID,
Name: saSec.ObjectMeta.Name, Name: saSec.ObjectMeta.Name,
}, saSec); err != nil { }, saSec); err != nil {
return "", err return "", err
} }
return string(saSec.Data["token"]), nil return string(saSec.Data["token"]), nil
} }

View File

@ -0,0 +1,13 @@
# package controllers
import "context"
type EmailSvc struct {}
type EmailData strict {
UserID string
}
func (svc *EmailSvc) SendVerification(ctx context.Context) {
}

View File

@ -5,13 +5,13 @@ import (
) )
type EmailConf struct { type EmailConf struct {
From string From string
Password string Password string
SmtpHost string SmtpHost string
SmtpPort string SmtpPort string
} }
func (e *EmailConf) SendEmail (to string, message string )error { func (e *EmailConf) SendEmail(to string, message string) error {
messageByte := []byte(message) messageByte := []byte(message)
auth := smtp.PlainAuth("", e.From, e.Password, e.SmtpHost) auth := smtp.PlainAuth("", e.From, e.Password, e.SmtpHost)
@ -19,4 +19,4 @@ func (e *EmailConf) SendEmail (to string, message string )error {
return err return err
} }
return nil return nil
} }

View File

@ -8,7 +8,6 @@ func HashPassword(password string, cost int) (string, error) {
} }
func CheckPasswordHash(password, hash string) bool { func CheckPasswordHash(password, hash string) bool {
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
return err == nil return err == nil
} }

View File

@ -8,14 +8,14 @@ import (
) )
func TestHashValid(t *testing.T) { func TestHashValid(t *testing.T) {
password := "qwertyu9" password := "qwertyu9"
hpass, err := hash.HashPassword(password, 10) hpass, err := hash.HashPassword(password, 10)
assert.NoError(t, err) assert.NoError(t, err)
assert.True(t, hash.CheckPasswordHash(password, hpass)) assert.True(t, hash.CheckPasswordHash(password, hpass))
} }
func TestHashInvalid(t *testing.T) { func TestHashInvalid(t *testing.T) {
password := "qwertyu9" password := "qwertyu9"
invhash := "qwertyu9" invhash := "qwertyu9"
assert.False(t, hash.CheckPasswordHash(password, invhash)) assert.False(t, hash.CheckPasswordHash(password, invhash))
} }

19
main.go
View File

@ -16,14 +16,14 @@ import (
) )
type Serve struct { type Serve struct {
Port int16 `short:"p" env:"SOFTPLAYER_PORT" default:"8080"` Port int16 `short:"p" env:"SOFTPLAYER_PORT" default:"8080"`
Host string `env:"SOFTPLAYER_HOST" default:"0.0.0.0"` Host string `env:"SOFTPLAYER_HOST" default:"0.0.0.0"`
HashCost int16 `env:"SOFTPLAYER_HASH_COST" default:"10"` HashCost int16 `env:"SOFTPLAYER_HASH_COST" default:"10"`
Reflection bool `env:"SOFTPLAYER_REFLECTION" default:"false"` Reflection bool `env:"SOFTPLAYER_REFLECTION" default:"false"`
SmtpHost string `env:"SOFTPLAYER_SMTP_HOST"` SmtpHost string `env:"SOFTPLAYER_SMTP_HOST"`
SmtpPort string `env:"SOFTPLAYER_SMTP_PORT" default:"587"` SmtpPort string `env:"SOFTPLAYER_SMTP_PORT" default:"587"`
SmtpFrom string `env:"SOFTPLAYER_SMTP_FROM" default:"overlord@badhouseplants.net"` SmtpFrom string `env:"SOFTPLAYER_SMTP_FROM" default:"overlord@badhouseplants.net"`
SmtpPassword string `env:"SOFTPLAYER_SMTP_PASSWORD"` SmtpPassword string `env:"SOFTPLAYER_SMTP_PASSWORD"`
} }
var CLI struct { var CLI struct {
@ -42,14 +42,13 @@ func main() {
} }
} }
func server(params Serve) error { func server(params Serve) error {
controller, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{}) controller, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{})
if err != nil { if err != nil {
return err return err
} }
go func(){ go func() {
controller.Start(context.Background()) controller.Start(context.Background())
}() }()