class EmailAddress(EntityType): """an electronic mail address associated to a short alias""" __permissions__ = { # application that wishes public email, or use it for something else # than users (eg Company, Person), should explicitly change permissions 'read': ('managers', ERQLExpression('U use_email X')), 'add': ( 'managers', 'users', ), 'delete': ('managers', 'owners', ERQLExpression('P use_email X, U has_update_permission P')), 'update': ('managers', 'owners', ERQLExpression('P use_email X, U has_update_permission P')), } alias = String(fulltextindexed=True, maxsize=56) address = String(required=True, fulltextindexed=True, indexed=True, unique=True, maxsize=128) prefered_form = SubjectRelation( 'EmailAddress', cardinality='?*', description=_('when multiple addresses are equivalent \ (such as [email protected] and [email protected]), set this \ to indicate which is the preferred form.'))
class CWUser(WorkflowableEntityType): """define a CubicWeb user""" __permissions__ = { 'read': ('managers', 'users', ERQLExpression('X identity U')), 'add': ('managers', ), 'delete': ('managers', ), 'update': ( 'managers', ERQLExpression('X identity U, NOT U in_group G, G name "guests"'), ), } login = String( required=True, unique=True, maxsize=64, description=_('unique identifier used to connect to the application')) upassword = Password( required=True) # password is a reserved word for mysql firstname = String(maxsize=64) surname = String(maxsize=64) last_login_time = TZDatetime(description=_('last connection date')) in_group = SubjectRelation( 'CWGroup', cardinality='+*', constraints=[RQLConstraint('NOT O name "owners"')], description=_('groups grant permissions to the user'))
def test_expression(self): expr = ERQLExpression('U use_email X') rql, found, keyarg = expr.transform_has_permission() self.assertEqual( rql, 'Any X WHERE EXISTS(U use_email X, X eid %(x)s, U eid %(u)s)') self.assertEqual(found, None) self.assertEqual(keyarg, None)
class Affaire(WorkflowableEntityType): __permissions__ = { 'read': ('managers', ERQLExpression('X owned_by U'), ERQLExpression('X concerne S?, S owned_by U')), 'add': ('managers', ERQLExpression('X concerne S, S owned_by U')), 'update': ('managers', 'owners', ERQLExpression('X in_state S, S name in ("pitetre", "en cours")')), 'delete': ('managers', 'owners', ERQLExpression('X concerne S, S owned_by U')), } ref = String(fulltextindexed=True, indexed=True, constraints=[SizeConstraint(16)]) sujet = String(fulltextindexed=True, constraints=[SizeConstraint(256)]) descr = RichString(fulltextindexed=True, description=_('more detailed description')) duration = Int() invoiced = Float() opt_attr = Bytes() depends_on = SubjectRelation('Affaire') require_permission = SubjectRelation('CWPermission') concerne = SubjectRelation(('Societe', 'Note')) todo_by = SubjectRelation('Personne', cardinality='?*') documented_by = SubjectRelation('Card')
class Note(WorkflowableEntityType): date = String(maxsize=10) type = String(vocabulary=[u'todo', u'a', u'b', u'T', u'lalala']) para = String(maxsize=512, __permissions__={ 'add': ('managers', ERQLExpression('X in_state S, S name "todo"')), 'read': ('managers', 'users', 'guests'), 'update': ('managers', ERQLExpression('X in_state S, S name "todo"')), }) something = String(maxsize=1, __permissions__={ 'read': ('managers', 'users', 'guests'), 'add': (ERQLExpression('NOT X para NULL'), ), 'update': ('managers', 'owners') }) migrated_from = SubjectRelation('Note') attachment = SubjectRelation('File') inline1 = SubjectRelation('Affaire', inlined=True, cardinality='?*', constraints=[ RQLUniqueConstraint( 'S type T, S inline1 A1, A1 todo_by C, ' 'Y type T, Y inline1 A2, A2 todo_by C', 'S,Y') ]) todo_by = SubjectRelation('CWUser')
class Societe(EntityType): __permissions__ = { 'read': ('managers', 'users', 'guests'), 'update': ('managers', 'owners', ERQLExpression('U login L, X nom L')), 'delete': ('managers', 'owners', ERQLExpression('U login L, X nom L')), 'add': ('managers', 'users',) } nom = String()
def project(etypeclass): """ decorator for the in-project entity types Sets the permissions """ etypeclass.__permissions__ = { 'read': ('managers', ERQLExpression('U canread P, X project P')), 'add': ('managers', 'users'), # can't really do it there 'update': ('managers', ERQLExpression('U canwrite P, X project P')), 'delete': ('managers', ERQLExpression('U canwrite P, X project P')) } return etypeclass
class Affaire(EntityType): __permissions__ = { 'read': ('managers', ERQLExpression('X owned_by U'), ERQLExpression('X concerne S?, S owned_by U')), 'add': ('managers', ERQLExpression('X concerne S, S owned_by U')), 'update': ('managers', 'owners', ERQLExpression('X in_state S, S name in ("pitetre", "en cours")')), 'delete': ('managers', 'owners', ERQLExpression('X concerne S, S owned_by U')), } ref = String(fulltextindexed=True, indexed=True, maxsize=16) documented_by = SubjectRelation('Card', cardinality='1*') concerne = SubjectRelation(('Societe', 'Note'), cardinality='1*')
class Project(EntityType): name = String(required=True, fulltextindexed=True) __permissions__ = { 'read': ('managers', ERQLExpression('X owned_by U'), ERQLExpression('U canread X')), 'add': ('managers', 'users'), 'update': ('managers', ERQLExpression('U canwrite X')), 'delete': ('managers', ERQLExpression('U canwrite X')) } subproject_of = SubjectRelation('Project', composite='object', inlined=True, cardinality='?*')
def test_erqlexpression(self): self.assertRaises(RQLSyntaxError, ERQLExpression, '1') expr = ERQLExpression('X travaille S, S owned_by U') self.assertEqual( str(expr), 'Any X WHERE X travaille S, S owned_by U, X eid %(x)s, U eid %(u)s' ) expr = ERQLExpression( 'X foo S, S bar U, X baz XE, S quux SE HAVING XE > SE') self.assertEqual( str(expr), 'Any X WHERE X foo S, S bar U, X baz XE, S quux SE, X eid %(x)s, ' 'U eid %(u)s HAVING XE > SE')
class Affaire(EntityType): __permissions__ = { 'read': ('managers', 'users', 'guests'), 'add': ('managers', ERQLExpression('X concerne S, S owned_by U')), 'update': ('managers', 'owners', ERQLExpression('X concerne S, S owned_by U')), 'delete': ('managers', 'owners', ERQLExpression('X concerne S, S owned_by U')), } ref = String(fulltextindexed=True, indexed=True, constraints=[SizeConstraint(16)]) sujet = String(fulltextindexed=True, constraints=[SizeConstraint(256)]) concerne = SubjectRelation('Societe') opt_attr = Bytes()
class Frozable(EntityType): __permissions__ = { 'read': ('managers', 'users'), 'add': ('managers', 'users'), 'update': ('managers', ERQLExpression('X frozen False'),), 'delete': ('managers', ERQLExpression('X frozen False'),) } name = String() frozen = Boolean(default=False, __permissions__ = { 'read': ('managers', 'users'), 'add': ('managers', 'users'), 'update': ('managers', 'owners') })
class ExternalResource(EntityType): """ An entity used to to store a file path. Attributes ---------- name: String (mandatory) the parameter name. filepath: String (mandatory) the file path. uploaded_by: SubjectRelation (mandatory) who has created the item. """ # Set default permissions __permissions__ = { "read": ( "managers", ERQLExpression("X uploaded_by U"), ), "add": ("managers", "users"), "delete": ("managers", ), "update": ("managers", ), } # Entity parameters name = String(required=True, indexed=False) filepath = String(required=True, indexed=False) # The link to the owner of the data uploaded_by = SubjectRelation("CWUser", cardinality="1*", composite="subject")
class Basket(EntityType): """a basket contains a set of other entities""" __permissions__ = { 'read': ( 'managers', ERQLExpression('X owned_by U'), ), 'add': ( 'managers', 'users', ), 'delete': ( 'managers', 'owners', ), 'update': ( 'managers', 'owners', ), } name = String(required=True, indexed=True, internationalizable=True, maxsize=128) description = RichString(fulltextindexed=True)
def post_build_callback(schema): config._CONTAINER_ETYPE_MAP.clear() project = config.Container('Project', 'project', subcontainers=('Version', )) project.define_container(schema) version = config.Container('Version', 'version') version.define_container(schema) def project_rtypes_perms(role_to_container): # role_to_container is either 'S' or 'O' return { 'read': ('managers', 'users'), 'add': ('managers', RRQLExpression('%s project P, U canwrite P' % role_to_container)), 'delete': ('managers', RRQLExpression('%s project P, U canwrite P' % role_to_container)) } project.setup_rdefs_security(schema, project_rtypes_perms, project_rtypes_perms) # version container & security version.setup_rdefs_security(schema, PUB_SYSTEM_REL_PERMS, PUB_SYSTEM_REL_PERMS) for conf in (version, project): conf.setup_etypes_security( schema, { 'read': ('managers', ERQLExpression( '(X owned_by U) OR (X %s C, U canread C)' % conf.crtype)), 'add': ('managers', 'users'), 'delete': ('managers', 'owners'), 'update': ('managers', 'owners', ERQLExpression('X %s C, U canwrite C' % conf.crtype)), })
class Societe(EntityType): __unique_together__ = [('nom', 'type', 'cp')] __permissions__ = { 'read': ('managers', 'users', 'guests'), 'update': ('managers', 'owners', ERQLExpression('U login L, X nom L')), 'delete': ('managers', 'owners', ERQLExpression('U login L, X nom L')), 'add': ('managers', 'users',) } nom = String(maxsize=64, fulltextindexed=True) web = String(maxsize=128) type = String(maxsize=128) # attribute in common with Note tel = Int() fax = Int() rncs = String(maxsize=128) ad1 = String(maxsize=128) ad2 = String(maxsize=128) ad3 = String(maxsize=128) cp = String(maxsize=12) ville= String(maxsize=32)
class CWSearch(EntityType): """ An entity used to save a search which may contains resources on the server file system. Attributes ---------- title: String (mandatory) a short description of the file. path: String (mandatory) the rql request that will be saved. expiration_data: Date (mandatory) the expiration date of the current search. result: SubjectRelation (mandatory) a json file with all the server resources associated with the current search - {"rql": rql, "files": [], "nonexistent-files": []} rset: SubjectRelation (mandatory) the result set associated with the current search. rset_type: String (optional, default 'jsonexport') the type of the rset. """ __permissions__ = { "read": ( "managers", ERQLExpression("X owned_by U"), ), "add": ("managers", "users"), "delete": ("managers", "owners"), "update": ("managers", "owners"), } title = String(maxsize=256, required=True, constraints=[ RQLUniqueConstraint( "X title N, S title N, X owned_by U, X is CWSearch", mainvars="X", msg=_("this name is already used")) ], description=_("Please set a unique subset name.")) path = String(required=True, description=_("the rql request we will save (do not edit " "this field).")) expiration_date = Date(required=True, indexed=True) # json which contains resultset and filepath result = SubjectRelation("File", cardinality="1*", inlined=True, composite="subject") rset = SubjectRelation("File", cardinality="1*", inlined=True, composite="subject") # view regid to show rset rset_type = String(required=True, default="jsonexport", maxsize=50)
class BlogEntry(WorkflowableEntityType): __permissions__ = { 'read': ( 'managers', 'users', ERQLExpression('X in_state S, S name "published"'), ), 'add': ('managers', 'users'), 'update': ('managers', 'owners'), 'delete': ('managers', 'owners') } title = String(required=True, fulltextindexed=True, maxsize=256) content = RichString(required=True, fulltextindexed=True) entry_of = SubjectRelation('Blog') same_as = SubjectRelation('ExternalUri')
class EmailPart(EntityType): """an email attachment""" __permissions__ = { 'read': ( 'managers', 'users', 'guests', ), # XXX if E parts X, U has_read_permission E 'add': ( 'managers', ERQLExpression('E parts X, U has_update_permission E'), ), 'delete': ('managers', ERQLExpression('E parts X, U has_update_permission E')), 'update': ( 'managers', 'owners', ), } content = String(fulltextindexed=True) content_format = String(required=True, maxsize=50) ordernum = Int(required=True) alternative = SubjectRelation('EmailPart', symmetric=True)
def test_yams_inheritance_and_security_bug(self): with self.temporary_permissions(Division={ 'read': ('managers', ERQLExpression('X owned_by U')) }): with self.new_access(u'iaminusersgrouponly').repo_cnx() as cnx: rqlst = self.repo.vreg.rqlhelper.parse( 'Any X WHERE X is_instance_of Societe') self.repo.vreg.solutions(cnx, rqlst, {}) self.repo.vreg.rqlhelper.annotate(rqlst) plan = cnx.repo.querier.plan_factory(rqlst, {}, cnx) plan.preprocess(rqlst) self.assertEqual( rqlst.as_string(), '(Any X WHERE X is IN(Societe, SubDivision)) UNION ' '(Any X WHERE X is Division, EXISTS(X owned_by %(B)s))')
class Note(Para): __specializes_schema__ = True __permissions__ = {'read': ('managers', 'users', 'guests',), 'update': ('managers', 'owners',), 'delete': ('managers', ), 'add': ('managers', ERQLExpression('X ecrit_part PE, U in_group G, ' 'PE require_permission P, P name "add_note", ' 'P require_group G'),)} whatever = Int(default=0) # keep it before `date` for unittest_migraction.test_add_attribute_int yesno = Boolean(default=False) date = Datetime() type = String(maxsize=1) unique_id = String(maxsize=1, required=True, unique=True) mydate = Date(default='TODAY') oldstyledefaultdate = Date(default='2013/01/01') newstyledefaultdate = Date(default=dt.date(2013, 1, 1)) shortpara = String(maxsize=11, default='hop', vocabulary=['hop', 'hop hop', 'hop hop hop']) ecrit_par = SubjectRelation('Personne', constraints=[RQLConstraint('S concerne A, O concerne A')]) attachment = SubjectRelation('File')
class CWProcessing(EntityType): """ An entity used to to processing status and results. Attributes ---------- status: String (mandatory) the processing status, one of ('error', 'sucess', 'scheduled', 'running'). result_form: SubjectRelation (mandatory) the link to the result form. uploaded_by: SubjectRelation (mandatory) who has created the item. """ # Set default permissions __permissions__ = { "read": ( "managers", ERQLExpression("X uploaded_by U"), ), "add": ("managers", "users"), "delete": ("managers", ), "update": ("managers", ), } # Entity parameters status = String(required=True, indexed=True, vocabulary=("error", "sucess", "scheduled", "running")) # The link to the result data result_form = SubjectRelation("UploadForm", cardinality="**", composite="subject") # The link to the owner of the data uploaded_by = SubjectRelation("CWUser", cardinality="1*", composite="subject")
class File(EntityType): """ A downloadable file which may contains binary data. """ __permissions__ = { "read": ( "managers", ERQLExpression("X owned_by U"), ), "add": ("managers", "users"), "delete": ("managers", "owners"), "update": ("managers", "owners"), } title = String(fulltextindexed=True, maxsize=256) data = Bytes(required=True, fulltextindexed=True, description=_("file to upload")) data_format = String( required=True, maxsize=128, description=_("MIME type of the file. Should be dynamically set at " "upload time.")) data_encoding = String( maxsize=32, description=_("encoding of the file when it applies (e.g. text). " "Should be dynamically set at upload time.")) data_name = String( required=True, fulltextindexed=True, description=_("name of the file. Should be dynamically set at upload " "time.")) data_sha1hex = String( maxsize=40, description=_("SHA1 sum of the file. May be set at upload time.")) description = RichString(fulltextindexed=True, internationalizable=True, default_format="text/rest")
def test_has_update_permission(self): expr = ERQLExpression('P use_email X, U has_update_permission P') rql, found, keyarg = expr.transform_has_permission() self.assertEqual(rql, 'Any X,P WHERE P use_email X, X eid %(x)s') self.assertEqual(found, [(u'update', 1)]) self.assertEqual(keyarg, None)
def test_comparison(self): self.assertEqual(ERQLExpression('X is CWUser', 'X', 0), ERQLExpression('X is CWUser', 'X', 0)) self.assertNotEqual(ERQLExpression('X is CWUser', 'X', 0), ERQLExpression('X is CWGroup', 'X', 0))
from cubicweb.schema import (CubicWebSchema, CubicWebSchemaLoader, RQLConstraint, RQLUniqueConstraint, RQLVocabularyConstraint, ERQLExpression, RRQLExpression, normalize_expression, order_eschemas, guess_rrqlexpr_mainvars, build_schema_from_namespace) from cubicweb.devtools.testlib import CubicWebTC DATADIR = join(dirname(__file__), 'data') # build a dummy schema ######################################################## PERSONNE_PERMISSIONS = { 'read': ('managers', 'users', 'guests'), 'update': ('managers', 'owners'), 'add': ('managers', ERQLExpression('X travaille S, S owned_by U')), 'delete': ( 'managers', 'owners', ), } CONCERNE_PERMISSIONS = { 'read': ('managers', 'users', 'guests'), 'add': ('managers', RRQLExpression('U has_update_permission S')), 'delete': ('managers', RRQLExpression('O owned_by U')), } schema = CubicWebSchema('Test Schema') enote = schema.add_entity_type(EntityType('Note')) eaffaire = schema.add_entity_type(EntityType('Affaire'))
# CubicWeb import from yams.buildobjs import EntityType from yams.buildobjs import String from yams.buildobjs import Bytes from yams.buildobjs import RichString from yams.buildobjs import RelationDefinition from cubicweb.schema import ERQLExpression from yams.buildobjs import SubjectRelation ############################################################################### # Modification of the schema ############################################################################### UPLOAD_PERMISSIONS = { "read": ("managers", ERQLExpression("X created_by U")), "add": ("managers", ), "update": ("managers", ), "delete": ("managers", ), } UPLOAD_RELATION_PERMISSIONS = { "read": ("managers", "users"), "add": ("managers", ), "delete": ("managers", ), } class UploadFile(EntityType): """ An entity used to upload file which may contain binary data.
class toto(RelationType): __permissions__ = { 'read': ('managers', ), 'add': ('managers', ERQLExpression('S bla Y'),), 'delete': ('managers',), }
enable_upload = config["enable-upload"] share_group_uploads = config["share_group_uploads"] authorized_upload_groups = config["authorized-upload-groups"] authorized_upload_groups = set(authorized_upload_groups) for group_name in authorized_upload_groups: BASE_GROUPS.add(group_name) authorized_upload_groups.add("managers") UPLOAD_PERMISSIONS["add"] = tuple(authorized_upload_groups) UPLOAD_RELATION_PERMISSIONS["add"] = UPLOAD_PERMISSIONS["add"] UPLOAD_PUBLIC_ENTITIES = [] if enable_upload: UPLOAD_PUBLIC_ENTITIES = ["CWUser", "CWGroup"] if share_group_uploads: UPLOAD_PERMISSIONS["read"] = ( "managers", ERQLExpression(("X created_by Y, Y in_group GY, NOT GY name 'users', " "U in_group GY"))) ############################################################################### # Define permission relations ############################################################################### # CWGROUP class can_read(RelationDefinition): """ Link a group to an assessment with this relation to give the group users read access to the assessment related entities. """ inlined = False subject = "CWGroup" object = "Assessment" cardinality = "*+"
from yams.buildobjs import EntityType, String, SubjectRelation, Bytes from cubicweb.schema import RRQLExpression, ERQLExpression, PUB_SYSTEM_REL_PERMS from cubes.container import config from cubes.container.secutils import PERM, PERMS # custom ad-hoc rules PERMS['project'] = { 'read': ('managers', ERQLExpression('(X owned_by U) OR (U canread X)')), 'add': ('managers', 'users'), 'delete': ('managers', 'owners'), 'update': ('managers', 'owners', ERQLExpression('U canwrite X')), } PERMS['project-documentation'] = { 'read': ('managers', 'users'), 'add': ('managers', 'project_managers'), 'delete': ('managers', 'project_managers') } PERMS['version-documentation'] = { 'read': ('managers', 'users'), 'add': ('managers', 'version_managers'), 'delete': ('managers', 'version_managers') } PERMS['ticket-documentation'] = { 'read': ('managers', 'users'), 'add': ('managers', 'ticket_managers'), 'delete': ('managers', 'ticket_managers') }