API for Views and models

Summary

We have a great deal of shared functionality for the models in the rhaptos2.repo.cnxbase which is inherited over multiple inheritence by each model class. The model classes also inherit SqlAlchemy base class.

It is intended to remove sqlalchemy and move to pure psycopg2 approach. If we move to greenlets as well we need to test that ability. As such I have not introduced a pool for psyocpg2 work yet. (see sessioncache)

API

rhaptos2.repo.model

dbase-backed models for content on unpub repositories

This module provides the class defintions for

These are backd onto SQLAlchemy foundations and then onto PostgresQL database. An explcit use of the ARRAY datatype in postgres limits the ability to swap out backends.

Security

We expect to receive a HTTP HEADER (REMOTE_USER / X-Fake-CNXUser) with a user-uri

A cnx user-uri is in the glossary (!)

models: I am trying to keep things simple. This may not be a good idea.

each model is a class, based on a SQLAlchemy foundation with :class:CNXBase as a extra inheritence. This CNXBase gives us to and from json capabilities, but each model has to manually override to and from json calls if

What is the same about each model / class

  1. They have only themselves - there are no child tables needing hierarchialcly handling. If this was needed we should look at rhaptos2.user for the approach - pretty simple, just modiufy the from and to dict calls
  2. They are representing resources - that is a entity we want to have some form of access control over. So we use the generic-ish approach of userroles - see below.
  3. THey are all ID’d by URI

Note on json - the obvious generic approach, of traversing the SQLA model and converting to/from JSON automagically has so far failed. There are no sensible approaches “out there”, seemingly because the obvious approaches (iter) have already been hijacked by SQLA and the edge cases are producing weird effects.

So, this basically implies a protocol for objects / classes

  1. support creater_uri= in your constructor
  2. override fomr and to json SQLA where needed
  3. Support ACLs
  4. err ....
class rhaptos2.repo.model.Collection(id_=None, creator_uuid=None)[source]
set_acls(owner_uuid, aclsd)[source]

allow each Folder / collection class to have a set_acls call, but catch here and then pass generic function the right UserRoleX klass. Still want to find way to generically follow sqla

class rhaptos2.repo.model.Folder(id_=None, creator_uuid=None)[source]

FOlder Class inheriting from SQLAlchemy and from a CNXBase class to get a few generic functions.

set_acls(owner_uuid, aclsd)[source]

allow each Folder / collection class to have a set_acls call, but catch here and then pass generic function the right UserRoleX klass. Still want to find way to generically follow sqla.

