Skip to content
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

Get permission service: clean "can_request_help" usage #1006

Merged
merged 18 commits into from
Sep 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
c5fced9
ViewPermissions: add can_request_help_to array fields to computed and…
GeoffreyHuck Sep 6, 2023
38c6426
Gherkin item permissions: Support source_group_id and origin
GeoffreyHuck Sep 6, 2023
6efa6b3
Gherkin item permissions: Create the group can_request_help_to in dat…
GeoffreyHuck Sep 7, 2023
dfe77ed
Gherkin comparison: Handle nil values
GeoffreyHuck Sep 8, 2023
b08cb57
viewPermissions: add array of can_request_help_to in $.[*].permission…
GeoffreyHuck Sep 8, 2023
33c6e60
viewPermission: computed now contain the aggregation of the other can…
GeoffreyHuck Sep 8, 2023
e476970
viewPermissions: add computed can_request_help_to groups
GeoffreyHuck Sep 8, 2023
2978654
Gherkin item relation: Add a definition to add item relations with pr…
GeoffreyHuck Sep 8, 2023
9389a66
viewPermission: Add can_request_help_to from parent items in computed…
GeoffreyHuck Sep 8, 2023
1deed0a
viewPermission: Refactor get can_request_help_to group with the other…
GeoffreyHuck Sep 11, 2023
17a052a
Refactor jsonPathResultMatchesValue because of lint cyclomatic comple…
GeoffreyHuck Sep 11, 2023
b35581b
viewPermission: Refactor the computation of can_request_help_to groups
GeoffreyHuck Sep 11, 2023
2da8b63
Gherkin: Uniformize checks of JSONPath responses with <null>, []
GeoffreyHuck Sep 12, 2023
3e48b77
viewPermission: remove can_request_help_to.name when it is not visible
GeoffreyHuck Sep 12, 2023
7eded6d
Gherkin: test undefined paths with <undefined> instead of having a fe…
GeoffreyHuck Sep 12, 2023
79d5e77
Gherkin: Add feature to check the result of a JSONPath as JSON
GeoffreyHuck Sep 13, 2023
960d83b
viewPermission: test $.granted.can_request_help_to with the JSON content
GeoffreyHuck Sep 13, 2023
dd8741a
Gherkin: The feature to check an array at a JSONPath now enforces tha…
GeoffreyHuck Sep 13, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions app/api/groups/get_participant_progress.feature
Original file line number Diff line number Diff line change
Expand Up @@ -574,11 +574,11 @@ Feature: Display the current progress of a participant on children of an item (g
When I send a GET request to "/items/1020/participant-progress?as_team_id=14"
Then the response code should be 200
And the response at $.item.item_id should be "1020"
And the response should not be defined at $.children
And the response at $.children should be "<undefined>"

Scenario: Should not return the children when the current user doesn't have a started result on the requested item with watched_group_id
Given I am the user with id "22"
When I send a GET request to "/items/210/participant-progress?watched_group_id=67"
Then the response code should be 200
And the response at $.item.item_id should be "210"
And the response should not be defined at $.children
And the response at $.children should be "<undefined>"
210 changes: 140 additions & 70 deletions app/api/groups/get_permissions.feature

Large diffs are not rendered by default.

318 changes: 232 additions & 86 deletions app/api/groups/get_permissions.go

Large diffs are not rendered by default.

182 changes: 167 additions & 15 deletions app/api/groups/get_permissions_can_request_help_to.feature

Large diffs are not rendered by default.

45 changes: 36 additions & 9 deletions app/api/threads/list_threads.feature
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ Feature: List threads
| -latest_update_at | 2 | 2 | 0 | @TaskMaxUpdateAt |
| -latest_update_at | 2 | 2 | 1 | @TaskSecondMaxUpdateAt |

Scenario Outline: Should support pagination parameters
Scenario: Should support pagination parameters with results
Given I am @John
And there are the following items:
| item | type |
Expand All @@ -268,14 +268,25 @@ Feature: List threads
| @John | @TaskMinUpdateAt | 1 | 2023-01-01 00:00:01 |
| @John | @TaskMaxUpdateAt | 1 | 2023-01-01 00:00:02 |
And I am @John
When I send a GET request to "/threads?is_mine=1&limit=1&sort=latest_update_at&from.item_id=<from.item_id>&from.participant_id=<from.participant_id>"
When I send a GET request to "/threads?is_mine=1&limit=1&sort=latest_update_at&from.item_id=@TaskMinUpdateAt&from.participant_id=@John"
Then the response code should be 200
And the response should be a JSON array with <nb_results> entries
And the response at $[0].item.id should be "<result_item>"
Examples:
| from.item_id | from.participant_id | nb_results | result_item |
| @TaskMinUpdateAt | @John | 1 | @TaskMaxUpdateAt |
| @TaskMaxUpdateAt | @John | 0 | |
And the response should be a JSON array with 1 entries
And the response at $[0].item.id should be "@TaskMaxUpdateAt"

Scenario: Should support pagination parameters with no results
Given I am @John
And there are the following items:
| item | type |
| @TaskMinUpdateAt | Task |
| @TaskMaxUpdateAt | Task |
And there are the following threads:
| participant | item | visible_by_participant | latest_update_at |
| @John | @TaskMinUpdateAt | 1 | 2023-01-01 00:00:01 |
| @John | @TaskMaxUpdateAt | 1 | 2023-01-01 00:00:02 |
And I am @John
When I send a GET request to "/threads?is_mine=1&limit=1&sort=latest_update_at&from.item_id=@TaskMaxUpdateAt&from.participant_id=@John"
Then the response code should be 200
And the response should be a JSON array with 0 entries

Scenario Outline: Should filter by status if parameter status is given
Given I am @John
Expand Down Expand Up @@ -320,4 +331,20 @@ Feature: List threads
| latest_update_gt | first_result_item | nb_results |
| 2023-01-01T00:00:00Z | @Task1 | 3 |
| 2023-01-01T00:00:02Z | @Task3 | 1 |
| 2023-01-01T00:00:03Z | | 0 |

Scenario: Should return no results when latest_update_gt is given but no entries are greater than latest_update_gt
Given I am @John
And there are the following items:
| item | type |
| @Task1 | Task |
| @Task2 | Task |
| @Task3 | Task |
And there are the following threads:
| participant | item | visible_by_participant | latest_update_at |
| @John | @Task1 | 1 | 2023-01-01 00:00:01 |
| @John | @Task2 | 1 | 2023-01-01 00:00:02 |
| @John | @Task3 | 1 | 2023-01-01 00:00:03 |
And I am @John
When I send a GET request to "/threads?is_mine=1&latest_update_gt=2023-01-01T00:00:03Z&sort=latest_update_at"
Then the response code should be 200
And the response should be a JSON array with 0 entries
4 changes: 2 additions & 2 deletions app/database/item_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -322,8 +322,8 @@ func (s *ItemStore) DeleteItem(itemID int64) (err error) {
})
}

