Skip to content

Commit

Permalink
initial progress on manager node auth
Browse files Browse the repository at this point in the history
  • Loading branch information
林志宇 committed Mar 20, 2019
1 parent aa9a29c commit 5dcb7df
Show file tree
Hide file tree
Showing 6 changed files with 293 additions and 13 deletions.
6 changes: 6 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,25 @@ require (
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5
github.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6
github.com/go-chi/chi v4.0.2+incompatible
github.com/google/go-cmp v0.2.0 // indirect
github.com/google/uuid v1.1.1
github.com/gorilla/sessions v1.1.3 // indirect
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/kr/pty v1.1.3
github.com/mattn/go-colorable v0.1.1 // indirect
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
github.com/mitchellh/go-homedir v1.1.0
github.com/pkg/errors v0.8.1 // indirect
github.com/sirupsen/logrus v1.4.0 // indirect
github.com/skycoin/skycoin v0.25.1
github.com/spf13/cobra v0.0.3
github.com/spf13/pflag v1.0.3 // indirect
github.com/stretchr/testify v1.3.0
github.com/volatiletech/authboss v2.2.0+incompatible // indirect
github.com/volatiletech/authboss-clientstate v0.0.0-20190112194853-0943df8b4e05 // indirect
go.etcd.io/bbolt v1.3.2
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a
golang.org/x/net v0.0.0-20190313220215-9f648a60d977
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421 // indirect
)
22 changes: 22 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
Expand All @@ -8,8 +9,17 @@ github.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6 h1:u/UEqS66A5ckRmS4yNp
github.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6/go.mod h1:1i71OnUq3iUe1ma7Lr6yG6/rjvM3emb6yoL7xLFzcVQ=
github.com/go-chi/chi v4.0.2+incompatible h1:maB6vn6FqCxrpz4FqWdh4+lwpyZIQS7YEAUcHlgXVRs=
github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/sessions v1.1.3 h1:uXoZdcdA5XdXF3QzuSlheVRUvjl+1rKY7zBXL68L9RU=
github.com/gorilla/sessions v1.1.3/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w=
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ=
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
Expand All @@ -26,6 +36,8 @@ github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1f
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.4.0 h1:yKenngtzGh+cUSSh6GWbxW2abRqhYUSR/t/6+2QqNvE=
Expand All @@ -41,17 +53,27 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/volatiletech/authboss v2.2.0+incompatible h1:12TCBRepupxjb9X59jTs+otNxfiY2a/elx2Dx0kPu9s=
github.com/volatiletech/authboss v2.2.0+incompatible/go.mod h1:EDBO8V+iiBoUR721My3a+iIeuH/1t6VcrCd5bl3v8Bs=
github.com/volatiletech/authboss-clientstate v0.0.0-20190112194853-0943df8b4e05 h1:djgQBr+jocgrtoRacNPgFCkg0ib6UtLSGzJ76jT+7yM=
github.com/volatiletech/authboss-clientstate v0.0.0-20190112194853-0943df8b4e05/go.mod h1:OfM17hvA6F9YRmXy2itY6fJiR4j4gXIIVs3NT1UPvoE=
go.etcd.io/bbolt v1.3.2 h1:Z/90sZLPOeCy2PwprqkFa25PdkusRzaj9P8zm/KNyvk=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a h1:YX8ljsm6wXlHZO+aRz9Exqr0evNhKRNe5K/gi+zKh4U=
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190313220215-9f648a60d977 h1:actzWV6iWn3GLqN8dZjzsB+CLt+gaV2+wsxroxiQI8I=
golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421 h1:Wo7BWFiOk0QRFMLYMqJGFMd9CgUAcGx7V+qEg/h5IBI=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
51 changes: 51 additions & 0 deletions pkg/manager/auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package manager

import (
"net/http"
"time"
)

const (
sessionCookieName = "swm_session"

boltTimeout = 10 * time.Second
boltUserBucketName = "users"
boltSessionBucketName = "sessions"
)

type CookieConfig struct {
Domain string // optional
MaxAge int
HTTPOnly bool
Secure bool
SameSite http.SameSite
}

type AuthConfig struct {
StorePath string
}

type Auth struct {

}

func NewAuth(c AuthConfig) (*Auth, error) {
return nil, nil
}

func (a *Auth) ChangePassword() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {

}
}

func (a *Auth) Login() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
}
}

func (a *Auth) Logout() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {

}
}
194 changes: 194 additions & 0 deletions pkg/manager/auth_store.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
package manager

import (
"encoding/json"
"errors"
"fmt"
"github.com/skycoin/skywire/internal/httputil"
"github.com/skycoin/skywire/pkg/cipher"
"go.etcd.io/bbolt"
"net/http"
"os"
"path/filepath"
)

type AuthErrorType string

const (
UnknownType = AuthErrorType("Unknown")
UserAlreadyExistsType = AuthErrorType("UserAlreadyExists")
VerificationFailedType = AuthErrorType("VerificationFailed")
)

type authError struct {
Type AuthErrorType
HTTPStatus int
HTTPMsg string
LogMsg string
}

func (e authError) Error() string {
if e.LogMsg != "" {
return e.LogMsg
}
return e.HTTPMsg
}

func (e authError) WriteHTTP(w http.ResponseWriter, r *http.Request) {
httputil.WriteJSON(w, r, e.HTTPStatus, errors.New(e.HTTPMsg))
}