class rhaptos2.repo.model.Module(id_=None, creator_uuid=None)[source]
>>> #test we can autogen a uuid
>>> m = Module(id_=None, creator_uuid="cnxuser:1234")
>>> m.mediaType
'application/vnd.org.cnx.module'
>>> j = m.jsonify("cnxuser:1234")
{...
>>> d = json.loads(j)
>>> assert 'id' in d.keys()
>>> assert 'mediaType' in d.keys()
set_acls(owner_uuid, aclsd)[source]

allow each Module class to have a set_acls call, but catch here and then pass generic function the right UserRoleX klass. Still want to find way to generically follow sqla

class rhaptos2.repo.model.UserRoleCollection(**kwargs)[source]

The roles and users assigned for a given folder

class rhaptos2.repo.model.UserRoleFolder(**kwargs)[source]

The roles and users assigned for a given folder

We have following Roles: Owner, Maintainer, XXX

Todo :storing timezones naively here needs fixing
class rhaptos2.repo.model.UserRoleModule(**kwargs)[source]

The roles and users assigned for a given folder

rhaptos2.repo.model.acl_setter(klass, uri, requesting_user_uri, acls_list)[source]
rhaptos2.repo.model.change_approval(uobj, jsond, requesting_user_uri, requesttype)[source]

is the change valid for the given ACL context? returns True / False

rhaptos2.repo.model.delete_o(resource_uri, requesting_user_uri)[source]
rhaptos2.repo.model.get_by_id(klass, ID, useruri)[source]

refactoring: ID -> uri Then use uri -> klass to get klass needed Then do not abort but raise capturable error. THen pass useruri all way through.

rhaptos2.repo.model.klass_from_uri(URI)[source]

Return the callable klass that corresponds to a URI

>>> c = klass_from_uri("cnxfolder:1234")
>>> c
<class '__main__.Folder'>
>>> c = klass_from_uri("cnxfolder:")
>>> c
<class '__main__.Folder'>
>>> c = klass_from_uri("cnxfolder:1234/acl/cnxuser:123456")
>>> c
<class '__main__.Folder'>
rhaptos2.repo.model.obj_from_urn(URN, requesting_user_uri, klass=None)[source]

THis is the refactored version of get_by_id

URN
cnxmodule:1234-5678
requesting_user_urn
cnxuser:1234-5678

I have reservations about encoding the type in the ID string. But not many.

rhaptos2.repo.model.post_o(klass, incomingd, requesting_user_uri)[source]

Given a dict representing the complete set of fields then create a new user and those fields

I am getting a dictionary direct form Flask request object - want to handle that myself with parser.

returns User object, for later saveing to DB

rhaptos2.repo.model.put_o(jsond, klass, ID, requesting_user_uri)[source]

Given a user_id, and a json_str representing the “Updated” fields then update those fields for that user_id

rhaptos2.repo.model.workspace_by_user(user_uri)[source]

Its at times like these I just want to pass SQL in...

rhaptos2.repo.cnxbase

THis exists solely to provide less typing for a “leaf node” in a simple realtional schema (1:M and 1:M-N:1) when used with SQLAlchemy

SA does not support class based inheritence in the normal Python way for objects inheriting from Base. Thus we have those objects perform multiple inheritence...

security issues

Discussion

I see each resource (folder, module, collection) as a individual resource with individually managed permissions.

Each function (GET PUT POST DELETE) should be requested through the API with a security “thing” (useruri). This is to try and keep stateless end to end (ie not have to worry how we are handling sessions from the request point through the backend)

Security use cases

folder

  1. User has RW permission set on Folder F and children C1,C2
class rhaptos2.repo.cnxbase.CNXBase[source]

The resources we use (Folder, Collection, Module) all adhere to a common access protocol that is defined in :class:CNXBase.

Where incomingjsond is a python representation of a json object that meets a folder jsonschema

> f2 = model.Folder(creator_uuid=user_urn) > f2.populate_self(incomingjsond) f2 will now be populated

> f2.to_dict(requesting_user_urn) Here I am getting the object to return as python std types, so they can be easily jsonified at the last possible minute.

adduserrole(userrole_klass, usrdict)[source]

keeping a common funciton in one place

Given a usr_uuid and a role_type, update a UserRole object

I am checking setter_user is authorised in calling function. Ideally check here too.

delete(dbase_session)[source]

Assumes we are working with sqlalchemy dbsessions

from_dict(userprofile_dict)[source]

given a dict, derived from either test fixture or json POST populate the object.

setattr is valid as a means of internally updating a sqlalchemy object, and will correctly pass the diamond lookup.

>>> m = model.Module(creator_uuid="test")
>>> d = {"totalfake":100}
>>> m.populate_self(d)
... Tried to set attr totalfake when no ...

[correct usage would be as:] >> d = {“title”:”testtitle”} >> m.populate_self(d) >> m.save(db_session)

get_utcnow()[source]

Eventually we shall handle TZones here too

is_action_auth(action=None, requesting_user_uri=None)[source]
Given a user and a action type, determine if it is
authorised on this object

#unittest not available as setup is large. >> C = CNXBase() >> C.is_action_auth(action=”PUT”, requesting_user_uri=”Fake1”) * [u’Fake1’] True >> C.is_action_auth(action=”PUT”, requesting_user_uri=”ff”) * [u’Fake1’] False

jsonify(requesting_user_uri, softform=True)[source]

public method to return the object as a JSON formatted string.

form. There are two types we shall support. softform and hardform A resource only contains links (pointers) to other resources - so a container-type resource (folder, collection) will hold links only such as

body = [“/folder/1234”, “/module/5678”]

However if we returned that resource to the client, it would then need to perform n more requests to get the title of each.

To avoid this we return a softform

body = [{‘id’: ‘/folder/1234’, ‘title’: ‘foo’, ‘mediatype’:’application/vnd.org.cnx.folder’},
{‘id’: ‘/module/5678’, ‘title’: ‘bar’, ‘mediatype’:’application/vnd.org.cnx.module’},

This however needs us to descend into the container, and requiores a security check at each resource. It also requires a flag for whih form.

At the moment only folder has any need for a softform approach and it is the default here

parse_json(jsonstr)[source]

Given a json-formatted string representing a folder, return a dict

There is a lot todo here. We should have version handling (see online discussions) We should check that the json is actually valid for a folder

populate_self(d)[source]
safe_type_out(col)[source]

return the value of a coulmn field safely for json This is essentially a JSONEncoder sublclass inside object - ...

save(dbase_session)[source]

Assumes we are working with sqlalchemy dbsessions

This is a naive implementation of the dateModified field. More sensitive approaches would include taking the timestamp of a request as the point of all changes. FIXME

set_acls(setter_user_uri, acllist, userrole_klass=None)[source]

set the user acls on this object.

inheriting from CNXBase implies we are modelling a resource, and we want to control Read?write of the resource through ACLs - which are represented in dbase as userrole_<resource>

NB whilst practical to use one userrole table and preferable SQLAlchemy seems to place limits on it. and I dont want to muck about.

SOme, not all objects that inherit form CNXBase (!) will have a relatred user_roles table. This will map the object ID to a acl type and a user

[{‘dateLastModifiedUTC’: None,
‘dateCreatedUTC’: None, ‘user_uri’: u’Testuser1’, ‘role_type’: ‘author’},
{‘dateLastModifiedUTC’: None,
‘dateCreatedUTC’: None, ‘user_uri’: u’testuser2’, ‘role_type’: ‘author’}]
validateid(id_)[source]

Given a id_ check it is of correct uri format

>>> C = CNXBase()
>>> C.validateid("cnxuser:1234")
True
>>> C.validateid("1234")
False

Project Versions

Table Of Contents

Previous topic

Simple spec links

Next topic

Authentication API

This Page