Skip to content
Permalink
Browse files

feat: implement CLI 'sql adduser' command

  • Loading branch information...
moul committed May 23, 2019
1 parent 0179199 commit a6fc90273a7d6f206800507725c7c2f2e5a7b11c
Showing with 358 additions and 270 deletions.
  1. +2 −0 Makefile
  2. +4 −2 docker-compose.yml
  3. +268 −230 entity/entity.pb.go
  4. +1 −0 entity/entity.proto
  5. +1 −1 go.mod
  6. +4 −3 hypervisor/cmd_hypervisor_run.go
  7. +3 −2 main.go
  8. +2 −2 hypervisor/util.go → pkg/randstring/randstring.go
  9. +59 −29 sql/cmd_sql_adduser.go
  10. +5 −1 sql/sql.go
  11. +9 −0 swagger.yaml
@@ -136,6 +136,8 @@ integration.build:
.PHONY:integration.run
integration.run:
docker-compose up -d --no-build server
docker-compose run server "./wait-for-it.sh mysql:3306 -- echo server ready"
docker-compose run server "pathwar.pw sql adduser --sql-config=$$SQL_CONFIG --email=integration@example.com --username=integration --password=integration"
docker-compose run web npm test

.PHONY: lint
@@ -1,4 +1,4 @@
version: '2.3'
version: '3'

services:
server:
@@ -7,8 +7,10 @@ services:
entrypoint:
- "/bin/bash"
- "-c"
environment:
- SQL_CONFIG='root:uns3cur3@tcp(mysql:3306)/pathwar?charset=utf8&parseTime=true'
command:
- "./wait-for-it.sh mysql:3306 -- /bin/pathwar.pw server --sql-config='root:uns3cur3@tcp(mysql:3306)/pathwar?charset=utf8&parseTime=true' --http-bind=0.0.0.0:8000 --grpc-bind=0.0.0.0:9111"
- "./wait-for-it.sh mysql:3306 -- /bin/pathwar.pw server --http-bind=0.0.0.0:8000 --grpc-bind=0.0.0.0:9111"
depends_on:
- mysql
ports:

Large diffs are not rendered by default.