// getAncestorsRequestHelpPropagationQuery gets all ancestors of an itemID while request_help_propagation = 1.
func (s *ItemStore) getAncestorsRequestHelpPropagationQuery(itemID int64) *DB {
// GetAncestorsRequestHelpPropagatedQuery gets all ancestors of an itemID while request_help_propagation = 1.
func (s *ItemStore) GetAncestorsRequestHelpPropagatedQuery(itemID int64) *DB {
return s.Raw(`
WITH RECURSIVE items_ancestors_request_help_propagation(item_id) AS
(
Expand Down
2 changes: 1 addition & 1 deletion app/database/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ func (u *User) CanRequestHelpTo(s *DataStore, itemID, helperGroupID int64) bool
// one of the ancestors (including himself) of User has the can_request_help_to(Group) on Item,
// recursively on Item’s ancestors while request_help_propagation=1, for each Group being a descendant of Group.

itemAncestorsRequestHelpPropagationQuery := s.Items().getAncestorsRequestHelpPropagationQuery(itemID)
itemAncestorsRequestHelpPropagationQuery := s.Items().GetAncestorsRequestHelpPropagatedQuery(itemID)

canRequestHelpTo, err := s.Users().
Joins("JOIN groups_ancestors_active ON groups_ancestors_active.child_group_id = ?", u.GroupID).
Expand Down
3 changes: 2 additions & 1 deletion testhelpers/feature_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ func FeatureContext(s *godog.Suite) {
s.Step(`^there are the following items:$`, ctx.ThereAreTheFollowingItems)
s.Step(`^there are the following tasks:$`, ctx.ThereAreTheFollowingTasks)
s.Step(`^there are the following item permissions:$`, ctx.ThereAreTheFollowingItemPermissions)
s.Step(`^there are the following item relations:$`, ctx.ThereAreTheFollowingItemRelations)
s.Step(`^I can watch the group (@\w+)$`, ctx.ICanWatchGroup)
s.Step(`^I can watch the participant with id "([^"]*)"$`, ctx.ICanWatchGroupWithID)
s.Step(`^I can view (none|info|content|content_with_descendants|solution) on item with id "([^"]*)"$`,
Expand Down Expand Up @@ -83,7 +84,7 @@ func FeatureContext(s *godog.Suite) {
s.Step(`^the response should be a JSON array with (\d+) entr(ies|y)$`, ctx.ItShouldBeAJSONArrayWithEntries)
s.Step(`^the response at ([^ ]+) should be "([^"]*)"$`, ctx.TheResponseAtShouldBeTheValue)
s.Step("^the response at ([^ ]+) should be:$", ctx.TheResponseAtShouldBe)
s.Step("^the response should not be defined at ([^ ]+)$", ctx.TheResponseShouldNotBeDefinedAt)
s.Step("^the response at ([^ ]+) in JSON should be:$", ctx.TheResponseAtInJSONShouldBe)

s.Step(`^the table "([^"]*)" should be:$`, ctx.TableShouldBe)
s.Step(`^the table "([^"]*)" should be empty$`, ctx.TableShouldBeEmpty)
Expand Down
113 changes: 105 additions & 8 deletions testhelpers/steps_app_language.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ const (
strTrue = "true"
)

var itemPermissionKeys = []string{"can_view", "can_grant_view", "can_watch", "can_edit", "is_owner", "can_request_help_to"}
var (
itemPermissionKeys = []string{"can_view", "can_grant_view", "can_watch", "can_edit", "is_owner", "can_request_help_to"}
itemPropagationKeys = []string{"grant_view_propagation", "watch_propagation", "edit_propagation", "request_help_propagation"}
)

// ctx.getParameterMap parses parameters in format key1=val1,key2=val2,... into a map.
func (ctx *TestContext) getParameterMap(parameters string) map[string]string {
Expand Down Expand Up @@ -257,23 +260,34 @@ func (ctx *TestContext) addGroupManager(manager, group, canWatchMembers, canGran
}

// addPermissionsGranted adds a permission granted in the database.
func (ctx *TestContext) addPermissionGranted(group, item, permission, permissionValue string) {
func (ctx *TestContext) addPermissionGranted(group, item, sourceGroup, origin, permission, permissionValue string) {
groupID := ctx.getReference(group)
sourceGroupID := ctx.getReference(sourceGroup)
itemID := ctx.getReference(item)

permissionsGrantedTable := "permissions_granted"
key := strconv.FormatInt(groupID, 10) + "," + strconv.FormatInt(itemID, 10)
key := strconv.FormatInt(groupID, 10) + "," +
strconv.FormatInt(itemID, 10) + "," +
strconv.FormatInt(sourceGroupID, 10) + "," +
origin

if !ctx.isInDatabase(permissionsGrantedTable, key) {
ctx.addInDatabase(permissionsGrantedTable, key, map[string]interface{}{
"group_id": groupID,
"source_group_id": groupID,
"source_group_id": sourceGroupID,
"item_id": itemID,
"origin": origin,
})
}

if permission == "can_request_help_to" {
permissionValue = strconv.FormatInt(ctx.getReference(permissionValue), 10)
canRequestHelpToGroupID := strconv.FormatInt(ctx.getReference(permissionValue), 10)

if !ctx.isInDatabase("groups", canRequestHelpToGroupID) {
ctx.addGroup(permissionValue, "Group "+referenceToName(permissionValue), "Class")
}

permissionValue = canRequestHelpToGroupID
}

if permission == "is_owner" {
Expand Down Expand Up @@ -320,14 +334,18 @@ func (ctx *TestContext) addResult(attemptID, participant, item string, validated
)
}

func (ctx *TestContext) getItemItemKey(parentItemID, childItemID int64) string {
return strconv.FormatInt(parentItemID, 10) + "," + strconv.FormatInt(childItemID, 10)
}

// addItemItem adds an item-item in the database.
func (ctx *TestContext) addItemItem(parentItem, childItem string) {
parentItemID := ctx.getReference(parentItem)
childItemID := ctx.getReference(childItem)

ctx.addInDatabase(
"items_items",
strconv.FormatInt(parentItemID, 10)+","+strconv.FormatInt(childItemID, 10),
ctx.getItemItemKey(parentItemID, childItemID),
map[string]interface{}{
"parent_item_id": parentItemID,
"child_item_id": childItemID,
Expand All @@ -336,6 +354,12 @@ func (ctx *TestContext) addItemItem(parentItem, childItem string) {
)
}

func (ctx *TestContext) addItemItemPropagation(parent, child, propagation, propagationValue string) {
key := ctx.getItemItemKey(ctx.getReference(parent), ctx.getReference(child))

ctx.dbTables["items_items"][key][propagation] = propagationValue
}

// addItem adds an item in the database.
func (ctx *TestContext) addItem(fields map[string]string) {
dbFields := make(map[string]interface{})
Expand Down Expand Up @@ -693,9 +717,26 @@ func (ctx *TestContext) ThereAreTheFollowingItemPermissions(itemPermissions *mes
}

func (ctx *TestContext) applyUserPermissionsOnItem(itemPermission map[string]string) error {
sourceGroup := itemPermission["group"]
if definedSourceGroup, ok := itemPermission["source_group"]; ok {
sourceGroup = definedSourceGroup
}

origin := "group_membership"
if definedOrigin, ok := itemPermission["origin"]; ok {
origin = definedOrigin
}

for _, permissionKey := range itemPermissionKeys {
if permissionValue, ok := itemPermission[permissionKey]; ok {
err := ctx.UserSetPermissionOnItemWithID(permissionKey, permissionValue, itemPermission["group"], itemPermission["item"])
err := ctx.UserSetPermissionExtendedOnItemWithID(
permissionKey,
permissionValue,
itemPermission["group"],
itemPermission["item"],
sourceGroup,
origin,
)
if err != nil {
return err
}
Expand All @@ -705,6 +746,48 @@ func (ctx *TestContext) applyUserPermissionsOnItem(itemPermission map[string]str
return nil
}

// ThereAreTheFollowingItemRelations defines item relations, in items_items table.
func (ctx *TestContext) ThereAreTheFollowingItemRelations(itemPermissions *messages.PickleStepArgument_PickleTable) error {
for i := 1; i < len(itemPermissions.Rows); i++ {
itemRelation := ctx.getRowMap(i, itemPermissions)

err := ctx.applyItemRelation(itemRelation)
if err != nil {
return err
}
}

return nil
}

func (ctx *TestContext) applyItemRelation(itemRelation map[string]string) error {
ctx.addItemItem(itemRelation["parent"], itemRelation["item"])

for _, propagationKey := range itemPropagationKeys {
if propagationValue, ok := itemRelation[propagationKey]; ok {
boolValue, err := strconv.ParseBool(propagationValue)
if err != nil {
panic(fmt.Sprintf("applyItemRelation: %v cannot be parsed as a boolean", boolValue))
}

if boolValue {
propagationValue = "1"
} else {
propagationValue = "0"
}

ctx.addItemItemPropagation(
itemRelation["parent"],
itemRelation["item"],
propagationKey,
propagationValue,
)
}
}

return nil
}

// ICanWatchGroup adds the permission for the user to watch a group.
func (ctx *TestContext) ICanWatchGroup(groupName string) error {
return ctx.UserIsAManagerOfTheGroupWith(getParameterString(map[string]string{
Expand Down Expand Up @@ -816,9 +899,23 @@ func (ctx *TestContext) IAmAMemberOfTheGroup(name string) error {
return ctx.IAmAMemberOfTheGroupWithID(name)
}

// ItemRelationSetPropagation adds a propagation on an item relation.
func (ctx *TestContext) ItemRelationSetPropagation(propagation, value, parent, item string) error {
ctx.addItemItemPropagation(parent, item, propagation, value)

return nil
}

// UserSetPermissionExtendedOnItemWithID gives a user a permission on an item with a specific source_group and origin.
func (ctx *TestContext) UserSetPermissionExtendedOnItemWithID(permission, value, user, item, sourceGroup, origin string) error {
ctx.addPermissionGranted(user, item, sourceGroup, origin, permission, value)

return nil
}

// UserSetPermissionOnItemWithID gives a user a permission on an item.
func (ctx *TestContext) UserSetPermissionOnItemWithID(permission, value, user, item string) error {
ctx.addPermissionGranted(user, item, permission, value)
ctx.addPermissionGranted(user, item, user, "group_membership", permission, value)

return nil
}
Expand Down
Loading