working commit

This commit is contained in:
2026-02-13 13:25:16 +02:00
parent c2d231c844
commit 930df60877
11 changed files with 343 additions and 37 deletions
+2 -1
View File
@@ -46,6 +46,7 @@ type Config struct {
Keypath string `json:"keypath,omitempty" yaml:"keypath"` Keypath string `json:"keypath,omitempty" yaml:"keypath"`
X509Cert string `json:"-" yaml:"-"` X509Cert string `json:"-" yaml:"-"`
X509Key string `json:"-" yaml:"-"` X509Key string `json:"-" yaml:"-"`
Datadir string `json:"datadir"`
} }
func NewConfig() *Config { func NewConfig() *Config {
@@ -76,7 +77,7 @@ func NewConfig() *Config {
Logpath: logpath, Logpath: logpath,
Runpath: runpath, Runpath: runpath,
Version: version, Version: version,
Datadir: datadir,
//Certpath: certpath, //Certpath: certpath,
//Keypath: keypath, //Keypath: keypath,
} }
+23 -4
View File
@@ -10,12 +10,20 @@
package descr package descr
const AnonymousID = "10000000-0000-0000-0000-000000000001" const (
AnonimousUsername = "anonymous"
AnonymousID = "10000000-0000-0000-0000-000000000001"
ServerUsername = "server"
ServerID = "10000000-0000-0000-0000-000000000002"
InitUsername = "mstore"
InitID = "10000000-0000-0000-0000-000000000005"
)
type Grant struct { type Grant struct {
ID string `json:"id" db:"id"` ID string `json:"id" db:"id"`
AccountID string `json:"accountID" db:"account_id"` AccountID string `json:"accountID" db:"account_id"`
Operation string `json:"operation" db:"operation"` Right string `json:"right" db:"right"`
Pattern string `json:"pattern" db:"pattern"` Pattern string `json:"pattern" db:"pattern"`
CreatedAt string `json:"createdAt" db:"created_at"` CreatedAt string `json:"createdAt" db:"created_at"`
UpdatedAt string `json:"updatedAt" db:"updated_at"` UpdatedAt string `json:"updatedAt" db:"updated_at"`
@@ -24,7 +32,7 @@ type Grant struct {
} }
type GrantShort struct { type GrantShort struct {
Operation string `json:"operation" db:"operation"` Right string `json:"right" db:"right"`
Pattern string `json:"pattern" db:"pattern"` Pattern string `json:"pattern" db:"pattern"`
CreatedAt string `json:"createdAt" db:"created_at"` CreatedAt string `json:"createdAt" db:"created_at"`
UpdatedAt string `json:"updatedAt" db:"updated_at"` UpdatedAt string `json:"updatedAt" db:"updated_at"`
@@ -33,5 +41,16 @@ type GrantShort struct {
} }
const ( const (
GrantCreateAccount = "createAccount" // Accounts
RightReadAccounts = "readAccounts" // GetAccount, ListAccounts
RightWriteAccounts = "writeAccounts" // CreateAccount, UpdateAccount, DeleteAccount
// Frants
RightReadGrants = "readGrants" // Like account operation
RightWriteGrants = "writeGrants"
// Files
RightWriteFiles = "writeFiles" // FileInfo, GetFile, ListFiles
RightReadFiles = "readFiles" // PutFile, DeleteFile
// Images: manifests, layers
RightReadImages = "readImages" // ManifestInfo, GetManifest, BlobInfo, GetBlob
RightWriteImages = "writeImages" // other opearion
) )
+17
View File
@@ -0,0 +1,17 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*
* This work is published and licensed under a Creative Commons
* Attribution-NonCommercial-NoDerivatives 4.0 International License.
*
* Distribution of this work is permitted, but commercial use and
* modifications are strictly prohibited.
*/
package descr
type Server struct {
SchemeCreated bool `json:"SchemeCreated"`
AnonymousCreated bool `json:"AnonymousCreated"`
InituserCreated bool `json:"inituserCreated"`
SchemeCreatedAt string `json:"inituserCreatedAt"`
}
+8 -6
View File
@@ -9,10 +9,12 @@ import (
"mstore/app/router" "mstore/app/router"
) )
func (hand *Handler) CheckGrant(ctx context.Context, accountID, grant, subject string) (bool, error) { func (hand *Handler) CheckRight(ctx context.Context, accountID, right, subject string) (bool, error) {
var err error var err error
var res bool var res bool
hand.logg.Debugf("CheckRight %s: %s %s", accountID, right, subject)
res = true
return res, err return res, err
} }
@@ -22,9 +24,13 @@ func (hand *Handler) CreateAccount(rctx *router.Context) {
params := &operator.CreateAccountParams{} params := &operator.CreateAccountParams{}
err = rctx.BindJSON(params) err = rctx.BindJSON(params)
if err != nil {
hand.SendError(rctx, err)
return
}
operatorID, _ := rctx.GetString(userTag) operatorID, _ := rctx.GetString(userTag)
opEnable, err := hand.CheckGrant(rctx.Ctx, operatorID, descr.GrantCreateAccount, params.Username) opEnable, err := hand.CheckRight(rctx.Ctx, operatorID, descr.RightWriteAccounts, params.Username)
if err != nil { if err != nil {
err := fmt.Errorf("CreateAccount error: %v", err) err := fmt.Errorf("CreateAccount error: %v", err)
hand.SendError(rctx, err) hand.SendError(rctx, err)
@@ -36,10 +42,6 @@ func (hand *Handler) CreateAccount(rctx *router.Context) {
return return
} }
if err != nil {
hand.SendError(rctx, err)
return
}
res, err := hand.oper.CreateAccount(rctx.Ctx, operatorID, params) res, err := hand.oper.CreateAccount(rctx.Ctx, operatorID, params)
if err != nil { if err != nil {
hand.logg.Errorf("CreateAccount error: %v", err) hand.logg.Errorf("CreateAccount error: %v", err)
+9
View File
@@ -1,3 +1,12 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*
* This work is published and licensed under a Creative Commons
* Attribution-NonCommercial-NoDerivatives 4.0 International License.
*
* Distribution of this work is permitted, but commercial use and
* modifications are strictly prohibited.
*/
package handler package handler
import ( import (
+17 -8
View File
@@ -1,3 +1,12 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*
* This work is published and licensed under a Creative Commons
* Attribution-NonCommercial-NoDerivatives 4.0 International License.
*
* Distribution of this work is permitted, but commercial use and
* modifications are strictly prohibited.
*/
package maindb package maindb
import ( import (
@@ -8,9 +17,9 @@ import (
func (db *Database) InsertGrant(ctx context.Context, grant *descr.Grant) error { func (db *Database) InsertGrant(ctx context.Context, grant *descr.Grant) error {
var err error var err error
request := `INSERT INTO grants(id, account_id, operation, pattern, created_at, updated_at, created_by, updated_by) request := `INSERT INTO grants(id, account_id, right, pattern, created_at, updated_at, created_by, updated_by)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)` VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`
_, err = db.db.Exec(request, grant.ID, grant.AccountID, grant.Operation, grant.Pattern, _, err = db.db.Exec(request, grant.ID, grant.AccountID, grant.Right, grant.Pattern,
grant.CreatedAt, grant.UpdatedAt, grant.CreatedBy, grant.UpdatedBy) grant.CreatedAt, grant.UpdatedAt, grant.CreatedBy, grant.UpdatedBy)
if err != nil { if err != nil {
return err return err
@@ -67,12 +76,12 @@ func (db *Database) GetGrantByID(ctx context.Context, id string) (bool, *descr.G
return true, res, err return true, res, err
} }
func (db *Database) GetGrantByAccoundIDOperationPattern(ctx context.Context, accountID, operation, pattern string) (bool, *descr.Grant, error) { func (db *Database) GetGrantByAccoundIDRightPattern(ctx context.Context, accountID, right, pattern string) (bool, *descr.Grant, error) {
var err error var err error
res := &descr.Grant{} res := &descr.Grant{}
request := `SELECT * FROM grants WHERE account_id = $1 AND operation = $2 AND pattern = $3 LIMIT 1` request := `SELECT * FROM grants WHERE account_id = $1 AND right = $2 AND pattern = $3 LIMIT 1`
dbRes := make([]descr.Grant, 0) dbRes := make([]descr.Grant, 0)
err = db.db.Select(&dbRes, request, accountID, operation, pattern) err = db.db.Select(&dbRes, request, accountID, right, pattern)
if err != nil { if err != nil {
return false, res, err return false, res, err
} }
@@ -84,10 +93,10 @@ func (db *Database) GetGrantByAccoundIDOperationPattern(ctx context.Context, acc
return true, res, err return true, res, err
} }
func (db *Database) DeleteGrantByAccountIDOperationPattern(ctx context.Context, accountID, operation, pattern string) error { func (db *Database) DeleteGrantByAccountIDRightPattern(ctx context.Context, accountID, right, pattern string) error {
var err error var err error
request := `DELETE FROM grants WHERE account_id = $1 AND operation = $2 AND pattern = $3` request := `DELETE FROM grants WHERE account_id = $1 AND right = $2 AND pattern = $3`
_, err = db.db.Exec(request, accountID, operation, pattern) _, err = db.db.Exec(request, accountID, right, pattern)
if err != nil { if err != nil {
return err return err
} }
+154
View File
@@ -0,0 +1,154 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*
* This work is published and licensed under a Creative Commons
* Attribution-NonCommercial-NoDerivatives 4.0 International License.
*
* Distribution of this work is permitted, but commercial use and
* modifications are strictly prohibited.
*/
package maindb
import (
"context"
"mstore/app/descr"
"mstore/pkg/auxpwd"
"mstore/pkg/auxtool"
"mstore/pkg/auxuuid"
)
func (db *Database) WriteAnonymous(ctx context.Context) error {
var err error
now := auxtool.TimeNow()
password := auxtool.RandomString(64)
passhash := auxpwd.MakeSHA256Hash([]byte(password))
accountDescr := &descr.Account{
ID: auxuuid.NewUUID(),
Username: descr.AnonimousUsername,
Passhash: passhash,
Disabled: false,
CreatedAt: now,
UpdatedAt: now,
CreatedBy: descr.ServerID,
UpdatedBy: descr.ServerID,
}
err = db.InsertAccount(ctx, accountDescr)
if err != nil {
return err
}
grantDescr := &descr.Grant{
ID: auxuuid.NewUUID(),
AccountID: accountDescr.ID,
Right: descr.RightReadFiles,
Pattern: "*",
CreatedAt: now,
UpdatedAt: now,
CreatedBy: descr.ServerID,
UpdatedBy: descr.ServerID,
}
err = db.InsertGrant(ctx, grantDescr)
if err != nil {
return err
}
grantDescr = &descr.Grant{
ID: auxuuid.NewUUID(),
AccountID: accountDescr.ID,
Right: descr.RightReadFiles,
Pattern: "*",
CreatedAt: now,
UpdatedAt: now,
CreatedBy: descr.ServerID,
UpdatedBy: descr.ServerID,
}
err = db.InsertGrant(ctx, grantDescr)
if err != nil {
return err
}
return err
}
func (db *Database) WriteInituser(ctx context.Context) error {
var err error
now := auxtool.TimeNow()
passhash := auxpwd.MakeSHA256Hash([]byte(descr.InitUsername))
accountDescr := &descr.Account{
ID: auxuuid.NewUUID(),
Username: descr.AnonimousUsername,
Passhash: passhash,
Disabled: false,
CreatedAt: now,
UpdatedAt: now,
CreatedBy: descr.ServerID,
UpdatedBy: descr.ServerID,
}
err = db.InsertAccount(ctx, accountDescr)
if err != nil {
return err
}
// Files
grantDescr := &descr.Grant{
ID: auxuuid.NewUUID(),
AccountID: accountDescr.ID,
Right: descr.RightReadFiles,
Pattern: "*",
CreatedAt: now,
UpdatedAt: now,
CreatedBy: descr.ServerID,
UpdatedBy: descr.ServerID,
}
err = db.InsertGrant(ctx, grantDescr)
if err != nil {
return err
}
grantDescr = &descr.Grant{
ID: auxuuid.NewUUID(),
AccountID: accountDescr.ID,
Right: descr.RightWriteFiles,
Pattern: "*",
CreatedAt: now,
UpdatedAt: now,
CreatedBy: descr.ServerID,
UpdatedBy: descr.ServerID,
}
err = db.InsertGrant(ctx, grantDescr)
if err != nil {
return err
}
// Images
grantDescr = &descr.Grant{
ID: auxuuid.NewUUID(),
AccountID: accountDescr.ID,
Right: descr.RightReadImages,
Pattern: "*",
CreatedAt: now,
UpdatedAt: now,
CreatedBy: descr.ServerID,
UpdatedBy: descr.ServerID,
}
err = db.InsertGrant(ctx, grantDescr)
if err != nil {
return err
}
grantDescr = &descr.Grant{
ID: auxuuid.NewUUID(),
AccountID: accountDescr.ID,
Right: descr.RightWriteImages,
Pattern: "*",
CreatedAt: now,
UpdatedAt: now,
CreatedBy: descr.ServerID,
UpdatedBy: descr.ServerID,
}
err = db.InsertGrant(ctx, grantDescr)
if err != nil {
return err
}
return err
}
+10 -2
View File
@@ -110,8 +110,12 @@ func (oper *Operator) GetAccount(ctx context.Context, params *GetAccountParams)
} }
for _, grantDescrs := range grantDescrs { for _, grantDescrs := range grantDescrs {
grantShorts := descr.GrantShort{ grantShorts := descr.GrantShort{
Operation: grantDescrs.Operation, Right: grantDescrs.Right,
Pattern: grantDescrs.Pattern,
CreatedAt: grantDescrs.CreatedAt, CreatedAt: grantDescrs.CreatedAt,
UpdatedAt: grantDescrs.UpdatedAt,
CreatedBy: grantDescrs.CreatedBy,
UpdatedBy: grantDescrs.UpdatedBy,
} }
accountShort.Grants = append(accountShort.Grants, grantShorts) accountShort.Grants = append(accountShort.Grants, grantShorts)
} }
@@ -253,8 +257,12 @@ func (oper *Operator) ListAccounts(ctx context.Context, params *ListAccountsPara
} }
for _, grantDescrs := range grantDescrs { for _, grantDescrs := range grantDescrs {
grantShorts := descr.GrantShort{ grantShorts := descr.GrantShort{
Operation: grantDescrs.Operation, Right: grantDescrs.Right,
Pattern: grantDescrs.Pattern,
CreatedAt: grantDescrs.CreatedAt, CreatedAt: grantDescrs.CreatedAt,
UpdatedAt: grantDescrs.UpdatedAt,
CreatedBy: grantDescrs.CreatedBy,
UpdatedBy: grantDescrs.UpdatedBy,
} }
accountShort.Grants = append(accountShort.Grants, grantShorts) accountShort.Grants = append(accountShort.Grants, grantShorts)
} }
+4 -4
View File
@@ -11,7 +11,7 @@ import (
type CreateGrantParams struct { type CreateGrantParams struct {
AccountID string `json:"accountID"` AccountID string `json:"accountID"`
Operation string `json:"operation"` Right string `json:"operation"`
Pattern string `json:"pattern"` Pattern string `json:"pattern"`
} }
type CreateGrantResult struct { type CreateGrantResult struct {
@@ -26,7 +26,7 @@ func (oper *Operator) CreateGrant(ctx context.Context, params *CreateGrantParams
err := fmt.Errorf("Empty accountId parameters") err := fmt.Errorf("Empty accountId parameters")
return res, err return res, err
} }
if params.Operation == "" { if params.Right == "" {
err := fmt.Errorf("Empty operation parameter") err := fmt.Errorf("Empty operation parameter")
return res, err return res, err
} }
@@ -35,7 +35,7 @@ func (oper *Operator) CreateGrant(ctx context.Context, params *CreateGrantParams
return res, err return res, err
} }
grantExists, _, err := oper.mdb.GetGrantByAccoundIDOperationPattern(ctx, params.AccountID, params.Operation, params.Pattern) grantExists, _, err := oper.mdb.GetGrantByAccoundIDRightPattern(ctx, params.AccountID, params.Right, params.Pattern)
if err != nil { if err != nil {
return res, err return res, err
} }
@@ -47,7 +47,7 @@ func (oper *Operator) CreateGrant(ctx context.Context, params *CreateGrantParams
grantDescr := &descr.Grant{ grantDescr := &descr.Grant{
ID: auxuuid.NewUUID(), ID: auxuuid.NewUUID(),
AccountID: params.AccountID, AccountID: params.AccountID,
Operation: params.Operation, Right: params.Right,
Pattern: params.Pattern, Pattern: params.Pattern,
CreatedAt: now, CreatedAt: now,
UpdatedAt: now, UpdatedAt: now,
+71 -12
View File
@@ -11,20 +11,26 @@
package server package server
import ( import (
"context"
"io/ioutil"
"os" "os"
"os/signal" "os/signal"
"os/user" "os/user"
"path/filepath" "path/filepath"
"strconv" "strconv"
"syscall" "syscall"
"time"
"mstore/app/config" "mstore/app/config"
"mstore/app/descr"
"mstore/app/handler" "mstore/app/handler"
"mstore/app/logger" "mstore/app/logger"
"mstore/app/maindb" "mstore/app/maindb"
"mstore/app/operator" "mstore/app/operator"
"mstore/app/service" "mstore/app/service"
"mstore/app/storage" "mstore/app/storage"
"sigs.k8s.io/yaml"
) )
type Server struct { type Server struct {
@@ -35,6 +41,7 @@ type Server struct {
hand *handler.Handler hand *handler.Handler
logg *logger.Logger logg *logger.Logger
stor *storage.Storage stor *storage.Storage
stat descr.Server
} }
func NewServer() (*Server, error) { func NewServer() (*Server, error) {
@@ -95,50 +102,102 @@ func (srv *Server) Configure() error {
func (srv *Server) Build() error { func (srv *Server) Build() error {
var err error var err error
srv.logg.Infof("Server build") srv.logg.Infof("Server building")
confDump := srv.conf.String() confDump := srv.conf.String()
srv.logg.Infof("Current server configuration is:\n%s\n", confDump) srv.logg.Infof("Current server configuration is:\n%s\n", confDump)
if srv.conf.AsDaemon { if srv.conf.AsDaemon {
logdir := filepath.Dir(srv.conf.Logpath) logdir := filepath.Dir(srv.conf.Logpath)
srv.logg.Infof("Create log directory %s", logdir) srv.logg.Infof("Creating log directory %s", logdir)
err = os.MkdirAll(logdir, 0750) err = os.MkdirAll(logdir, 0750)
if err != nil { if err != nil {
return err return err
} }
rundir := filepath.Dir(srv.conf.Runpath) rundir := filepath.Dir(srv.conf.Runpath)
srv.logg.Infof("Create run directory %s", rundir) srv.logg.Infof("Creating run directory %s", rundir)
err = os.MkdirAll(rundir, 0750) err = os.MkdirAll(rundir, 0750)
if err != nil { if err != nil {
return err return err
} }
} }
// Creating datadir
datadir := srv.conf.Datadir
srv.logg.Infof("Creating data directory %s ", datadir)
err = os.MkdirAll(datadir, 0750)
if err != nil {
return err
}
// Read state file
statefilePath := filepath.Join(srv.conf.Datadir, "server.yaml")
stateData, err := ioutil.ReadFile(statefilePath)
if err != nil {
err = yaml.Unmarshal(stateData, &srv.stat)
if err != nil {
return err
}
}
// Creating database // Creating database
dbdir := srv.conf.Database.Basepath dbdir := srv.conf.Database.Basepath
srv.logg.Infof("Create database directory %s ", dbdir) srv.logg.Infof("Creating database directory %s ", dbdir)
err = os.MkdirAll(dbdir, 0750) err = os.MkdirAll(dbdir, 0750)
if err != nil { if err != nil {
return err return err
} }
mdb := maindb.NewDatabase(dbdir) mdb := maindb.NewDatabase(dbdir)
srv.logg.Infof("Open main database") srv.logg.Infof("Opening main database")
err = mdb.OpenDatabase() err = mdb.OpenDatabase()
if err != nil { if err != nil {
return err return err
} }
srv.logg.Infof("Initialize main database")
err = mdb.InitDatabase()
if err != nil {
return err
}
srv.mdb = mdb srv.mdb = mdb
ctx, _ := context.WithTimeout(context.Background(), 5*time.Second)
// Created db scheme
if !srv.stat.SchemeCreated {
srv.logg.Infof("Initialize main database")
err = mdb.InitDatabase()
if err != nil {
return err
}
srv.stat.SchemeCreated = true
}
// Created anonymous user
if !srv.stat.AnonymousCreated {
srv.logg.Infof("Creating anonimous user")
err = mdb.WriteAnonymous(ctx)
if err != nil {
return err
}
srv.stat.AnonymousCreated = true
}
if !srv.stat.InituserCreated {
srv.logg.Infof("Creating init user")
err = srv.mdb.WriteAnonymous(ctx)
if err != nil {
return err
}
srv.stat.InituserCreated = true
}
// Write status file
stateData, err = yaml.Marshal(srv.stat)
if err != nil {
return err
}
err = ioutil.WriteFile(statefilePath, stateData, 0640)
if err != nil {
return err
}
// Creating storage // Creating storage
srv.logg.Infof("Create storage directory") srv.logg.Infof("Creating storage directory")
datadir := srv.conf.Database.Basepath datadir = srv.conf.Database.Basepath
err = os.MkdirAll(datadir, 0750) err = os.MkdirAll(datadir, 0750)
if err != nil { if err != nil {
return err return err
+28
View File
@@ -0,0 +1,28 @@
package auxtool
import (
"os"
)
func FileExists(name string) bool {
fileStat, err := os.Stat(name)
if err != nil {
if os.IsNotExist(err) {
return false
}
}
if fileStat.IsDir() {
return false
}
return true
}
func DirExists(name string) bool {
fileStat, err := os.Stat(name)
if err != nil {
if os.IsNotExist(err) {
return false
}
}
return fileStat.IsDir()
}