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

Permissions/rows #267

Merged
merged 53 commits into from
Oct 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
d47a172
Update user_table schema to add row_permissions
s-tschne Jan 23, 2023
3dd28ee
Setup new schema versions
s-tschne Jan 23, 2023
a48bfa6
Add Permissions to rows
s-tschne Jan 23, 2023
f50b9d5
Add row permissions to history
s-tschne Jan 23, 2023
d2783d7
Adapt AuthTests to new Row Permissions
s-tschne Jan 23, 2023
6a26376
Add row_permissions column to user_table creation
s-tschne Jan 23, 2023
07f31f5
Add viewRow Permission to Rolemodel
s-tschne Feb 23, 2023
284d09b
Add rowPermission retrieval functions to RowModel
s-tschne Feb 23, 2023
529b21c
Update schema Version in System Model
s-tschne Feb 23, 2023
e3a9f30
Fix RowAnnotationHistory Entry duplicates
s-tschne Feb 23, 2023
0c9e536
Recursively reject link and concat values the current user is not per…
s-tschne Feb 23, 2023
ed960a9
Add viewRow permission to test permissions
s-tschne Feb 23, 2023
479ff8f
Adapt existing tests to viewRow Permission
s-tschne Feb 23, 2023
6fa323b
Adapt more tests to viewRow permission
s-tschne Feb 24, 2023
042676c
Improve rowPermissions retrieval and form and improve isMatching func…
s-tschne Feb 24, 2023
9a418a4
Fix bug in unauthorized value removal
s-tschne Feb 24, 2023
121a114
Add tests for viewRow permissions
s-tschne Feb 24, 2023
d66819e
Remove println statement
s-tschne Feb 24, 2023
a26b326
Fix db versions
s-tschne Feb 24, 2023
0f1183f
Remove prints
s-tschne Feb 24, 2023
08dd3fe
comply to consistent naming
zingmane Oct 5, 2023
ecc0fe7
add row annotation history test
zingmane Oct 5, 2023
e01a8c7
add row permission test with more than one perm
zingmane Oct 6, 2023
cae3703
re-introduce finalFlag, we don't want to mix row
zingmane Oct 10, 2023
a4940b9
revert test changes
zingmane Oct 10, 2023
2184623
fix error in row annotation open api doc
zingmane Oct 10, 2023
8cbeeca
add row permission endpoints to open api doc
zingmane Oct 10, 2023
ebdd388
skip row permission tests for now
zingmane Oct 10, 2023
5b7e0a7
add default row permission role
zingmane Oct 11, 2023
763fed0
fix fallback getRowPermission for not existing row
zingmane Oct 11, 2023
4b97f9c
update open api doc,
zingmane Oct 11, 2023
d3d2448
add preparation for row permission insert
zingmane Oct 11, 2023
46c93d7
write rowPermission to user_table
zingmane Oct 11, 2023
1e41886
write rowPermission history to user_table_history
zingmane Oct 12, 2023
eaa7c5d
fix error with optional getJson
zingmane Oct 12, 2023
91a6ce0
fix some code quality issues
zingmane Oct 12, 2023
aa68724
add router, controller and model for row perms
zingmane Oct 12, 2023
ff6c432
add row permission tests
zingmane Oct 12, 2023
ae62c2f
change endpoint b/c DELETE doesn't allow body
zingmane Oct 13, 2023
75c9191
do not response row permissions
zingmane Oct 13, 2023
33705b6
move tests to separat RowPermissionTest file
zingmane Oct 13, 2023
e75116c
temp
zingmane Oct 13, 2023
847638b
refactor
zingmane Oct 13, 2023
c063a3d
one test failing
zingmane Oct 13, 2023
7410f94
all row permission tests green
zingmane Oct 16, 2023
8feafc5
add more row permission tests
zingmane Oct 16, 2023
0fdf2f4
add default testRole to be able to change rowPerms
zingmane Oct 16, 2023
3f343b5
refactor and clean up
zingmane Oct 16, 2023
fddb150
add test for hidden identifier rows
zingmane Oct 16, 2023
52056c9
add *.jsonc files to gitignore
zingmane Oct 16, 2023
5c96081
do not pass method to isMatching, not needed
zingmane Oct 16, 2023
41c303a
reactivate matrix build
zingmane Oct 16, 2023
0baa7a0
comply with PR comments
zingmane Oct 20, 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
2 changes: 1 addition & 1 deletion .github/workflows/main_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,6 @@ jobs:
path: artifacts

