-
Notifications
You must be signed in to change notification settings - Fork 672
MF-1317 - Configurable regexp rule for password #1355
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
"validate user with empty password": { | ||
user: users.User{ | ||
Email: email, | ||
Password: "", | ||
}, | ||
err: users.ErrMalformedEntity, | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a reason to remove this test?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed because password validation is removed from users/api/requests.go
Codecov Report
@@ Coverage Diff @@
## master #1355 +/- ##
==========================================
- Coverage 60.55% 60.53% -0.03%
==========================================
Files 123 123
Lines 9411 9416 +5
==========================================
+ Hits 5699 5700 +1
- Misses 3219 3221 +2
- Partials 493 495 +2
Continue to review full report at Codecov.
|
cmd/users/main.go
Outdated
@@ -108,6 +111,8 @@ const ( | |||
envAuthTimeout = "MF_AUTH_GRPC_TIMEOUT" | |||
) | |||
|
|||
var passRegex = regexp.MustCompile(mainflux.Env(envPassRegex, defPassRegex)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Question: This will panic if not compile, is that expected behavior? Or we should use regexp.Compile
and check for errors?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In my opinion nothing should fail without being logged and log sent remotely. But this is main()
, so I do not know if this should be tolerated, as you will be probably looking the logs directly in console when deploying. However, I can not guarantee that, as there can be automated deployments (even without a console), and logs should be watched remotely.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have used MustCompile
because I saw it's used on other places:
https://github.com/mainflux/mainflux/blob/master/users/users.go#L26-L28
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@blokovi I would use Compile
instead of MustCompile
and check the errors: https://golang.org/pkg/regexp/#Compile
You can maybe also fix it in users service in this PR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd do something like that
logger, err := logger.New(os.Stdout, cfg.logLevel)
if err != nil {
log.Fatalf(err.Error())
}
passRegex, err := regexp.Compile(mainflux.Env(envPassRegex, defPassRegex))
if err != nil {
passRegex, err := regexp.MustCompile(defPassRegex)
}
@@ -24,6 +25,10 @@ const ( | |||
invalidEmail = "userexample.com" | |||
) | |||
|
|||
var ( | |||
passRegex = regexp.MustCompile("^.{8,}$") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would rather export defPassRegex
from main if you need it in multiple places.
Or set a new exported constant which will be used in other files, if you want to keep consistency (no exported def values) and then bind defPassRegex = new constant
users/api/requests.go
Outdated
@@ -17,6 +16,9 @@ type userReq struct { | |||
} | |||
|
|||
func (req userReq) validate() error { | |||
if !passRegex.MatchString(req.user.Password) { | |||
return users.ErrMalformedEntity |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe the error should state exactly what the problem is password doesn't match policy
, something like that
@@ -47,10 +46,6 @@ func (u User) Validate() error { | |||
return ErrMalformedEntity | |||
} | |||
|
|||
if len(u.Password) < minPassLen { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why we dont check password here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
two reasons:
- we already check it on other places (validating requests for registering and password changing)
- can't import passRegex from api package here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@blokovi I would check it here as it was done before and simply do:
func (req userReq) validate() error {
return req.user.Validate()
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@manuio can't use passRegex
from api package here in users (because api imports users)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@blokovi first remove it from the api layer :)
users/api/endpoint_test.go
Outdated
invalidData := toJSON(users.User{Email: invalidEmail, Password: "password"}) | ||
invalidPasswordData := toJSON(users.User{Email: "user@example.com", Password: "pass"}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you create consts for validEmail
, validPass
and invalidPass
please
@@ -47,10 +46,6 @@ func (u User) Validate() error { | |||
return ErrMalformedEntity | |||
} | |||
|
|||
if len(u.Password) < minPassLen { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@blokovi I would check it here as it was done before and simply do:
func (req userReq) validate() error {
return req.user.Validate()
}
cmd/users/main.go
Outdated
@@ -108,6 +111,8 @@ const ( | |||
envAuthTimeout = "MF_AUTH_GRPC_TIMEOUT" | |||
) | |||
|
|||
var passRegex = regexp.MustCompile(mainflux.Env(envPassRegex, defPassRegex)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In my opinion nothing should fail without being logged and log sent remotely. But this is main()
, so I do not know if this should be tolerated, as you will be probably looking the logs directly in console when deploying. However, I can not guarantee that, as there can be automated deployments (even without a console), and logs should be watched remotely.
cmd/users/main.go
Outdated
@@ -111,7 +111,7 @@ const ( | |||
envAuthTimeout = "MF_AUTH_GRPC_TIMEOUT" | |||
) | |||
|
|||
var passRegex = regexp.MustCompile(mainflux.Env(envPassRegex, defPassRegex)) | |||
// var passRegex = regexp.MustCompile(mainflux.Env(envPassRegex, defPassRegex)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove this please
users/service.go
Outdated
return &usersService{ | ||
users: users, | ||
groups: groups, | ||
hasher: hasher, | ||
auth: auth, | ||
email: m, | ||
idProvider: idp, | ||
// passRegex: passRegex, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Uncomment and use this one
users/service.go
Outdated
} | ||
|
||
// New instantiates the users service implementation | ||
func New(users UserRepository, groups GroupRepository, hasher Hasher, auth mainflux.AuthServiceClient, m Emailer, idp mainflux.IDProvider) Service { | ||
func New(users UserRepository, groups GroupRepository, hasher Hasher, auth mainflux.AuthServiceClient, m Emailer, idp mainflux.IDProvider, passRegex *regexp.Regexp) Service { | ||
PassRegex = passRegex |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove this one
users/api/requests.go
Outdated
// if !passRegex.MatchString(req.user.Password) { | ||
// return users.ErrPasswordPolicy | ||
// } | ||
return req.user.Validate() | ||
// if err := req.user.Validate(); err != nil { | ||
// return err | ||
// } | ||
// if !passRegex.MatchString(req.user.Password) { | ||
// return users.ErrPasswordPolicy | ||
// } | ||
// return nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove this comments please
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While I see why we added password regex as a field to the Users service, I find it a bit cleaner to use a package-level variable in the api
package because we offload data validation to the transport layer (that's what we usually do). While package-level variables are generally not a good idea, this one is 1) safe for concurrent use and 2) set only on call of MakeHandler
and never changed (i.e. does not hold the state and is easy to test and mock).
cmd/users/main.go
Outdated
if err != nil { | ||
passRegex = regexp.MustCompile(defPassRegex) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to fail silently. We can do it similar to the other env var handling:
passRegex, err := regexp.Compile(mainflux.Env(envPassRegex, defPassRegex))
if err != nil {
log.Fatalf("Invalid password validation rules %s\n", envPassRegex)
}
users/service.go
Outdated
@@ -66,6 +66,9 @@ var ( | |||
|
|||
// ErrAssignUserToGroup indicates an error in assigning user to a group. | |||
ErrAssignUserToGroup = errors.New("failed assigning user to a group") | |||
|
|||
// ErrPasswordPolicy indicates weak password. | |||
ErrPasswordPolicy = errors.New("password doesn't match policy") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using Policy
terminology can be confusing; maybe ErrPasswordFormat
.
@@ -302,6 +310,9 @@ func (svc usersService) ChangePassword(ctx context.Context, authToken, password, | |||
if err != nil { | |||
return errors.Wrap(ErrUnauthorizedAccess, err) | |||
} | |||
if !svc.passRegex.MatchString(password) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we validate password format in the ResetPassword
metod?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Definitely
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
83c60e8
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Signed-off-by: Ivan Milosevic <iva@blokovi.com>
Signed-off-by: Ivan Milosevic <iva@blokovi.com>
Signed-off-by: Ivan Milosevic <iva@blokovi.com>
Signed-off-by: Ivan Milosevic <iva@blokovi.com>
Signed-off-by: Ivan Milosevic <iva@blokovi.com>
Signed-off-by: Ivan Milosevic <iva@blokovi.com>
Signed-off-by: Ivan Milosevic <iva@blokovi.com>
Signed-off-by: Ivan Milosevic <iva@blokovi.com>
Signed-off-by: Ivan Milosevic <iva@blokovi.com>
… var Signed-off-by: Ivan Milosevic <iva@blokovi.com>
Signed-off-by: Ivan Milosevic <iva@blokovi.com>
Signed-off-by: Ivan Milosevic <iva@blokovi.com>
Signed-off-by: Ivan Milosevic <iva@blokovi.com>
Signed-off-by: Ivan Milosevic <iva@blokovi.com>
Signed-off-by: Ivan Milosevic <iva@blokovi.com>
Signed-off-by: Ivan Milosevic <iva@blokovi.com>
Signed-off-by: Ivan Milosevic <iva@blokovi.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
* read and validate regex envar Signed-off-by: Ivan Milosevic <iva@blokovi.com> * pass regexp to user/api Signed-off-by: Ivan Milosevic <iva@blokovi.com> * resolve conflicts Signed-off-by: Ivan Milosevic <iva@blokovi.com> * use exported regexp variable Signed-off-by: Ivan Milosevic <iva@blokovi.com> * move password validation from users package Signed-off-by: Ivan Milosevic <iva@blokovi.com> * remove dead code Signed-off-by: Ivan Milosevic <iva@blokovi.com> * add password change request Signed-off-by: Ivan Milosevic <iva@blokovi.com> * move regexp from api to users package Signed-off-by: Ivan Milosevic <iva@blokovi.com> * fix tests Signed-off-by: Ivan Milosevic <iva@blokovi.com> * remove commented code Signed-off-by: Ivan Milosevic <iva@blokovi.com> * add regexp as field in userService, remove it as user exported global var Signed-off-by: Ivan Milosevic <iva@blokovi.com> * add passwd validation in service Signed-off-by: Ivan Milosevic <iva@blokovi.com> * Add psswd validation for change password in service Signed-off-by: Ivan Milosevic <iva@blokovi.com> * add password validation in password reset Signed-off-by: Ivan Milosevic <iva@blokovi.com> * Remove password validation from user validation test Signed-off-by: Ivan Milosevic <iva@blokovi.com> * Replace email and passwords in test with constants Signed-off-by: Ivan Milosevic <iva@blokovi.com> * compile error not fail silently Signed-off-by: Ivan Milosevic <iva@blokovi.com> * fix tempate path Signed-off-by: Ivan Milosevic <iva@blokovi.com>
Signed-off-by: Ivan Milosevic iva@blokovi.com
Which issue(s) does this PR fix/relate to?
Resolves #1317
List any changes that modify/break current functionality
Have you included tests for your changes?
Did you document any new/modified functionality?
Notes