@@ -99,6 +99,7 @@ message AuthMethod {
string totp_token = 104;
string url = 105;
bool is_verified = 107;
Provider provider = 108;

//
// associations
2 go.mod
@@ -36,7 +36,7 @@ require (
github.com/spf13/pflag v1.0.3
github.com/spf13/viper v1.3.1
go.uber.org/zap v1.9.1
golang.org/x/crypto v0.0.0-20190103213133-ff983b9c42bc // indirect
golang.org/x/crypto v0.0.0-20190103213133-ff983b9c42bc
golang.org/x/net v0.0.0-20190110044637-be1c187aa6c6 // indirect
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c // indirect
@@ -23,6 +23,7 @@ import (
"go.uber.org/zap"

"pathwar.pw/pkg/cli"
"pathwar.pw/pkg/randstring"
pwctlconfig "pathwar.pw/pwctl/config"
)

@@ -123,9 +124,9 @@ func runRun(opts runOptions) error {

pwctlConfig := pwctlconfig.Config{
Passphrases: []string{
randString(10),
randString(10),
randString(10),
randstring.RandString(10),
randstring.RandString(10),
randstring.RandString(10),
},
}
// if !pwctlConfig.Validate() ...
@@ -65,6 +65,9 @@ func newRootCommand() *cobra.Command {
// setup viper
viper.AddConfigPath(".")
viper.SetConfigName(".pathwar")
viper.SetEnvPrefix("PATHWAR")
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
viper.AutomaticEnv()
if err := viper.MergeInConfig(); err != nil {
if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
return errors.Wrap(err, "failed to apply viper config")
@@ -87,7 +90,5 @@ func newRootCommand() *cobra.Command {
cmd.AddCommand(command.CobraCommand(commands))
}

viper.AutomaticEnv()
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
return cmd
}
@@ -1,4 +1,4 @@
package hypervisor // import "pathwar.pw/hypervisor"
package randstring // import "pathwar.pw/randstring"

import "math/rand"

@@ -11,7 +11,7 @@ const (
letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits
)

func randString(n int) string {
func RandString(n int) string {
b := make([]byte, n)
// A rand.Int63() generates 63 random bits, enough for letterIdxMax letters!
for i, cache, remain := n-1, rand.Int63(), letterIdxMax; i >= 0; {
@@ -1,22 +1,30 @@
package sql

import (
"encoding/json"
"errors"
"fmt"

"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/spf13/viper"
"go.uber.org/zap"
"golang.org/x/crypto/bcrypt"

"pathwar.pw/entity"
"pathwar.pw/pkg/cli"
"pathwar.pw/pkg/randstring"
)

type adduserOptions struct {
sql Options `mapstructure:"sql"`

email string `mapstructure:"email"`
username string `mapstructure:"username"`
password string `mapstructure:"password"`
email string `mapstructure:"email"`
username string `mapstructure:"username"`
locale string `mapstructure:"locale"`
password string `mapstructure:"password"`
websiteURL string `mapstructure:"website-url"`
isStaff bool `mapstructure:"is-staff"`
}

type adduserCommand struct{ opts adduserOptions }
@@ -45,41 +53,63 @@ func (cmd *adduserCommand) ParseFlags(flags *pflag.FlagSet) {
flags.StringVarP(&cmd.opts.email, "email", "", "", "valid email address")
flags.StringVarP(&cmd.opts.username, "username", "", "", "random value if empty")
flags.StringVarP(&cmd.opts.password, "password", "", "", "random value if empty")
flags.StringVarP(&cmd.opts.locale, "locale", "", "fr_FR", "locale")
flags.StringVarP(&cmd.opts.websiteURL, "website-url", "", "", "website url")
flags.BoolVarP(&cmd.opts.isStaff, "is-staff", "", false, "is staff?")
if err := viper.BindPFlags(flags); err != nil {
zap.L().Warn("failed to bind viper flags", zap.Error(err))
}
}

func runAdduser(opts adduserOptions) error {
return fmt.Errorf("implementation is outdated and needs to be updated")
/*
db, err := FromOpts(&opts.sql)
if err != nil {
return err
}
db, err := FromOpts(&opts.sql)
if err != nil {
return err
}

if opts.password == "" {
opts.password = randstring.RandString(15)
zap.L().Info("password is empty, generating a new one", zap.String("password", opts.password))
}
if opts.username == "" {
opts.username = randstring.RandString(10)
zap.L().Info("username is empty, generating a new one", zap.String("username", opts.username))
}
hash, err := bcrypt.GenerateFromPassword([]byte(opts.password), bcrypt.DefaultCost)
if err != nil {
return err
}

user := entity.User{
Email: opts.email,
Username: opts.username,
PasswordSalt: "FIXME: randomize",
}
user.PasswordHash = "FIXME: generate"
user := entity.User{
Username: opts.username,
WebsiteURL: opts.websiteURL,
IsStaff: opts.isStaff,
Locale: opts.locale,
AuthMethods: []*entity.AuthMethod{
{
Identifier: opts.email,
EmailAddress: opts.email,
PasswordHash: string(hash),
Provider: entity.AuthMethod_EmailAndPassword,
IsVerified: true,
},
},
}

// FIXME: randomize username, password if empty
// FIXME: verify email address validity
// FIXME: verify email address spam/blacklist
// FIXME: user.Validate()
// FIXME: verify email address validity
// FIXME: verify email address spam/blacklist
// FIXME: verify email for duplicate
// FIXME: user.Validate()

if err := db.Create(&user).Error; err != nil {
return err
}
if err := db.Create(&user).Error; err != nil {
return err
}

out, err := json.MarshalIndent(user, "", " ")
if err != nil {
return err
}
fmt.Println(string(out))
out, err := json.MarshalIndent(user, "", " ")
if err != nil {
return err
}
fmt.Println(string(out))

return nil
*/
return nil
}
@@ -16,7 +16,11 @@ import (
)

func FromOpts(opts *Options) (*gorm.DB, error) {
db, err := gorm.Open("mysql", opts.Config)
sqlConfig := opts.Config
if envConfig := os.Getenv("SQL_CONFIG"); envConfig != "" { // this should be done using viper's built-in env support
sqlConfig = envConfig
}
db, err := gorm.Open("mysql", sqlConfig)
if err != nil {
return nil, err
}
@@ -3,6 +3,13 @@ basePath: /
consumes:
- application/json
definitions:
AuthMethodProvider:
default: Undefined
enum:
- Undefined
- EmailAndPassword
- SSHKey
type: string
InventoryItemItem:
default: Unknown
enum:
@@ -95,6 +102,8 @@ definitions:
$ref: '#/definitions/entityMetadata'
password_hash:
type: string
provider:
$ref: '#/definitions/AuthMethodProvider'
salt:
type: string
totp_token:

0 comments on commit a6fc902

Please sign in to comment.
You can’t perform that action at this time.