- name: Publish Unit Test Results
uses: EnricoMi/publish-unit-test-result-action@v1
uses: EnricoMi/publish-unit-test-result-action@v2
with:
files: artifacts/test-results/**/*.xml
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Tableaux specific
conf.json
conf-*.jsonc
conf-*.json
!conf-example.json
!conf-test-example.json
Expand All @@ -12,6 +13,7 @@ classes/
bin/
out/
**.DS_Store
role-*.jsonc
role-*.json
!role-permissions-example.json
!role-permissions-test.json
Expand Down
13 changes: 13 additions & 0 deletions role-permissions-test.json
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,19 @@
]
}
],
"view-test-row-permissions": [
{
"type": "grant",
"action": [
"viewRow"
],
"condition": {
"row": {
"permissions": "onlyGroupA|perm_1|perm_2|perm_3"
}
}
}
],
// Developer test role to be able to pass all integration tests
"dev": [
{
Expand Down
13 changes: 13 additions & 0 deletions src/main/resources/schema/schema_v34.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
CREATE OR REPLACE FUNCTION update_user_tables(tableid BIGINT)
RETURNS TEXT AS $$
BEGIN

EXECUTE 'ALTER TABLE public.user_table_' || tableid || ' ADD COLUMN row_permissions jsonb DEFAULT NULL;';
RETURN 'user_table_' || tableid :: TEXT;
END
$$ LANGUAGE plpgsql;

SELECT update_user_tables(table_id)
FROM system_table;

DROP FUNCTION update_user_tables( BIGINT );
124 changes: 120 additions & 4 deletions src/main/resources/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -1298,6 +1298,13 @@
"items": {
"$ref": "#/definitions/Shared Request: Row"
}
},
"rowPermissions": {
"type": "array",
"description": "Array of row permissions",
"items": {
"type": "string"
}
}
},
"example": {
Expand All @@ -1314,6 +1321,10 @@
}
]
}
],
"rowPermissions": [
"onlyUserGroupA",
"onlyUserGroupB"
]
}
}
Expand Down Expand Up @@ -1479,7 +1490,20 @@
"operationId": "update-row-annotations",
"parameters": [
{
"$ref": "#/parameters/langtags"
"name": "row annotation",
"in": "body",
"required": false,
"schema": {
"type": "object",
"properties": {
"final": {
"type": "boolean"
}
},
"example": {
"final": true
}
}
}
],
"responses": {
Expand Down Expand Up @@ -1764,6 +1788,74 @@
}
}
}
},
"/tables/{tableId}/rows/{rowId}/permissions": {
"parameters": [
{
"$ref": "#/parameters/tableId"
},
{
"$ref": "#/parameters/rowId"
}
],
"patch": {
"summary": "Add permissions to this row.",
"description": "Add permissions to this row.",
"tags": [
"permission"
],
"operationId": "add-row-permissions",
"parameters": [
{
"$ref": "#/parameters/rowPermissionBody"
}
],
"responses": {
"200": {
"$ref": "#/responses/ok-empty-body"
},
"404": {
"$ref": "#/responses/not-found-in-database"
}
}
},
"delete": {
"summary": "Delete all permissions of this row.",
"description": "Delete permissions of this row.",
"tags": [
"permission"
],
"operationId": "delete-row-permissions",
"responses": {
"200": {
"$ref": "#/responses/ok-empty-body"
},
"404": {
"$ref": "#/responses/not-found-in-database"
}
}
},
"put": {
"summary": "Replace permissions of this row.",
"description": "Replace current permissions of this row. This overwrites all permissions of this row with the new provided permission array.",
"tags": [
"permission"
],
"operationId": "replace-row-permissions",
"parameters": [
{
"$ref": "#/parameters/rowPermissionBody"
}
],
"responses": {
"200": {
"$ref": "#/responses/ok-empty-body"
},
"404": {
"$ref": "#/responses/not-found-in-database"
}
}
}
}
},
"definitions": {
Expand Down Expand Up @@ -2221,7 +2313,8 @@
"row_created",
"row_deleted",
"annotation_added",
"annotation_removed"
"annotation_removed",
"row_permissions_changed"
]
},
"historyType": {
Expand All @@ -2232,7 +2325,8 @@
"cell",
"comment",
"cell_flag",
"row_flag"
"row_flag",
"row_permissions"
]
},
"languageType": {
Expand Down Expand Up @@ -2280,7 +2374,8 @@
"datetime",
"concat",
"currency",
"group"
"group",
"permissions"
]
}
}
Expand Down Expand Up @@ -3615,6 +3710,11 @@
"formatPattern": "city {{1}} has {{2}} inhabitants"
}
]
},
"RowPermission": {
"description": "A row annotation",
"type": "string",
"example": "onlyUserGroupA"
}
},
"parameters": {
Expand Down Expand Up @@ -3765,6 +3865,22 @@
"type": "integer",
"format": "int64",
"required": false
},
"rowPermissionBody": {
"name": "value",
"description": "An array of row permission to add, remove or replace.",
"in": "body",
"required": true,
"schema": {
"type": "array",
"items": {
"type": "string"
},
"example": [
"onlyUserGroupA",
"onlyUserGroupB"
]
}
}
},
"responses": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,8 @@ class SystemController(
columnIds = columns.map(_.id)
rowsWithColumnIdAndValue = rows.map(columnIds.zip(_))

_ <- tableauxModel.createRows(table, rowsWithColumnIdAndValue)
// don't pass row permissions here, because method is only used for demo data
_ <- tableauxModel.createRows(table, rowsWithColumnIdAndValue, None)
} yield table
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import com.campudus.tableaux.router.auth.permission._

