Design considerations for TwistraNet
+++++++++++++++++++++++++++++++++++++

Design considerations
=====================

Most things with TwistraNet are derivated from Twistable objects.
Stuff which can produce content are Account objects. Thus, user profiles (but not only) are derivated account objects.

This inheritance stuff is there in case we one day move data into a cassandra-like DB.

Caveats
-------

The directory structure is nonstandard for a regular django project. This is ok for everything but models.
If you want your additional models to work, you have to:
- import them from models.__init__
- add a Meta attribute "app_label = 'twistranet'" in your model class

Hot topics
==========

SSO
----

See http://docs.djangoproject.com/en/dev/howto/auth-remote-user/ to find what we've got to do on this topic.


LDAP / ActiveDirectory
-----------------------

Twistranet works fairly well with LDAP. If you want to authenticate against LDAP, first install django-ldap-auth module,
then update your settings.py with the following information (this if for default AD install, your mileage may vary):

AUTHENTICATION_BACKENDS = (
    'django_auth_ldap.backend.LDAPBackend',
    'django.contrib.auth.backends.ModelBackend',
)

AUTH_LDAP_SERVER_URI = "ldap://xx.xx.xx.xx:389"
AUTH_LDAP_BIND_DN = "CN=admin,DC=my-company,DC=dom"
AUTH_LDAP_BIND_PASSWORD = "admin-password"
AUTH_LDAP_USER_SEARCH = LDAPSearch("ou=Users,dc=my-company,dc=dom", ldap.SCOPE_SUBTREE, "(SAMAccountName=%(user)s)")

AUTH_LDAP_USER_ATTR_MAP = {
    "first_name":   "givenName", 
    "last_name":    "sn",
    "email":        "mail",
}

AUTH_LDAP_PROFILE_ATTR_MAP = {
    "title":        "cn",
    "slug":         "uid",
}
AUTH_LDAP_ALWAYS_UPDATE_USER = True


With this configuration, user data will automatically get populated from AD upon login.

Acknoledgements
================

The Menu system is derived from http://code.google.com/p/django-menu/

Security Management
===================

User authentication and model access
-------------------------------------

Model access is restricted according to user authentication status.
The <mymodel>.objects managers are overloaded so that security is ensured when you call them.
Basically, you don't have to worry about security in your views. If you use the 'request' objects, 
the model will dig into it to find authenticated user and restrain security for you.

If you never use a model method or attribute begining with an _, we guarantee you're safe.
(XXX TODO: document the __account__ hack)

Roles and permissions
---------------------

A roles is an account property on a given object. It gives you information about the relation between the account and the object.
Role can be (as of writing) either:

