Skip to content

Commit

Permalink
use aztable users when deployed
Browse files Browse the repository at this point in the history
  • Loading branch information
ivarprudnikov committed Apr 26, 2024
1 parent 420619a commit 980dbdf
Show file tree
Hide file tree
Showing 6 changed files with 169 additions and 3 deletions.
7 changes: 6 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,21 @@ module github.com/ivarprudnikov/secretshare
go 1.22

require (
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.2
github.com/Azure/azure-sdk-for-go/sdk/data/aztables v1.2.0
github.com/gorilla/sessions v1.2.2
golang.org/x/crypto v0.22.0
)

require (
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.2 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/securecookie v1.1.2 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
golang.org/x/net v0.22.0 // indirect
golang.org/x/sys v0.19.0 // indirect
golang.org/x/text v0.14.0 // indirect
Expand Down
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
Expand Down
10 changes: 10 additions & 0 deletions internal/configuration/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ const keyEnvironment = "SERVER_ENV"
const keySalt = "DB_SALT_KEY"
const keyCookieAuth = "COOK_AUTH_KEY"
const keyCookieEnc = "COOK_ENC_KEY"
const tableUsers = "AZTABLE_USERS"
const tableMessages = "AZTABLE_MESSAGES"
const envTest = "test"
const testKey = "12345678123456781234567812345678"
const requiredKeyLen = 32
Expand Down Expand Up @@ -53,6 +55,14 @@ func (c *ConfigReader) GetCookieEnc() string {
return c.getKey(keyCookieEnc, true)
}

func (c *ConfigReader) GetUsersTableName() string {
return c.getKey(tableUsers, true)
}

func (c *ConfigReader) GetMessagesTableName() string {
return c.getKey(tableMessages, true)
}

// Production environment expects the value to be set in the
// environmental variable. If not set the application will fail to start.
func (c *ConfigReader) getKey(name string, assert bool) string {
Expand Down
29 changes: 29 additions & 0 deletions internal/storage/aztablestore/aztable.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package aztablestore

import (
"errors"
"fmt"
"os"

"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
"github.com/Azure/azure-sdk-for-go/sdk/data/aztables"
)

// Use default function credentials and use it for the table client
// the expectation is that the function identity has access to the table
func getTableClient(tableName string) (*aztables.Client, error) {
accountName, ok := os.LookupEnv("AZURE_STORAGE_ACCOUNT")
if !ok {
return nil, errors.New("AZURE_STORAGE_ACCOUNT environment variable not found")
}
cred, err := azidentity.NewDefaultAzureCredential(nil)
if err != nil {
return nil, err
}
serviceURL := fmt.Sprintf("https://%s.table.core.windows.net/%s", accountName, tableName)
client, err := aztables.NewClient(serviceURL, cred, nil)
if err != nil {
return nil, err
}
return client, nil
}
117 changes: 117 additions & 0 deletions internal/storage/aztablestore/users.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package aztablestore

import (
"context"
"encoding/json"
"errors"

"github.com/Azure/azure-sdk-for-go/sdk/data/aztables"
"github.com/ivarprudnikov/secretshare/internal/storage"
)

type azUserStore struct {
tableName string
salt string
}

func NewAzUserStore(tableName, salt string) storage.UserStore {
return &azUserStore{tableName: tableName, salt: salt}
}

func (u *azUserStore) getClient() (*aztables.Client, error) {
return getTableClient(u.tableName)
}

// aztables does not have a way to query the size of the table
// need to scan through all of the records :(
func (u *azUserStore) CountUsers() (int64, error) {
var count int64 = 0
client, err := u.getClient()
if err != nil {
return count, err
}
keySelector := "$select=PartitionKey"
metadataFormat := aztables.MetadataFormatNone
listPager := client.NewListEntitiesPager(&aztables.ListEntitiesOptions{
Select: &keySelector,
Format: &metadataFormat,
})
for listPager.More() {
response, err := listPager.NextPage(context.TODO())
if err != nil {
return count, err
}
count += int64(len(response.Entities))
}
return count, nil
}

func (u *azUserStore) AddUser(username string, password string, permissions []string) (*storage.User, error) {
existing, err := u.GetUser(username)
if err != nil {
return nil, err
}
if existing != nil {
return nil, errors.New("username is not available")
}
usr, err := storage.NewUser(username, password, permissions)
if err != nil {
return nil, err
}
marshalled, err := json.Marshal(usr)
if err != nil {
return nil, err
}
client, err := u.getClient()
if err != nil {
return nil, err
}
resp, err := client.AddEntity(context.TODO(), marshalled, nil)
if err != nil {
return nil, err
}
var saved *storage.User
err = json.Unmarshal(resp.Value, &saved)
if err != nil {
return nil, err
}
return saved, nil
}

func (u *azUserStore) GetUser(username string) (*storage.User, error) {
client, err := u.getClient()
if err != nil {
return nil, err
}
resp, err := client.GetEntity(context.TODO(), username, username, nil)
if err != nil {
return nil, err
}
if resp.Value == nil {
return nil, nil
}
var user *storage.User
err = json.Unmarshal(resp.Value, &user)
if err != nil {
return nil, err
}
return user, nil
}

func (u *azUserStore) GetUserWithPass(username string, password string) (*storage.User, error) {
user, err := u.GetUser(username)
if err != nil {
return nil, err
}
hashedPass := "unknown"
if user != nil {
hashedPass = user.Password
}
// even if user is not found evaluate the password
// this will reduce the effect on time difference
// at the time of the login check
if err := storage.CompareHashToPass(hashedPass, password); err == nil {
return user, nil
}
return nil, nil
}
8 changes: 6 additions & 2 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/gorilla/sessions"
"github.com/ivarprudnikov/secretshare/internal/configuration"
"github.com/ivarprudnikov/secretshare/internal/storage"
"github.com/ivarprudnikov/secretshare/internal/storage/aztablestore"
"github.com/ivarprudnikov/secretshare/internal/storage/memstore"
)

Expand Down Expand Up @@ -53,9 +54,12 @@ func getPort() string {
// Production environment needs to work with Azure Table Storage which is not
// available locally. Locally an in-memory implementation of storage is used.
func getStorageImplementation(config *configuration.ConfigReader) (storage.MessageStore, storage.UserStore) {
messages := memstore.NewMemMessageStore(config.GetSalt())
users := memstore.NewMemUserStore(config.GetSalt())
var messages storage.MessageStore = memstore.NewMemMessageStore(config.GetSalt())
var users storage.UserStore = aztablestore.NewAzUserStore(config.GetUsersTableName(), config.GetSalt())
if !config.IsProd() {
messages = memstore.NewMemMessageStore(config.GetSalt())
users = memstore.NewMemUserStore(config.GetSalt())

// add test users
users.AddUser("joe", "joe", []string{})
users.AddUser("alice", "alice", []string{})
Expand Down

0 comments on commit 980dbdf

Please sign in to comment.