func AuthError(err error) *authError {
authErr, ok := err.(*authError)
if !ok {
authErr = &authError{
Type: UnknownType,
HTTPStatus: http.StatusInternalServerError,
HTTPMsg: "unknown error",
LogMsg: err.Error(),
}
}
return authErr
}

func ErrUserAlreadyExists(username string) error {
return &authError{
Type: UserAlreadyExistsType,
HTTPStatus: http.StatusForbidden,
HTTPMsg: fmt.Sprintf("user of name '%s' already exists", username),
}
}

func ErrVerificationFailed(username string, userExists bool) error {
var logMsg string
if userExists {
logMsg = fmt.Sprintf("verification failed: invalid password provided for existing user '%s'", username)
} else {
logMsg = fmt.Sprintf("verification failed: no such user of username '%s'", username)
}
return &authError{
Type: VerificationFailedType,
HTTPStatus: http.StatusUnauthorized,
HTTPMsg: "username or password incorrect",
LogMsg: logMsg,
}
}

type UserEntry struct {
Name string `json:"name"`
PwSalt []byte `json:"pw_salt"`
PwHash cipher.SHA256 `json:"pw_hash"`
}

func (u *UserEntry) SetPassword(saltLen int, password string) {
u.PwSalt = cipher.RandByte(saltLen)
u.PwHash = cipher.SumSHA256(append([]byte(password), u.PwSalt...))
}

func (u *UserEntry) Verify(username, password string) error {
if u.Name != username {
return errors.New("invalid bbolt user entry: username does not match")
}
if cipher.SumSHA256(append([]byte(password), u.PwSalt...)) != u.PwHash {
return ErrVerificationFailed(username, true)
}
return nil
}

func (u *UserEntry) Encode() []byte {
raw, err := json.Marshal(u)
if err != nil {
panic(err) // TODO: log this.
}
return raw
}

func (u *UserEntry) Decode(username string, raw []byte) {
if err := json.Unmarshal(raw, u); err != nil {
panic(err) // TODO: log this.
}
}

type AuthStorer interface {
NewUser(username, password string) error
DeleteUser(username string) error
VerifyUser(username, password string) error
ChangePassword(username, password string) error

NewSession(username string) (*http.Cookie, error)
DeleteSession(value string) error
UserSessions(username string) ([]*http.Cookie, error)
}

type BoltAuthStore struct {
db *bbolt.DB
saltLen int
}

func NewBoltAuthStore(path string, saltLen int) (*BoltAuthStore, error) {
if err := os.MkdirAll(filepath.Dir(path), os.FileMode(0700)); err != nil {
return nil, err
}
db, err := bbolt.Open(path, os.FileMode(0600), &bbolt.Options{Timeout: boltTimeout})
if err != nil {
return nil, err
}
err = db.Update(func(tx *bbolt.Tx) error {
if _, err := tx.CreateBucketIfNotExists([]byte(boltUserBucketName)); err != nil {
return err
}
if _, err := tx.CreateBucketIfNotExists([]byte(boltSessionBucketName)); err != nil {
return err
}
return nil
})
return &BoltAuthStore{db: db, saltLen: saltLen}, err
}

func (s *BoltAuthStore) NewUser(username, password string) error {
return s.db.Update(func(tx *bbolt.Tx) error {
b := tx.Bucket([]byte(boltUserBucketName))
if len(b.Get([]byte(username))) > 0 {
return ErrUserAlreadyExists(username)
}
entry := UserEntry{Name: username}
entry.SetPassword(s.saltLen, password)
return b.Put([]byte(username), entry.Encode())
})
}

func (s *BoltAuthStore) DeleteUser(username string) error {
return s.db.Update(func(tx *bbolt.Tx) error {
return tx.
Bucket([]byte(boltUserBucketName)).
Delete([]byte(username))
})
}

func (s *BoltAuthStore) VerifyUser(username, password string) error {
return s.db.View(func(tx *bbolt.Tx) error {
raw := tx.
Bucket([]byte(boltUserBucketName)).
Get([]byte(username))
if len(raw) == 0 {
return ErrVerificationFailed(username, false)
}
var entry UserEntry
entry.Decode(username, raw)
return entry.Verify(username, password)
})
}

func (s *BoltAuthStore) ChangePassword(username, password string) error {
return s.db.Update(func(tx *bbolt.Tx) error {
b := tx.Bucket([]byte(boltUserBucketName))
raw := b.Get([]byte(username))
if len(raw) == 0 {
return ErrVerificationFailed(username, false) // TODO: Change
}
var entry UserEntry
entry.Decode(username, raw)
entry.SetPassword(s.saltLen, password)
return b.Put([]byte(boltUserBucketName), entry.Encode())
})
}
11 changes: 9 additions & 2 deletions pkg/manager/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,20 @@ func (m *Node) ServeHTTP(w http.ResponseWriter, r *http.Request) {
mux.Use(middleware.Timeout(time.Second * 30))
mux.Use(middleware.Logger)

mux.Mount("/api", m.apiRouter())
mux.Mount("/auth", m.authHandler())
mux.Mount("/api", m.apiHandler())

mux.ServeHTTP(w, r)
}

func (m *Node) apiRouter() http.Handler {
func (m *Node) authHandler() http.Handler {
r := chi.NewRouter()
return r
}

func (m *Node) apiHandler() http.Handler {
r := chi.NewRouter()
r.Use() // TODO: Complete!

r.Get("/nodes", m.getNodes())
r.Get("/nodes/{pk}", m.getNode())
Expand Down
Loading

0 comments on commit 5dcb7df

Please sign in to comment.