- ANONYMOUS (special role to allow public interaction)
- AUTHENTICATED (no particular relation between the account and the object)
- COMMUNITY_MEMBER (the account is a member of the object - used for communities only)
- COMMUNITY_MANAGER (the account is a community manager of the object - used for communities only)
- CONTENT_PUBLIC (the account has the right to view the content's publisher)
- CONTENT_AUTHOR (the account is the author of the protected content - used for content only)
- CONTENT_NETWORK (the account is in the network of the content publisher - used for content only)
- ADMINISTRATOR (the account is a global administrator - ie. he belongs to the admin community)
- SYSTEM (a special role given only to the system account)

Other applications can define additional rights.

Object methods can be protected by a permission. And permissions are given to roles.
The role / permission mapping may be different between objects.

For example, a community may have the following permissions:

- can_list (permission allowing the given roles to know the object exists and access its title, descr. and picture)
- can_view (permission to allow the given roles to read more than the object basic properties
- can_publish (permission to allow writing content on the community's wall)
- can_edit (permission to modify the community properties)
- can_join (permission to join a community)
- can_leave (permission to leave the community)
- can_list_members (permission to view who's inside the community)

On an object, each permission is given a list of allowed roles. All those roles are computed inside a permissions set.
The permission set is what the end user manipulates: it makes easier to understand the security system.
Each object has a list of available permissions set which can be applied by the save() method (or upon object creation).
You can't manipulate individual permissions as they'll stick with the permission set upon save anyway.

Roles are python objects, they're not stored on the database.
Permissions are stored in database in an object-specific table. Permissions are hardcoded (they're heavily used in the code anyway).
Permission templates are python objects and are not stored in database but may be in the future.

Permissions API
---------------

The role module provide a protect_by_permission(perm_label) decorator taking a permission string as a parameter. Example:

@protect_by_permission("can_view")
def view_inside_stuff(self,):
    pass

The decorator will ensure that currently logged user has one of the 'can_view' authorized roles on the current object

Internally, permissions are stored as Integers bit fields. There can't be more than 32 roles on a project - which is not a problem by now.


Resources
==========

Description
------------

Resources are links to digital assets that TwistraNet doesn't really manage directly.
A resource can be:
- A regular file ;
- An image, video, office file, each one offering additional services

Creation and security
----------------------

Ressources are not stored in TN's database nor accessible directly through the web: it is secured either by a content or an account.
A ressource is usually bound to a content, and is available in the same exact conditions as the content it's bound to.
But resources can also be bound to several contents, avoiding file duplication. If you have an image you want to use in several contents, you can use only one resource (and store only one file).
Finally, a resource can be unbound, ie. not associated to a content. In this case, security is driven by the account's can_view permission. To protect security, a resource must be explicitly unbound before beeing "visible".
A resource owner can always reach its own resources.

An editable resource is deleted when the last content bound to it is deleted (unless explicitly told against it).

Resources are shared across accounts (ie. a resource created by one account can be freely used by another), but a resource has an owner account who is the only one to be able to edit it (if the owner is a community, then the community managers can edit it).

URL Access
----------

Resource can be reached either by id, by alias or, if they're an attribute of an object, by a x/resource/pty_name where 'x' is the object base url.
Example for profile picture : /account/xxx/resource/picture

Resources overloading
---------------------

(TODO: Explain how this works eg. for translation of regular text fields)

I18N
------

(v2) Resources can be translated (both metadata AND resource pointer). In case of a translated resource, the translator is the owner of the translation, but if the original resource is modified/deleted, all translated resources get modified/deleted too.

Resource Managers
-----------------

Resources are handled via a resource manager which tells TN where to find the resource content.
The resource managers provides ways for TN to fetch the resource, and (for some of them) to create new ones, to search them, etc.
The resource managers bundled with TN are:
- FilesystemResourceManager: This if for files stored on the filesystem. TN provides a basic file management system to allow upload, storage and deletion of files.
- ReadonlyFilesystemResourceManager: Same as FilesystemResourceManager, but read-only. This is useful for default resources provided by TN or to enable directory access without hassles.
- LinkResourceManager: Resources are designated by a URL. This URL is proxied by the server when served.
- DatabaseResourceManager : Resources are stored in the same database as TN regular content objects. This is especially useful for I18N resources

Each account can define its own (and only one) editable ResourceManager. This way, when a resource is uploaded, it's put in the right bucket.
If an account doesn't define an editable ResourceManager, the one from the Global Community is used. If no global one is defined... then no file upload permited. No arms, no chocolate.

Each resource manager must define its own ways of setting the locator attribute (listing, text entry, ...).



I18N
====

The lib/languages.py file describes all available language for TN.
Everytime a content is produced, a language can be associated to it.
Then, it will be preferably displayed for users having this language in their spoken languages list.

Same for a resource.

A content can be translated, then the source version will always have precedence over translated versions.



Database consistancy
====================

The model is not a rdbm-normal one, THIS IS ON PURPOSE:
1/ for read performance reasons
2/ to allow the model to be quite easily migrated to a nosql backend someday

The framework normally takes care of keeping it straight. But it may be broken in some way (fixture not correct, updates or bugs, crashes, ...)
Here are some clues to ensure that the schema is ok:

0/ Default objects
- System Account is a system-wide account which can read everything
- Global Community is the community every account belongs to
- Admin Community is the default community for administrators.

1/ Content types

Content are typed, but the 'content' table holds generic information. Check every content type object and check that content.content_type field is set to the right type.

2/ Relations

Symetrical (ie. approved) relations are two entries in the relations table. Check that for approved relations both are present (and approved!)


TODO: DEV
==========

Check that alias are not formed like ids (all numbers)....


TODO: DESIGN
=============

There are some issues to solve.

- Translation (including translatable content vs. translatable fields (for accounts by example))
- Fully-accessible vs. Listed (ie. only title/image/description visible) content or accounts: how to manage that?
- Search in faceted and secured ways
- Resources: resource listing (eg. directory listing)
- Taxonomy: use a 'Folder' content type?

