-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
warden/groups: add a PUT method to overwrite members & some refactoring #768
Conversation
There was no way to overwrite all group members meaning just sending the actual value, instead one had to keep track of changes or remove all members to add the new ones again. Now a http PUT method allows to set the members array.
not quite ready to merge, just for review if I am doing it right |
|
||
if err := json.NewDecoder(r.Body).Decode(&g); err != nil { | ||
h.H.WriteError(w, r, errors.WithStack(err)) | ||
if err := h.checkRequest(r, h.PrefixResource(GroupsResource), "create"); 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.
isn't it better to always first check the request?
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.
Yes, maybe I was using g
here for some reason which was why it was decoded first, but as it stands now the order can be reversed´the way you did it.
if err := h.Manager.RemoveGroupMembers(id, m.Members); err != nil { | ||
w.WriteHeader(http.StatusNoContent) | ||
} | ||
|
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.
missing comment
warden/group/manager_sql.go
Outdated
return nil | ||
} | ||
|
||
func (m *SQLManager) applyInTransaction(requests ...func(tx *sqlx.Tx) error) 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.
check if rollbacks are correct
shouldn't this function (and the next one) be non capital because it is not checking the request at all, it is just a helper right? |
I'm not sure right now, I think they need to be upper case for swagger docs to work. I'd have to check, otherwise all of the handlers can be lower case. |
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 pretty solid so far!
warden/group/handler.go
Outdated
w.WriteHeader(http.StatusNoContent) | ||
} | ||
|
||
func (h *Handler) OverwriteGroupMembers(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { |
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 probably rename it to "UpdateGroup"
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 think I'm going for UpdateGroupMembers
because the http PUT url is GroupsHandlerPath + "/:id/members"
so only one group is affected, not all of them
|
||
if err := json.NewDecoder(r.Body).Decode(&g); err != nil { | ||
h.H.WriteError(w, r, errors.WithStack(err)) | ||
if err := h.checkRequest(r, h.PrefixResource(GroupsResource), "create"); 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.
Yes, maybe I was using g
here for some reason which was why it was decoded first, but as it stands now the order can be reversed´the way you did it.
warden/group/manager_sql.go
Outdated
return nil | ||
} | ||
|
||
func (m *SQLManager) applyInTransaction(requests ...func(tx *sqlx.Tx) error) 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.
requests
are really transactions
, right?
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.
A transaction is a sequence of requests I would rather say.
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.
How about executors
?
warden/group/manager_sql.go
Outdated
|
||
for _, req := range requests { | ||
if err := req(tx); err != nil { | ||
tx.Rollback() |
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 rollback fails, the error should be returned instead
if err := tx.Rollback(); err != nil {
return err
}
warden/group/manager_sql.go
Outdated
@@ -183,3 +187,33 @@ func (m *SQLManager) ListGroups(limit, offset int) ([]Group, error) { | |||
|
|||
return groups, nil | |||
} | |||
|
|||
func (m *SQLManager) OverwriteGroupMembers(group string, members []string) error { | |||
if err := m.applyInTransaction(m.deleteGroup(group), m.createGroup(group), m.addGroupMembers(group, members)); 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.
this can be simplified to return m.ApplyIn...
warden/group/manager_sql.go
Outdated
return func(tx *sqlx.Tx) error { | ||
for _, subject := range subjects { | ||
if _, err := tx.Exec(m.DB.Rebind("DELETE FROM hydra_warden_group_member WHERE member=? AND group_id=?"), subject, group); err != nil { | ||
if err := tx.Rollback(); 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.
You have duplicate rollbacks here, let applyInTransaction
handle rollbacks instead!
warden/group/manager_sql.go
Outdated
return func(tx *sqlx.Tx) error { | ||
for _, subject := range subjects { | ||
if _, err := tx.Exec(m.DB.Rebind("INSERT INTO hydra_warden_group_member (group_id, member) VALUES (?, ?)"), group, subject); err != nil { | ||
if err := tx.Rollback(); 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.
Rollback is already covered by applyInTransaction
warden/group/manager_sql.go
Outdated
func (m *SQLManager) createGroup(group string) func(tx *sqlx.Tx) error { | ||
return func(tx *sqlx.Tx) error { | ||
if _, err := tx.Exec(m.DB.Rebind("INSERT INTO hydra_warden_group (id) VALUES (?)"), group); err != nil { | ||
return 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.
Normal errors do not have stack traces in go, to add a stack trace do return errors.WithStack(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.
Oh now I got it, I thought that errors.WithStack(err)
just has to be called once to get the whole stacktrace.
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.
Exactly, and it also needs to be called at the location which is the deepest in the stack trace and there only once - otherwise you'll overwrite the stack trace. This is a bit tricky unfortunately. The rule is that we wrap all errors coming from other libraries but don't wrap errors coming from our own. That way we ensure that traces are always as deep as possible in the stack.
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.
So this should be actually without a stacktrace?
warden/group/manager_sql.go
Outdated
func (m *SQLManager) deleteGroup(id string) func(tx *sqlx.Tx) error { | ||
return func(tx *sqlx.Tx) error { | ||
if _, err := tx.Exec(m.DB.Rebind("DELETE FROM hydra_warden_group WHERE id=?"), id); err != nil { | ||
return 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.
Normal errors do not have stack traces in go, to add a stack trace do return errors.WithStack(err)
warden/group/manager_sql.go
Outdated
if err := tx.Rollback(); err != nil { | ||
return err | ||
} | ||
return 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.
Normal errors do not have stack traces in go, to add a stack trace do return errors.WithStack(err)
ready for review @arekkas |
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.
Totally forgot about this one, have some questions. I'll check out the SQL implementation in more detail because the changes are a bit complex.
// oauth2: hydra.warden.groups | ||
// | ||
// Responses: | ||
// 204: emptyResponse |
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.
PUT
requests typically don't return 204 responses but instead 200 + body. 204 is typically used for DELETE
responses to aknowledge that the resource is gone
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 may not have changed everything correctly when copying the comments
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's also in the response, see w.WriteHeader(http.StatusNoContent)
return func(tx *sqlx.Tx) error { | ||
_, err := tx.Exec(m.DB.Rebind("INSERT INTO hydra_warden_group (id) VALUES (?)"), group) | ||
|
||
return errors.WithStack(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.
Not sure if this works for nil values? So if error is nil, does this return nil as well or not?
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.
https://godoc.org/github.com/pkg/errors#WithStack
I looked it up it works as expected
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.
Absolutely, it returns nil in that case: https://github.com/pkg/errors/blob/master/errors.go#L145
This is now tracked as ory/keto#10 |
* transfer UpdateRoleMembers from ory/hydra#768 to keto * fix tests by using right http method & correcting sql request * Change behavior to overwrite the whole role instead of just the members. + small sql migration fix
There was no way to overwrite all group members meaning just sending the actual value, instead one had to keep track of changes or remove all members to add the new ones again.
Now a http PUT method allows to set the members array.
closes #745