diff --git a/docker-compose.yml b/docker-compose.yml index 4881f38..4405a93 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,6 +13,20 @@ services: command: server /data --console-address ":9001" volumes: - minio_dev:/data + db: + container_name: db + image: mariadb + environment: + - MARIADB_ROOT_PASSWORD=hunter2 + - MARIADB_DATABASE=s3Browser + - MARIADB_USER=s3Browser + - MARIADB_PASSWORD=hunter2 + ports: + - 3306:3306 + volumes: + - mariadb_dev:/var/lib/mysql volumes: minio_dev: - name: minio_dev \ No newline at end of file + name: minio_dev + mariadb_dev: + name: mariadb_dev \ No newline at end of file diff --git a/go.mod b/go.mod index 87e5d65..6cc49d4 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.16 require ( github.com/alexflint/go-arg v1.4.2 + github.com/go-sql-driver/mysql v1.6.0 github.com/golang-jwt/jwt v3.2.2+incompatible github.com/gorilla/mux v1.8.0 github.com/graph-gophers/dataloader v5.0.0+incompatible @@ -13,4 +14,5 @@ require ( github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible github.com/sirupsen/logrus v1.8.1 + golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f ) diff --git a/go.sum b/go.sum index ecd472d..ebf04cd 100644 --- a/go.sum +++ b/go.sum @@ -7,6 +7,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= +github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= diff --git a/internal/db/db.go b/internal/db/db.go new file mode 100644 index 0000000..8b11e1f --- /dev/null +++ b/internal/db/db.go @@ -0,0 +1,96 @@ +package db + +import ( + "context" + "database/sql" + _ "embed" + "time" + + _ "github.com/go-sql-driver/mysql" + "golang.org/x/crypto/bcrypt" +) + +//go:embed setup.sql +var setupSql string + +const DB_NAME = "s3Browser" + +type DB struct { + dbConn *sql.DB +} + +func NewDB(driver, dataSourceName string) (*DB, error) { + db, err := sql.Open(driver, dataSourceName) + + if err != nil { + return nil, err + } + + if driver == "mysql" { + db.SetConnMaxLifetime(time.Minute * 3) + db.SetMaxOpenConns(10) + db.SetMaxIdleConns(10) + } + + return &DB{ + dbConn: db, + }, nil +} + +func (d *DB) Setup() error { + tx, err := d.dbConn.Begin() + if err != nil { + return err + } + + _, err = tx.Exec(setupSql) + if err != nil { + tx.Rollback() + return err + } + + err = tx.Commit() + if err != nil { + return err + } + + return nil +} + +func (d *DB) CheckLogin(ctx context.Context, username, password string) (bool, error) { + rows, err := d.dbConn.QueryContext(ctx, "SELECT password FROM user WHERE username = ?", username) + if err != nil { + return false, err + } + + if !rows.Next() { + return false, nil + } + + var passwordHash []byte + err = rows.Scan(&passwordHash) + if err != nil { + return false, err + } + + if bcrypt.CompareHashAndPassword(passwordHash, []byte(password)) != nil { + return false, nil + } + + return true, nil +} + +func (d *DB) AddUser(ctx context.Context, username, password string) error { + hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) + if err != nil { + return err + } + + _, err = d.dbConn.ExecContext(ctx, "INSERT INTO user (username,password) VALUES (?,?)", username, hash) + + if err != nil { + return err + } + + return nil +} diff --git a/internal/db/setup.sql b/internal/db/setup.sql new file mode 100644 index 0000000..b09916d --- /dev/null +++ b/internal/db/setup.sql @@ -0,0 +1,11 @@ +CREATE TABLE s3Browser.`user` ( + id INT auto_increment NOT NULL, + username varchar(100) NOT NULL, + password varchar(60) NOT NULL, + CONSTRAINT user_PK PRIMARY KEY (id), + CONSTRAINT user_UN UNIQUE KEY (username) +) + +ENGINE=InnoDB +DEFAULT CHARSET=utf8mb4 +COLLATE=utf8mb4_general_ci; diff --git a/internal/gql/mutations.go b/internal/gql/mutations.go index c5ce6ff..ca64d0e 100644 --- a/internal/gql/mutations.go +++ b/internal/gql/mutations.go @@ -7,6 +7,7 @@ import ( "github.com/minio/minio-go/v7" + "git.kapelle.org/niklas/s3browser/internal/db" helper "git.kapelle.org/niklas/s3browser/internal/helper" "git.kapelle.org/niklas/s3browser/internal/loader" types "git.kapelle.org/niklas/s3browser/internal/types" @@ -203,8 +204,11 @@ func deleteDirectory(ctx context.Context, id types.ID) error { //login Checks for valid username password combination. Returns singed jwt string func login(ctx context.Context, username, password string) (types.LoginResult, error) { - // TODO: replace with propper user management - if username != "admin" && password != "hunter2" { + dbStore := ctx.Value("dbStore").(*db.DB) + + succes, err := dbStore.CheckLogin(ctx, username, password) + + if !succes { return types.LoginResult{ Successful: false, }, nil diff --git a/internal/s3Broswer.go b/internal/s3Broswer.go index ff36d8c..b6da599 100644 --- a/internal/s3Broswer.go +++ b/internal/s3Broswer.go @@ -8,6 +8,7 @@ import ( "github.com/minio/minio-go/v7/pkg/credentials" log "github.com/sirupsen/logrus" + "git.kapelle.org/niklas/s3browser/internal/db" gql "git.kapelle.org/niklas/s3browser/internal/gql" httpserver "git.kapelle.org/niklas/s3browser/internal/httpserver" "git.kapelle.org/niklas/s3browser/internal/loader" @@ -55,6 +56,11 @@ func Start(config types.AppConfig) { } log.Info("s3 client connected") + dbStore, err := db.NewDB("mysql", "s3Browser:hunter2@/s3Browser") + if err != nil { + log.Error("Failed to connect DB: ", err.Error()) + } + log.Debug("Creating dataloader") loader := loader.NewLoader(config) @@ -69,6 +75,7 @@ func Start(config types.AppConfig) { resolveContext := context.WithValue(context.Background(), "s3Client", s3Client) resolveContext = context.WithValue(resolveContext, "loader", loader) + resolveContext = context.WithValue(resolveContext, "dbStore", dbStore) log.Debug("Starting HTTP server") err = httpserver.InitHttp(resolveContext, schema, config.Address)