Skip to content

Commit

Permalink
Merge pull request #937 from girder/annotation-access-flag
Browse files Browse the repository at this point in the history
Add annotation creation access flag
  • Loading branch information
eagw authored Aug 18, 2022
2 parents 580ac93 + 99ada00 commit 20e0749
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 23 deletions.
6 changes: 6 additions & 0 deletions girder_annotation/girder_large_image_annotation/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#############################################################################

from girder import events
from girder.constants import registerAccessFlag
from girder.exceptions import ValidationException
from girder.plugin import GirderPlugin, getPlugin
from girder.settings import SettingDefault
Expand Down Expand Up @@ -58,6 +59,11 @@ def validateBoolean(doc):
constants.PluginSettings.LARGE_IMAGE_ANNOTATION_HISTORY: True,
})

# Access flags

registerAccessFlag(constants.ANNOTATION_ACCESS_FLAG, 'Create annotations',
'Allow user to create annotations')


class LargeImageAnnotationPlugin(GirderPlugin):
DISPLAY_NAME = 'Large Image Annotation'
Expand Down
2 changes: 2 additions & 0 deletions girder_annotation/girder_large_image_annotation/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@
from girder_large_image.constants import PluginSettings

PluginSettings.LARGE_IMAGE_ANNOTATION_HISTORY = 'large_image.annotation_history'

ANNOTATION_ACCESS_FLAG = 'large_image_create_annotations'
42 changes: 32 additions & 10 deletions girder_annotation/girder_large_image_annotation/rest/annotation.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,13 @@
from girder.api.rest import Resource, filtermodel, loadmodel, setResponseHeader
from girder.constants import AccessType, SortDir, TokenScope
from girder.exceptions import AccessException, RestException, ValidationException
from girder.models.folder import Folder
from girder.models.item import Item
from girder.models.user import User
from girder.utility import JsonEncoder
from girder.utility.progress import setResponseTimeLimit

from .. import constants
from ..models.annotation import Annotation, AnnotationSchema
from ..models.annotationelement import Annotationelement

Expand Down Expand Up @@ -61,6 +63,7 @@ def __init__(self):
self.route('DELETE', ('item', ':id'), self.deleteItemAnnotations)
self.route('GET', ('folder', ':id'), self.returnFolderAnnotations)
self.route('GET', ('folder', ':id', 'present'), self.existFolderAnnotations)
self.route('GET', ('folder', ':id', 'create'), self.canCreateFolderAnnotations)
self.route('PUT', ('folder', ':id', 'access'), self.setFolderAnnotationAccess)
self.route('GET', ('old',), self.getOldAnnotations)
self.route('DELETE', ('old',), self.deleteOldAnnotations)
Expand Down Expand Up @@ -256,22 +259,29 @@ def generateResult():
.param('body', 'A JSON object containing the annotation.',
paramType='body')
.errorResponse('ID was invalid.')
.errorResponse('Write access was denied for the item.', 403)
.errorResponse('Read access was denied for the item.', 403)
.errorResponse('Invalid JSON passed in request body.')
.errorResponse("Validation Error: JSON doesn't follow schema.")
)
@access.user
@loadmodel(map={'itemId': 'item'}, model='item', level=AccessType.WRITE)
@loadmodel(map={'itemId': 'item'}, model='item', level=AccessType.READ)
@filtermodel(model='annotation', plugin='large_image')
def createAnnotation(self, item, params):
try:
return Annotation().createAnnotation(
item, self.getCurrentUser(), self.getBodyJson())
except ValidationException as exc:
logger.exception('Failed to validate annotation')
raise RestException(
"Validation Error: JSON doesn't follow schema (%r)." % (
exc.args, ))
user = self.getCurrentUser()
folder = Folder().load(id=item['folderId'], user=user, level=AccessType.READ)
if Folder().hasAccess(folder, user, AccessType.WRITE) or Folder(
).hasAccessFlags(folder, user, constants.ANNOTATION_ACCESS_FLAG):
try:
return Annotation().createAnnotation(
item, self.getCurrentUser(), self.getBodyJson())
except ValidationException as exc:
logger.exception('Failed to validate annotation')
raise RestException(
"Validation Error: JSON doesn't follow schema (%r)." % (
exc.args, ))
else:
raise RestException('Write access and annotation creation access '
'were denied for the item.', code=403)

@describeRoute(
Description('Copy an annotation from one item to an other.')
Expand Down Expand Up @@ -671,6 +681,18 @@ def count():
annotations.count = count
return annotations

@autoDescribeRoute(
Description('Check if the user can create annotations in a folder')
.param('id', 'The ID of the folder', required=True, paramType='path')
.errorResponse('ID was invalid.')
)
@access.user
@loadmodel(model='folder', level=AccessType.READ)
def canCreateFolderAnnotations(self, folder):
user = self.getCurrentUser()
return Folder().hasAccess(folder, user, AccessType.WRITE) or Folder().hasAccessFlags(
folder, user, constants.ANNOTATION_ACCESS_FLAG)

@autoDescribeRoute(
Description('Set the access for all the user-owned annotations from the items in a folder')
.param('id', 'The ID of the folder', required=True, paramType='path')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
if annotations.length
a.g-annotation-download(href=`${apiRoot}/annotation/item/${item.id}`, title='Download annotations', download=`${item.get('name')}_annotations.json`)
i.icon-download
if accessLevel >= AccessType.WRITE
if creationAccess
a.g-annotation-upload(title='Upload annotation')
i.icon-upload
if accessLevel >= AccessType.ADMIN && annotations.length
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,16 +59,22 @@ const AnnotationListWidget = View.extend({
},

render() {
this.$el.html(annotationList({
item: this.model,
accessLevel: this.model.getAccessLevel(),
annotations: this.collection,
users: this.users,
canDraw: this._viewer && this._viewer.annotationAPI(),
drawn: this._drawn,
apiRoot: getApiRoot(),
AccessType
}));
restRequest({
type: 'GET',
url: 'annotation/folder/' + this.model.get('folderId') + '/create'
}).done((createResp) => {
this.$el.html(annotationList({
item: this.model,
accessLevel: this.model.getAccessLevel(),
creationAccess: createResp,
annotations: this.collection,
users: this.users,
canDraw: this._viewer && this._viewer.annotationAPI(),
drawn: this._drawn,
apiRoot: getApiRoot(),
AccessType
}));
});
return this;
},

Expand Down Expand Up @@ -185,7 +191,8 @@ const AnnotationListWidget = View.extend({
type: 'annotation',
hideRecurseOption: true,
parentView: this,
model
model,
noAccessFlag: true
}).on('g:accessListSaved', () => {
this.collection.fetch(null, true);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ function editAnnotAccess() {
modelType: 'annotation',
model,
hideRecurseOption: false,
parentView: this
parentView: this,
noAccessFlag: true
});
});
});
Expand Down

0 comments on commit 20e0749

Please sign in to comment.