-
Notifications
You must be signed in to change notification settings - Fork 669
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
NOISSUE - Add user groups #1228
Conversation
@mteodor CI is failing. Please, use this issue as reference: https://github.com/mainflux/mainflux/issues/649 |
|
||
var cmdGroups = []cobra.Command{ | ||
cobra.Command{ | ||
Use: "create", |
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.
What is the full command? group create ...
? Would it not be confusing when we add other entities groups (things, channels)?
cmd/users/main.go
Outdated
groupName := c.adminGroup | ||
|
||
_, err := userRepo.RetrieveByEmail(context.Background(), user.Email, false) | ||
if err == 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.
RetrieveByEmail
actually fails when there is no user? I did not know that, I though it returns empty result...
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.
Also, I see below that you are using users.ErrNotFound
, so please be consistent.
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.
Also, inline the check
@@ -28,6 +28,7 @@ func TestCreateChannel(t *testing.T) { | |||
sdkConf := sdk.Config{ | |||
BaseURL: ts.URL, | |||
UsersPrefix: "", | |||
GroupsPrefix: "", |
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.
Group of what? Users? What about Things?
users/README.md
Outdated
@@ -31,6 +31,8 @@ default values. | |||
| MF_USERS_HTTP_PORT | Users service HTTP port | 8180 | | |||
| MF_USERS_SERVER_CERT | Path to server certificate in pem format | | | |||
| MF_USERS_SERVER_KEY | Path to server key in pem format | | | |||
| MF_USERS_ADMIN_EMAIL | Default user, created on startup | | |
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.
Please align text nicely
cmd/users/main.go
Outdated
@@ -292,10 +309,60 @@ func newService(db *sqlx.DB, tracer opentracing.Tracer, auth mainflux.AuthNServi | |||
Help: "Total duration of requests in microseconds.", | |||
}, []string{"method"}), | |||
) | |||
|
|||
if err := createAdmin(svc, userRepo, groupRepo, c); err != nil { | |||
logger.Error("failed to create admin user:" + err.Error()) |
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 you can use Wrap here. Otherwise add a white space after :
users/service.go
Outdated
return svc.groups.Save(ctx, group) | ||
} | ||
|
||
func (svc usersService) ListGroups(ctx context.Context, token string, offset, limit uint64, name string, meta Metadata) (GroupPage, error) { |
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.
Who can assign user to the group?
Who can remove the user from the group?
Who can delete a group?
I saw you have owner checking only in GET. What's the idea for other operations?
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.
access rights will be introduced with policies
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.
That makes sense. I was confused with the owner checking in RetrieveAll.
Maybe policies should handle this also, I mean if you have read in policies that mean that I can retrieve all groups where I am assigned. Not sure how this should work together, the thing to think about :)
users/postgres/users.go
Outdated
if _, err := ur.db.NamedExecContext(ctx, q, dbu); err != nil { | ||
return errors.Wrap(errUpdateUserDB, err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (ur userRepository) RetrieveByEmail(ctx context.Context, email string) (users.User, error) { | ||
func (ur userRepository) RetrieveByEmail(ctx context.Context, email string, groups bool) (users.User, error) { |
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 this group retrieves? Do we need this?
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.
Probably not, that was my initial idea to have a flag whether you want to retrieve all groups that user belongs along
but I made a methods RetrieveAllGroupsForUser
and RetrieveAllUsersForGroup
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 see. Yes, I also think that we don't need it now, its better to decouple.
users/service.go
Outdated
return svc.groups.Save(ctx, group) | ||
} | ||
|
||
func (svc usersService) ListGroups(ctx context.Context, token string, offset, limit uint64, name string, meta Metadata) (GroupPage, error) { |
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.
That makes sense. I was confused with the owner checking in RetrieveAll.
Maybe policies should handle this also, I mean if you have read in policies that mean that I can retrieve all groups where I am assigned. Not sure how this should work together, the thing to think about :)
cmd/users/main.go
Outdated
groupName := c.adminGroup | ||
|
||
_, err := userRepo.RetrieveByEmail(context.Background(), user.Email, false) | ||
if err == 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.
Also, inline the check
Codecov Report
@@ Coverage Diff @@
## master #1228 +/- ##
==========================================
- Coverage 74.36% 68.03% -6.33%
==========================================
Files 106 108 +2
Lines 6159 7136 +977
==========================================
+ Hits 4580 4855 +275
- Misses 1194 1850 +656
- Partials 385 431 +46
Continue to review full report at Codecov.
|
docker/docker-compose.yml
Outdated
@@ -103,6 +103,8 @@ services: | |||
POSTGRES_DB: ${MF_USERS_DB} | |||
networks: | |||
- mainflux-base-net | |||
ports: | |||
- "6001:5432" |
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 maybe use ENVARS here? It's hard to understand this values.
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.
this is mistake, will remove this, only for development purposes
users/users.go
Outdated
RetrieveByEmail(ctx context.Context, email string) (User, error) | ||
|
||
// RetrieveByID retrieves user by its unique identifier ID. | ||
// If groups = TRUE retrieve groups for given User |
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.
do we need this comment?
users/users.go
Outdated
|
||
// Update updates the user metadata. | ||
UpdateUser(ctx context.Context, u User) error | ||
|
||
// RetrieveByEmail retrieves user by its unique identifier (i.e. email). | ||
// If groups = TRUE retrieve groups for given User |
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.
do we need this comment?
users/service.go
Outdated
} | ||
|
||
return svc.users.RetrieveAllForGroup(ctx, groupID, offset, limit, um) | ||
|
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 blank line
users/service.go
Outdated
ErrUserAlreadyAssigned = errors.New("user is already assigned to a group") | ||
|
||
// ErrAssignUserToGroup indicates an error in assigning user to a group. | ||
ErrAssignUserToGroup = errors.New("error assigning user to a group") |
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.
Please, avoid error
in an error message. Use Failed to
instead.
users/postgres/users.go
Outdated
q := fmt.Sprintf(`SELECT u.id, u.email, u.metadata FROM users u, group_relations g | ||
WHERE u.id = g.user_id AND g.group_id = :group | ||
%s ORDER BY id LIMIT :limit OFFSET :offset;`, mq) |
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.
This doesn't look properly aligned
users/postgres/init.go
Outdated
email VARCHAR(254) PRIMARY KEY, | ||
password CHAR(60) NOT NULL |
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.
This doesn't look properly aligned
users/groups.go
Outdated
// AssignUser adds user to group. | ||
AssignUser(ctx context.Context, userID, groupID string) error | ||
|
||
// RemoveUser |
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.
Complete this comment
users/emailer/emailer.go
Outdated
@@ -24,6 +24,6 @@ func New(url string, c *email.Config) (users.Emailer, error) { | |||
|
|||
func (e *emailer) SendPasswordReset(To []string, host string, token string) error { | |||
url := fmt.Sprintf("%s%s?token=%s", host, e.resetURL, token) | |||
content := fmt.Sprintf("%s", url) | |||
content := url |
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.
Do you need content
anymore?
users/api/metrics.go
Outdated
return ms.svc.ListGroups(ctx, token, id, offset, limit, meta) | ||
} | ||
|
||
func (ms *metricsMiddleware) ListUsersInGroup(ctx context.Context, token, id string, offset, limit uint64, meta users.Metadata) (users.UserPage, error) { |
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.
This could be ListGroupUsers
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 ListUsersGroup
doesn't it sound more logical? Following work orders List
Users
Group
e.g List all users group. Detail ;)
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.
@nmarcetic I don't think so :) We want users that are in a group not the group belonging to users.
We can keep this naming to avoid confusions.
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.
Hm @manuio Yea, that makes more sense :)
cmd/users/main.go
Outdated
@@ -99,21 +107,26 @@ const ( | |||
envAuthnCACerts = "MF_AUTHN_CA_CERTS" | |||
envAuthnURL = "MF_AUTHN_GRPC_URL" | |||
envAuthnTimeout = "MF_AUTHN_GRPC_TIMEOUT" | |||
|
|||
defaultAdminGroupDescription = "Mainflux default admin group" |
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 we move this with other def
values?
pkg/sdk/go/groups.go
Outdated
return tp, nil | ||
} | ||
|
||
func (sdk mfSDK) UsersGroups(token string, offset, limit uint64, id string) (UsersGroupsPage, error) { |
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'm not sure what would fit better here, this method will list Groups, later we will introduce groups for things so we will have ThingsGroups, if I use User
instead of Users
may sound like list Groups for certain user
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.
@mteodor I was thinking that you will split user groups to users and thing groups to things. Not making pkg/sdk/go/groups
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.
it was clearer to me have groups separate as a separate entity, similar to the channels.
there is a possibility that we will actually reuse these groups for things as well, maybe just adding type to the group entity.
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.
Its something that we should agree, I know about this idea from ~ 1 year ago :) at least. It's still not documented and you started to work in freestyle mode, not really :) But you get the point.
@mteodor I think we need new issue like Generic groups and there discuss about this and get a clear vision. It will helps a lot if we brainstorm
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 agree. I'm not sure about the idea. However, if we decide to create generic groups, it starts to look like a new service, which I am skeptical about.
users/api/metrics.go
Outdated
return ms.svc.ListGroups(ctx, token, id, offset, limit, meta) | ||
} | ||
|
||
func (ms *metricsMiddleware) ListUsersInGroup(ctx context.Context, token, id string, offset, limit uint64, meta users.Metadata) (users.UserPage, error) { |
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.
@nmarcetic I don't think so :) We want users that are in a group not the group belonging to users.
We can keep this naming to avoid confusions.
pkg/sdk/go/groups.go
Outdated
return tp, nil | ||
} | ||
|
||
func (sdk mfSDK) UsersGroups(token string, offset, limit uint64, id string) (UsersGroupsPage, error) { |
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'm not sure what would fit better here, this method will list Groups, later we will introduce groups for things so we will have ThingsGroups, if I use User
instead of Users
may sound like list Groups for certain user
pkg/sdk/go/sdk.go
Outdated
Metadata map[string]interface{} `json:"metadata,omitempty"` | ||
ID string `json:"id,omitempty"` | ||
Email string `json:"email,omitempty"` | ||
UsersGroups []string `json:"groups,omitempty"` |
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 simply use Groups
or GroupsIDs
pkg/sdk/go/sdk.go
Outdated
AssignUserToGroup(userID, groupID, token string) error | ||
|
||
// RemoveUserFromGroup | ||
RemoveUserFromGroup(userID, groupID, token string) error | ||
|
||
// ListUsersForGroup | ||
ListUsersForGroup(groupID, token string, offset, limit uint64) (UsersPage, error) |
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.
This naming is not consistent with the rest. I would use CreateGroupUser
, RemoveGroupUser
and GroupUsers
pkg/sdk/go/groups.go
Outdated
} | ||
|
||
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 blank line please
pkg/sdk/go/groups.go
Outdated
endpoint := fmt.Sprintf("%s/%s/users?offset=%d&limit=%d&", groupsEndpoint, groupID, offset, limit) | ||
url := createURL(sdk.baseURL, sdk.groupsPrefix, endpoint) | ||
|
||
fmt.Println(url) |
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 log
users/postgres/groups.go
Outdated
} else { | ||
q = `INSERT INTO groups (name, description, id, owner_id, parent_id, metadata) VALUES (:name, :description, :id, :owner_id, :parent_id, :metadata) RETURNING id` | ||
} |
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.
Please avoid using else
.
users/postgres/groups.go
Outdated
if group.ID == "" || group.Name == "" || !groupRegexp.MatchString(group.Name) { | ||
return users.Group{}, 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.
This can be done on the service layer.
users/postgres/groups.go
Outdated
} | ||
|
||
func (gr groupRepository) Delete(ctx context.Context, groupID string) error { | ||
tx, err := gr.db.BeginTxx(context.Background(), 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.
Looks like this check introduces a lot of complexity only to check if a group is empty. Leaving this check to the user and removing it from the code would simplify it. If there is a need, we can add it later.
users/postgres/groups.go
Outdated
tx, err := gr.db.BeginTxx(context.Background(), nil) | ||
if err != nil { | ||
return errors.Wrap(errDeleteGroupDB, err) | ||
} | ||
defer func() { | ||
if err != nil { | ||
if txErr := tx.Rollback(); txErr != nil { | ||
err = errors.Wrap(err, errors.Wrap(errTransRollback, txErr)) | ||
} | ||
} | ||
|
||
if err = tx.Commit(); err != nil { | ||
err = errors.Wrap(errDeleteGroupDB, err) | ||
} | ||
}() | ||
|
||
q := `SELECT COUNT(*) FROM group_relations WHERE group_id = :group_id AND user_id = :user_id ;` | ||
dbr, err := toDBGroupRelation(userID, groupID) | ||
if err != nil { | ||
return errors.Wrap(users.ErrAssignUserToGroup, err) | ||
} | ||
tot, err := total(ctx, gr.db, q, dbr) | ||
if err != nil { | ||
return errors.Wrap(users.ErrAssignUserToGroup, err) | ||
} | ||
if tot > 0 { | ||
return errors.Wrap(users.ErrUserAlreadyAssigned, err) | ||
} |
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.
The same goes here. We can simply try to insert, and examine an error to decide what went wrong.
users/postgres/groups.go
Outdated
Group uuid.NullUUID `db:"group_id"` | ||
User uuid.NullUUID `db:"user_id"` |
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 NullID if it can't be null by the schema?
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.
CI is failing, please fix it.
return svc | ||
} | ||
|
||
func createAdmin(svc users.Service, userRepo users.UserRepository, groupRepo users.GroupRepository, c config) error { |
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 like how you avoid exposing a new method in the service interface, but use existing methods from repo and service layers.
pkg/sdk/go/groups.go
Outdated
return nil | ||
} | ||
|
||
func (sdk mfSDK) AddGroupUser(userID, groupID, token string) error { |
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.
Please consider renaming this, since it's not clear what this method does. Is it assigning a user to a group?
pkg/sdk/go/groups.go
Outdated
return nil | ||
} | ||
|
||
func (sdk mfSDK) RemoveGroupUser(userID, groupID, token string) error { |
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.
Same here.
pkg/sdk/go/groups.go
Outdated
return nil | ||
} | ||
|
||
func (sdk mfSDK) GroupUsers(groupID, token string, offset, limit uint64) (UsersPage, error) { |
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.
ListUsersByGroup
sounds less ambiguous (even though it does not sound well), since this method does not group users, but lists users that belong to the group.
pkg/sdk/go/groups.go
Outdated
return tp, nil | ||
} | ||
|
||
func (sdk mfSDK) UsersGroups(token string, offset, limit uint64, id string) (UsersGroupsPage, error) { |
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 agree. I'm not sure about the idea. However, if we decide to create generic groups, it starts to look like a new service, which I am skeptical about.
users/api/metrics.go
Outdated
|
||
func (ms *metricsMiddleware) ListUserGroups(ctx context.Context, token, id string, offset, limit uint64, meta users.Metadata) (users.GroupPage, error) { | ||
defer func(begin time.Time) { | ||
ms.counter.With("method", "list_groups_for_user").Add(1) |
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.
"list_user_groups"
users/groups.go
Outdated
AssignUser(ctx context.Context, userID, groupID string) error | ||
|
||
// RemoveUser removes user from group | ||
RemoveUser(ctx context.Context, userID, groupID string) error |
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.
Consider using unassing
as the opposite of assign
or add
as the opposite of remove
. I prefer assign/unassign
.
users/postgres/database.go
Outdated
// Database provides a database interface | ||
type Database interface { | ||
NamedExecContext(context.Context, string, interface{}) (sql.Result, error) | ||
QueryRowxContext(context.Context, string, ...interface{}) *sqlx.Row | ||
NamedQueryContext(context.Context, string, interface{}) (*sqlx.Rows, error) | ||
GetContext(context.Context, interface{}, string, ...interface{}) error | ||
BeginTxx(ctx context.Context, opts *sql.TxOptions) (*sqlx.Tx, error) |
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.
Since you removed the code that uses this method, you can remove it from the interface and implementation, as well.
users/users.go
Outdated
@@ -54,16 +61,22 @@ func (u User) Validate() error { | |||
type UserRepository interface { | |||
// Save persists the user account. A non-nil error is returned to indicate | |||
// operation failure. | |||
Save(ctx context.Context, u User) error | |||
Save(ctx context.Context, u User) (User, error) |
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 do we need to return the User? We usually only need an ID of the created entity.
cmd/cli/main.go
Outdated
@@ -20,6 +20,7 @@ func main() { | |||
CertsURL: "http://localhost:8204", | |||
ReaderPrefix: "", | |||
UsersPrefix: "", | |||
GroupsPrefix: "", |
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.
Please align
pkg/sdk/go/things_test.go
Outdated
@@ -65,6 +65,7 @@ func TestCreateThing(t *testing.T) { | |||
sdkConf := sdk.Config{ | |||
BaseURL: ts.URL, | |||
UsersPrefix: "", | |||
GroupsPrefix: "", |
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.
Needs to be aligned
pkg/sdk/go/things_test.go
Outdated
@@ -125,6 +126,7 @@ func TestCreateThings(t *testing.T) { | |||
sdkConf := sdk.Config{ | |||
BaseURL: ts.URL, | |||
UsersPrefix: "", | |||
GroupsPrefix: "", |
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.
Same here
pkg/sdk/go/users.go
Outdated
@@ -147,3 +156,33 @@ func (sdk mfSDK) UpdatePassword(oldPass, newPass, token string) error { | |||
|
|||
return nil | |||
} | |||
func (sdk mfSDK) UserGroups(userID, token string, offset, limit uint64) (GroupsPage, error) { |
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.
If you are calling everything Groups
in SDK, probably you slhould leave Groups()
here as well - especially that this is users.go
file.
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.
This is for listing groups for user, therefore it is UserGroups
users/api/logging.go
Outdated
return lm.svc.UnassignUserFromGroup(ctx, token, userID, groupID) | ||
} | ||
|
||
func (lm *loggingMiddleware) ListUserGroups(ctx context.Context, token, id string, offset, limit uint64, meta users.Metadata) (e users.GroupPage, err error) { |
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.
Shouldn't this be ListGroups()
, in order to be consistent?
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.
this lists groups that user is assigned to
users/tracing/groups.go
Outdated
} | ||
|
||
// GroupRepositoryMiddleware tracks request and their latency, and adds spans | ||
// to context. |
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.
Move to upper line - ty to keep all comments in one line if it is not longer than code below.
users/tracing/groups.go
Outdated
return grm.repo.RetrieveByName(ctx, name) | ||
} | ||
|
||
func (grm groupRepositoryMiddleware) RetrieveAll(ctx context.Context, groupID string, offset, limit uint64, gm users.Metadata) (users.GroupPage, error) { |
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.
Please break this line - it's too long
users/tracing/groups.go
Outdated
return grm.repo.RetrieveAll(ctx, groupID, offset, limit, gm) | ||
} | ||
|
||
func (grm groupRepositoryMiddleware) RetrieveAllForUser(ctx context.Context, userID string, offset, limit uint64, gm users.Metadata) (users.GroupPage, error) { |
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.
Please break this line
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.
Memberships()
?
users/tracing/users.go
Outdated
@@ -68,6 +75,14 @@ func (urm userRepositoryMiddleware) UpdatePassword(ctx context.Context, email, p | |||
return urm.repo.UpdatePassword(ctx, email, password) | |||
} | |||
|
|||
func (urm userRepositoryMiddleware) RetrieveAllForGroup(ctx context.Context, groupID string, offset, limit uint64, gm users.Metadata) (users.UserPage, error) { |
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.
Please break this line
removeUser = "remove_user_from_group" | ||
) | ||
|
||
var _ users.GroupRepository = (*groupRepositoryMiddleware)(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.
Is this Middleware
suffix aligned to what we used in other services? It makes variable name pretty long. Do we need it at all?
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.
similar to : _ things.ChannelRepository = (*channelRepositoryMiddleware)(nil)
cli/README.md
Outdated
```bash | ||
mainflux-cli groups get all <user_auth_token> | ||
``` | ||
### List children groups for some group |
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.
Looks like `shows group with provided group ID`` command is missing
cli/groups.go
Outdated
ParentID - ID of a group that is a parent | ||
to the creating group |
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.
Do we need 2 lines here?
cli/README.md
Outdated
``` | ||
### List groups user belongs to | ||
```bash | ||
mainflux-cli users groups <user_id> <user_auth_token> |
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.
This is a users or groups command?
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.
this is users command
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 it's defined with Group commands?
cli/groups.go
Outdated
cmd := cobra.Command{ | ||
Use: "groups", | ||
Short: "Groups management", | ||
Long: `Groups management: create accounts and tokens"`, |
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.
create accounts and tokens
?
cmd/users/main.go
Outdated
envAdminPassword = "MF_USERS_ADMIN_PASSWORD" | ||
envAdminGroup = "MF_USERS_ADMIN_GROUP" | ||
|
||
envJaegerURL = "MF_JAEGER_URL" |
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 envJaegerURL
is defined here?
Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>
Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>
Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>
Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>
@@ -147,3 +156,33 @@ func (sdk mfSDK) UpdatePassword(oldPass, newPass, token string) error { | |||
|
|||
return nil | |||
} | |||
func (sdk mfSDK) Memberships(userID, token string, offset, limit uint64) (GroupsPage, error) { |
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.
Please add an empty line before this/after the previous method.
users/api/endpoint.go
Outdated
req := request.(listUserGroupReq) | ||
if err := req.validate(); err != nil { | ||
return users.UserPage{}, err | ||
} | ||
|
||
up, err := svc.Members(ctx, req.token, req.groupID, req.offset, req.limit, req.metadata) | ||
if err != nil { | ||
return users.UserPage{}, err | ||
} | ||
return buildUsersResponse(up), 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.
Please be consistent when adding empty lines. I don't mind any combination of
request typ eassert
errcheck
<AN EMPTY LINE>
svc method call
errcheck
<AN EMTPY LINE>
prepare return
<AN EMPTY LINE>
return result
You can have all the empty lines, some of them, or none of them. As long as you're consistent, each option is OK for me. Please check all the other places in the code where this can be an issue.
users/api/requests.go
Outdated
const minPassLen = 8 | ||
|
||
type apiReq interface { | ||
validate() error | ||
} | ||
const maxNameSize = 1024 |
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.
Group:
const (
minPassLen = 8
maxNameLen = 1024
)
users/api/requests.go
Outdated
if req.token == "" { | ||
return users.ErrUnauthorizedAccess | ||
} | ||
|
||
if len(req.Name) > maxNameSize || req.Name == "" { | ||
return users.ErrMalformedEntity | ||
} | ||
|
||
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.
The same remark for empty lines in validate
methods.
users/api/transport.go
Outdated
@@ -87,6 +105,69 @@ func MakeHandler(svc users.Service, tracer opentracing.Tracer, l log.Logger) htt | |||
opts..., | |||
)) | |||
|
|||
mux.Post("/users/groups", kithttp.NewServer( |
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.
We probably don't need /users
prefix here. I assume that you had Things Groups in mind, but this can be resolved in LB or internal Nginx. After all, it's two services.
users/groups.go
Outdated
// Update updates the group data. | ||
Update(ctx context.Context, g Group) error | ||
|
||
// Delete deletes group for given id |
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.
Either add .
at the end of the comment or not. @drasko And I don't agree around this (I'm for it, he's not). Just be consistent, whatever you choose.
users/postgres/groups.go
Outdated
return group, nil | ||
} | ||
|
||
func (gr groupRepository) RetrieveAll(ctx context.Context, groupID string, offset, limit uint64, gm users.Metadata) (users.GroupPage, error) { |
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.
Consider renaming to RetrieveWithAncestors
or something like that.
items = append(items, gr) | ||
} | ||
|
||
cq := fmt.Sprintf(`WITH RECURSIVE subordinates AS ( |
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'm not sure if we need this. I prefer just adding a limit to the depth to some arbitrary number (10?) and return the list with no need for pagination and complications. What do you think? I doubt that we'll ever need more than 10-15 ancestors, and we don't need pagination for that.
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.
This implies that we'll need this COUNT to check the depth when adding a group.
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.
it works like a charm
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.
Do you want to keep it like this? I'm perfectly fine with it. @mainflux/maintainers Your opinion? It's ancestors pagination
VS ancestors depth limit
.
Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>
Use: "membership", | ||
Short: "membership <user_id> <user_auth_token>", |
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.
memberships
cobra.Command{ | ||
Use: "membership", | ||
Short: "membership <user_id> <user_auth_token>", | ||
Long: `List user groups membership`, |
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.
same here
Short: "Groups management", | ||
Long: `Groups management: create groups and assigns user to groups"`, | ||
Run: func(cmd *cobra.Command, args []string) { | ||
logUsage("Usage: Groups [create | get | delete | assign | unassign | members | membership]") |
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.
if you change previous one be careful here
} | ||
|
||
func toUser(dbu dbUser) users.User { | ||
func toUser(dbu dbUser) (users.User, error) { |
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 is this function called toUser()
? What does this to
prefix mean?
Similarly - why is toDBUser()
called like this?
Maybe there is a reason, but it is cryptic - if it is valid please put a comment above function names.
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.
OK, this: https://github.com/mainflux/mainflux/blob/master/things/postgres/things.go#L344 shows consistency, so I'll approve.
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
Congrats on this one. Thanks a lot @mteodor for great contribution! |
@mteodor In this impl, Is there something specific with this relation group <-> user (except owner ID)? This would be much simple in the future impl if you allow grouping of different entities. It will simplify access control a lot and general usage. |
there is associationt table |
Groups are usually used for access control and Authorization service looks like a good candidate if we decide to make groups generic. |
I agree - this should be generalized. |
Great! Let's think about design and refactor in the next phase. |
* adding group Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * adding user group Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * adding group Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * add groups Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * add groups Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * add retrieve methods Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * add default admin user Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * add default admin user Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * adding endpoints Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * adding endpoints Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * adding tests Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * changes signature for AssignUser Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * adding tests Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * bug fixing retrieving groups Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * remove unused code Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * bug fixing retrieving groups Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * retrieve groups Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * change environment for admin Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * change environment for admin Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * retrieve groups Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * remove adding default group Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * expose port for debugging purposes Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * fix tests, and linter errors Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * add prefix Users for groups endpoint Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * fix linter problems Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * fix endpoint prefix url Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * fix endpoint test Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * add group features in cli Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * remove comments Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * remove println Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * when user is created return id in response Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * when user is created return id in response Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * adding default admin env Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * proper alignment Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * proper alignment Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * fix comments Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * rename method Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * return user id when created Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * return user id when created Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * remove unused variable Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * rename methods Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * fix to retrieve whole tree starting from parent Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * add endpoint to list groups for user Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * add readme for groups Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * fixing bugs Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * fixing bugs Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * add group commands for add and remove user Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * replace default email, use example.com Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * fix capital letters beginning of sentence Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * remove warning for deprecated api, mistakenly copied Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * simplify repo methods, rely on db driver rather than the check before operation Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * check if group is valid Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * openapi spec 3.0 Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * remove check for existing users in groups before delete Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * renaming methods Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * renaming methods Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * renaming methods Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * change func signature Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * change func signature Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * fix bugs, resolve comments Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * fix bugs, resolve comments Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * fix alignment Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * add missing command Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * reorganize envs Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * fix doc Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * fix compile Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * reorganize cli commands Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * minor corrections Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * renaming Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * renaming Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * renaming Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * rename methods Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * fix naming Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * renaming Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * renaming Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * resolve comments, minor changes Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>
What does this do?
Introducing groups for user, this is related to https://github.com/mainflux/mainflux/issues/649
List any changes that modify/break current functionality
None
Have you included tests for your changes?
Yes