import io.vertx.scala.ext.web.RoutingContext
import org.vertx.scala.core.json.Json
import org.vertx.scala.core.json.JsonArray

import scala.concurrent.Future
import scala.util.Try
Expand Down Expand Up @@ -243,7 +244,11 @@ class TableauxController(
}
}

def createRow(tableId: TableId, values: Option[Seq[Seq[(ColumnId, _)]]])(
def createRow(
tableId: TableId,
values: Option[Seq[Seq[(ColumnId, _)]]],
rowPermissionsOpt: Option[Seq[String]] = None
)(
implicit user: TableauxUser
): Future[DomainObject] = {
checkArguments(greaterZero(tableId))
Expand All @@ -255,10 +260,10 @@ class TableauxController(
case Some(seq) =>
checkArguments(nonEmpty(seq, "rows"))
logger.info(s"createRows ${table.id} $values")
repository.createRows(table, seq)
repository.createRows(table, seq, rowPermissionsOpt)
case None =>
logger.info(s"createRow ${table.id}")
repository.createRow(table)
repository.createRow(table, rowPermissionsOpt)
}
} yield row
}
Expand Down Expand Up @@ -531,7 +536,7 @@ class TableauxController(
}
table <- repository.createTable(tableName, hidden = false)
columnIds <- repository.createColumns(table, columns).map(_.map(_.id))
_ <- repository.createRows(table, rows.map(columnIds.zip(_)))
_ <- repository.createRows(table, rows.map(columnIds.zip(_)), None)
completeTable <- retrieveCompleteTable(table.id)
} yield completeTable
}
Expand Down Expand Up @@ -594,4 +599,40 @@ class TableauxController(
historySequence <- repository.retrieveTableHistory(table, langtagOpt, typeOpt)
} yield SeqHistory(historySequence)
}

def addRowPermissions(tableId: TableId, rowId: RowId, rowPermissions: Seq[String])(
implicit user: TableauxUser
): Future[EmptyObject] = {
checkArguments(greaterZero(tableId), greaterZero(rowId))
logger.info(s"addRowPermissions $tableId $rowId $rowPermissions")

for {
table <- repository.retrieveTable(tableId)
_ <- repository.addRowPermissions(table, rowId, rowPermissions)
} yield EmptyObject()
}

def deleteRowPermissions(tableId: TableId, rowId: RowId)(
implicit user: TableauxUser
): Future[EmptyObject] = {
checkArguments(greaterZero(tableId), greaterZero(rowId))
logger.info(s"removeRowPermissions $tableId $rowId")

for {
table <- repository.retrieveTable(tableId)
_ <- repository.deleteRowPermissions(table, rowId)
} yield EmptyObject()
}

def replaceRowPermissions(tableId: TableId, rowId: RowId, rowPermissions: Seq[String])(
implicit user: TableauxUser
): Future[EmptyObject] = {
checkArguments(greaterZero(tableId), greaterZero(rowId))
logger.info(s"replaceRowPermissions $tableId $rowId $rowPermissions")

for {
table <- repository.retrieveTable(tableId)
_ <- repository.replaceRowPermissions(table, rowId, rowPermissions)
} yield EmptyObject()
}
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,56 @@
package com.campudus.tableaux.database.domain

import com.campudus.tableaux.database.model.TableauxModel.{ColumnId, RowId}
import com.campudus.tableaux.database.model.TableauxModel._

import org.vertx.scala.core.json.{Json, JsonArray, JsonObject}
import org.vertx.scala.core.json.Json
import org.vertx.scala.core.json.JsonArray
import org.vertx.scala.core.json.JsonObject

import scala.collection.JavaConverters._

import java.util.UUID
import org.joda.time.DateTime

case class RowLevelAnnotations(finalFlag: Boolean) extends DomainObject {
trait RowAnnotation {
val value: Any
val jsonKey: String

def getJson: JsonObject = {
Json.obj(jsonKey -> value)
}
}

// TODO refactor for multiple row level annotations
case class RowLevelAnnotations(finalFlag: Boolean) extends RowAnnotation {

override val value: Boolean = finalFlag
override val jsonKey = "final"
def isDefined: Boolean = finalFlag

override def getJson: JsonObject = {
Json.obj(
"final" -> finalFlag
jsonKey -> finalFlag
)
}
}

object RowPermissions {

def apply(rowPermissionSeq: RowPermissionSeq): RowPermissions = {
val jsonArray = Json.arr(rowPermissionSeq.map(_.toString()): _*)
new RowPermissions(jsonArray)
}
}

case class RowPermissions(rowPermissions: JsonArray) extends RowAnnotation {

override val value: Seq[String] = rowPermissions.asScala.toSeq.map(_.asInstanceOf[String])
override val jsonKey = "permissions"
def isDefined: Boolean = rowPermissions.size() > 0

override def getJson: JsonObject = {
Json.obj(
jsonKey -> rowPermissions
)
}
}
Expand Down
Loading
Loading