diff --git a/.github/workflows/postman-tests.yml b/.github/workflows/postman-tests.yml index 462d8248..e5c52724 100644 --- a/.github/workflows/postman-tests.yml +++ b/.github/workflows/postman-tests.yml @@ -41,7 +41,7 @@ jobs: npm install - name: Setup - run: echo "APIKEY=$(./estuary setup --username admin --password password | sed -e's/.*EST/EST/g' | tail -n1)" >> $GITHUB_ENV + run: echo "APIKEY=$(./estuary setup --username admin --password password1 | sed -e's/.*EST/EST/g' | tail -n1)" >> $GITHUB_ENV - name: Run Estuary run: | diff --git a/README.md b/README.md index 58f055d6..072dbefe 100644 --- a/README.md +++ b/README.md @@ -25,8 +25,7 @@ To run locally in a 'dev' environment, first run: ./estuary setup --username= --password= ``` -Save the auth token that this outputs, you will need it for interacting with -and controlling the node. This username and password won't work to log in using the front end (estuary-www), but the auth token will. +Save the credentials you use here, you will need them to login to the estuary-www frontend. NOTE: if you want to use a different database than a sqlite instance stored in your local directory, you will need to configure that with the `--database` flag, like so: `./estuary setup --username= --password= --database=XXXXX` diff --git a/api/v1/handlers.go b/api/v1/handlers.go index 533099f2..316796d7 100644 --- a/api/v1/handlers.go +++ b/api/v1/handlers.go @@ -2673,7 +2673,7 @@ func (s *apiV1) handleRegisterUser(c echo.Context) error { } } - authToken, err := s.newAuthTokenForUser(newUser, time.Now().Add(constants.TokenExpiryDurationRegister), nil, TOKEN_LABEL_ON_REGISTER) + authToken, err := s.newAuthTokenForUser(newUser, time.Now().Add(constants.TokenExpiryDurationRegister), nil, TOKEN_LABEL_ON_REGISTER, true) if err != nil { return err } @@ -2740,7 +2740,7 @@ func (s *apiV1) handleLoginUser(c echo.Context) error { } } - authToken, err := s.newAuthTokenForUser(&user, time.Now().Add(constants.TokenExpiryDurationLogin), nil, TOKEN_LABEL_ON_LOGIN) + authToken, err := s.newAuthTokenForUser(&user, time.Now().Add(constants.TokenExpiryDurationLogin), nil, TOKEN_LABEL_ON_LOGIN, true) if err != nil { return err } @@ -2828,7 +2828,7 @@ func (s *apiV1) handleGetUserStats(c echo.Context, u *util.User) error { return c.JSON(http.StatusOK, stats) } -func (s *apiV1) newAuthTokenForUser(user *util.User, expiry time.Time, perms []string, label string) (*util.AuthToken, error) { +func (s *apiV1) newAuthTokenForUser(user *util.User, expiry time.Time, perms []string, label string, isSession bool) (*util.AuthToken, error) { if len(perms) > 1 { return nil, fmt.Errorf("invalid perms") } @@ -2853,6 +2853,7 @@ func (s *apiV1) newAuthTokenForUser(user *util.User, expiry time.Time, perms []s User: user.ID, Expiry: expiry, UploadOnly: uploadOnly, + IsSession: isSession, } if err := s.DB.Create(authToken).Error; err != nil { return nil, err @@ -2930,6 +2931,7 @@ type getApiKeysResp struct { TokenHash string `json:"tokenHash"` Label string `json:"label"` Expiry time.Time `json:"expiry"` + IsSession bool `json:"isSession"` } // handleUserRevokeApiKey godoc @@ -2986,7 +2988,7 @@ func (s *apiV1) handleUserCreateApiKey(c echo.Context, u *util.User) error { label := c.QueryParam("label") - authToken, err := s.newAuthTokenForUser(u, expiry, perms, label) + authToken, err := s.newAuthTokenForUser(u, expiry, perms, label, false) if err != nil { return err } @@ -2996,6 +2998,7 @@ func (s *apiV1) handleUserCreateApiKey(c echo.Context, u *util.User) error { TokenHash: authToken.TokenHash, Label: authToken.Label, Expiry: authToken.Expiry, + IsSession: authToken.IsSession, }) } @@ -3022,6 +3025,7 @@ func (s *apiV1) handleUserGetApiKeys(c echo.Context, u *util.User) error { TokenHash: k.TokenHash, Label: k.Label, Expiry: k.Expiry, + IsSession: k.IsSession, }) } diff --git a/constants/constants.go b/constants/constants.go index 5f10bb9f..b7323415 100644 --- a/constants/constants.go +++ b/constants/constants.go @@ -1,6 +1,7 @@ package constants import ( + "regexp" "time" "github.com/filecoin-project/go-state-types/abi" @@ -40,6 +41,32 @@ const TokenExpiryDurationLogin = time.Hour * 24 * 30 // 30 days const TokenExpiryDurationDefault = time.Hour * 24 * 30 // 30 days const TokenExpiryDurationPermanent = time.Hour * 24 * 365 * 100 // 100 years +var AdminUsernameAlphanumericRegex = regexp.MustCompile(`^[A-Za-z\d]{1,32}$`) + +func IsAdminUsernameValid(username string) bool { + return AdminUsernameAlphanumericRegex.MatchString(username) +} + +var AdminPasswordLengthAndAlphanumericRegex = regexp.MustCompile(`^[A-Za-z\d]{8,}$`) +var AdminPasswordContainsAlphaRegex = regexp.MustCompile(`[A-Za-z]`) +var AdminPasswordContainsNumericRegex = regexp.MustCompile(`\d`) + +var AdminPasswordRegexes = []*regexp.Regexp{ + AdminPasswordLengthAndAlphanumericRegex, + AdminPasswordContainsAlphaRegex, + AdminPasswordContainsNumericRegex, +} + +func IsAdminPasswordValid(password string) bool { + for _, regex := range AdminPasswordRegexes { + ok := regex.MatchString(password) + if !ok { + return false + } + } + return true +} + var DealMaxPrice abi.TokenAmount var VerifiedDealMaxPrice = abi.NewTokenAmount(0) diff --git a/docs/docs.go b/docs/docs.go index c6fc4bea..0abff7d6 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -3403,6 +3403,9 @@ const docTemplate = `{ "expiry": { "type": "string" }, + "isSession": { + "type": "boolean" + }, "label": { "type": "string" }, diff --git a/docs/swagger.json b/docs/swagger.json index ffa68a26..3d18dc80 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -3396,6 +3396,9 @@ "expiry": { "type": "string" }, + "isSession": { + "type": "boolean" + }, "label": { "type": "string" }, diff --git a/docs/swagger.yaml b/docs/swagger.yaml index dc8fbb87..bd3c98d8 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -52,6 +52,8 @@ definitions: properties: expiry: type: string + isSession: + type: boolean label: type: string token: diff --git a/main.go b/main.go index 840ed4d5..268d4c87 100644 --- a/main.go +++ b/main.go @@ -523,11 +523,21 @@ func main() { return errors.New("setup username cannot be empty") } + ok := constants.IsAdminUsernameValid(username) + if !ok { + return errors.New("username must be alphanumeric and 1-32 characters") + } + password := cctx.String("password") if password == "" { return errors.New("setup password cannot be empty") } + ok = constants.IsAdminPasswordValid(password) + if !ok { + return errors.New("password must be at least eight characters and contain at least one letter and one number") + } + db, err := setupDatabase(cfg.DatabaseConnString) if err != nil { return err diff --git a/util/users.go b/util/users.go index b171f024..7d1372c4 100644 --- a/util/users.go +++ b/util/users.go @@ -36,6 +36,7 @@ type AuthToken struct { User uint UploadOnly bool Expiry time.Time + IsSession bool } type InviteCode struct {