-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
move grunt-related build files to chipper/js/grunt/, phetsims/chipper#92
- Loading branch information
1 parent
5476d59
commit e87e7ca
Showing
4 changed files
with
357 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
|
||
Defining Schemas | ||
================ | ||
|
||
JSON Schema | ||
----------- | ||
|
||
Ramses supports JSON Schema Draft 3 and Draft 4. You can read the official `JSON Schema documentation here <http://json-schema.org/documentation.html>`_. | ||
|
||
.. code-block:: json | ||
{ | ||
"type": "object", | ||
"title": "Item schema", | ||
"$schema": "http://json-schema.org/draft-04/schema", | ||
(...) | ||
} | ||
All Ramses-specific properties are prefixed with an underscore. | ||
|
||
Showing Fields | ||
-------------- | ||
|
||
If you've enabled authentication, you can list which fields to return to authenticated users in ``_auth_fields`` and to non-authenticated users in ``_public_fields``. Additionaly, you can list fields to be hidden but remain hidden (with proper persmissions) in ``_hidden_fields``. | ||
|
||
.. code-block:: json | ||
{ | ||
(...) | ||
"_auth_fields": ["id", "name", "description"], | ||
"_public_fields": ["name"], | ||
"_hidden_fields": ["token"], | ||
(...) | ||
} | ||
Nested Documents | ||
---------------- | ||
|
||
If you use ``Relationship`` fields in your schemas, you can list those fields in ``_nested_relationships``. Your fields will then become nested documents instead of just showing the ``id``. You can control the level of nesting by specifying the ``_nesting_depth`` property, defaul is 1. | ||
|
||
.. code-block:: json | ||
{ | ||
(...) | ||
"_nested_relationships": ["relationship_field_name"], | ||
"_nesting_depth": 2 | ||
(...) | ||
} | ||
Custom "user" Model | ||
------------------- | ||
|
||
When authentication is enabled, a default "user" model will be created automatically with 4 fields: "username", "email", "groups" and "password". You can extend this default model by defining your own "user" schema and by setting ``_auth_model`` to ``true`` on that schema. You can add any additional fields in addition to those 4 default fields. | ||
|
||
.. code-block:: json | ||
{ | ||
(...) | ||
"_auth_model": true, | ||
(...) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
|
||
import logging | ||
|
||
import ramlfications | ||
from nefertari.acl import RootACL as NefertariRootACL | ||
from nefertari.utils import dictset | ||
|
||
|
||
log = logging.getLogger(__name__) | ||
|
||
|
||
def includeme(config): | ||
from .generators import generate_server, generate_models | ||
Settings = dictset(config.registry.settings) | ||
config.include('nefertari.engine') | ||
|
||
config.registry.database_acls = Settings.asbool('database_acls') | ||
if config.registry.database_acls: | ||
config.include('nefertari_guards') | ||
|
||
config.include('nefertari') | ||
config.include('nefertari.view') | ||
config.include('nefertari.json_httpexceptions') | ||
|
||
# Process nefertari settings | ||
if Settings.asbool('enable_get_tunneling'): | ||
config.add_tween('nefertari.tweens.get_tunneling') | ||
|
||
if Settings.asbool('cors.enable'): | ||
config.add_tween('nefertari.tweens.cors') | ||
|
||
if Settings.asbool('ssl_middleware.enable'): | ||
config.add_tween('nefertari.tweens.ssl') | ||
|
||
if Settings.asbool('request_timing.enable'): | ||
config.add_tween('nefertari.tweens.request_timing') | ||
|
||
# Set root factory | ||
config.root_factory = NefertariRootACL | ||
|
||
# Process auth settings | ||
root = config.get_root_resource() | ||
root_auth = getattr(root, 'auth', False) | ||
|
||
log.info('Parsing RAML') | ||
raml_root = ramlfications.parse(Settings['ramses.raml_schema']) | ||
|
||
log.info('Starting models generation') | ||
generate_models(config, raml_resources=raml_root.resources) | ||
|
||
if root_auth: | ||
from .auth import setup_auth_policies, get_authuser_model | ||
if getattr(config.registry, 'auth_model', None) is None: | ||
config.registry.auth_model = get_authuser_model() | ||
setup_auth_policies(config, raml_root) | ||
|
||
config.include('nefertari.elasticsearch') | ||
|
||
log.info('Starting server generation') | ||
generate_server(raml_root, config) | ||
|
||
log.info('Running nefertari.engine.setup_database') | ||
from nefertari.engine import setup_database | ||
setup_database(config) | ||
|
||
from nefertari.elasticsearch import ES | ||
ES.setup_mappings() | ||
|
||
if root_auth: | ||
config.include('ramses.auth') | ||
|
||
log.info('Server succesfully generated\n') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,216 @@ | ||
|
||
import logging | ||
|
||
import six | ||
from pyramid.security import ( | ||
Allow, Deny, | ||
Everyone, Authenticated, | ||
ALL_PERMISSIONS) | ||
from nefertari.acl import CollectionACL | ||
from nefertari.resource import PERMISSIONS | ||
from nefertari.elasticsearch import ES | ||
|
||
from .utils import resolve_to_callable, is_callable_tag | ||
|
||
|
||
log = logging.getLogger(__name__) | ||
|
||
|
||
actions = { | ||
'allow': Allow, | ||
'deny': Deny, | ||
} | ||
special_principals = { | ||
'everyone': Everyone, | ||
'authenticated': Authenticated, | ||
} | ||
ALLOW_ALL = (Allow, Everyone, ALL_PERMISSIONS) | ||
|
||
|
||
def validate_permissions(perms): | ||
""" Validate :perms: contains valid permissions. | ||
:param perms: List of permission names or ALL_PERMISSIONS. | ||
""" | ||
if not isinstance(perms, (list, tuple)): | ||
perms = [perms] | ||
valid_perms = set(PERMISSIONS.values()) | ||
if ALL_PERMISSIONS in perms: | ||
return perms | ||
if set(perms) - valid_perms: | ||
raise ValueError( | ||
'Invalid ACL permission names. Valid permissions ' | ||
'are: {}'.format(', '.join(valid_perms))) | ||
return perms | ||
|
||
|
||
def parse_permissions(perms): | ||
""" Parse permissions ("perms") which are either exact permission | ||
names or the keyword 'all'. | ||
:param perms: List or comma-separated string of nefertari permission | ||
names, or 'all' | ||
""" | ||
if isinstance(perms, six.string_types): | ||
perms = perms.split(',') | ||
perms = [perm.strip().lower() for perm in perms] | ||
if 'all' in perms: | ||
return ALL_PERMISSIONS | ||
return validate_permissions(perms) | ||
|
||
|
||
def parse_acl(acl_string): | ||
""" Parse raw string :acl_string: of RAML-defined ACLs. | ||
If :acl_string: is blank or None, all permissions are given. | ||
Values of ACL action and principal are parsed using `actions` and | ||
`special_principals` maps and are looked up after `strip()` and | ||
`lower()`. | ||
ACEs in :acl_string: may be separated by newlines or semicolons. | ||
Action, principal and permission lists must be separated by spaces. | ||
Permissions must be comma-separated. | ||
E.g. 'allow everyone view,create,update' and 'deny authenticated delete' | ||
:param acl_string: Raw RAML string containing defined ACEs. | ||
""" | ||
if not acl_string: | ||
return [ALLOW_ALL] | ||
|
||
aces_list = acl_string.replace('\n', ';').split(';') | ||
aces_list = [ace.strip().split(' ', 2) for ace in aces_list if ace] | ||
aces_list = [(a, b, c.split(',')) for a, b, c in aces_list] | ||
result_acl = [] | ||
|
||
for action_str, princ_str, perms in aces_list: | ||
# Process action | ||
action_str = action_str.strip().lower() | ||
action = actions.get(action_str) | ||
if action is None: | ||
raise ValueError( | ||
'Unknown ACL action: {}. Valid actions: {}'.format( | ||
action_str, list(actions.keys()))) | ||
|
||
# Process principal | ||
princ_str = princ_str.strip().lower() | ||
if princ_str in special_principals: | ||
principal = special_principals[princ_str] | ||
elif is_callable_tag(princ_str): | ||
principal = resolve_to_callable(princ_str) | ||
else: | ||
principal = princ_str | ||
|
||
# Process permissions | ||
permissions = parse_permissions(perms) | ||
|
||
result_acl.append((action, principal, permissions)) | ||
|
||
return result_acl | ||
|
||
|
||
class BaseACL(CollectionACL): | ||
""" ACL Base class. """ | ||
|
||
es_based = False | ||
_collection_acl = (ALLOW_ALL, ) | ||
_item_acl = (ALLOW_ALL, ) | ||
|
||
def _apply_callables(self, acl, obj=None): | ||
""" Iterate over ACEs from :acl: and apply callable principals | ||
if any. | ||
Principals are passed 3 arguments on call: | ||
:ace: Single ACE object that looks like (action, callable, | ||
permission or [permission]) | ||
:request: Current request object | ||
:obj: Object instance to be accessed via the ACL | ||
Principals must return a single ACE or a list of ACEs. | ||
:param acl: Sequence of valid Pyramid ACEs which will be processed | ||
:param obj: Object to be accessed via the ACL | ||
""" | ||
new_acl = [] | ||
for i, ace in enumerate(acl): | ||
principal = ace[1] | ||
if six.callable(principal): | ||
ace = principal(ace=ace, request=self.request, obj=obj) | ||
if not ace: | ||
continue | ||
if not isinstance(ace[0], (list, tuple)): | ||
ace = [ace] | ||
ace = [(a, b, validate_permissions(c)) for a, b, c in ace] | ||
else: | ||
ace = [ace] | ||
new_acl += ace | ||
return tuple(new_acl) | ||
|
||
def __acl__(self): | ||
""" Apply callables to `self._collection_acl` and return result. """ | ||
return self._apply_callables(acl=self._collection_acl) | ||
|
||
def generate_item_acl(self, item): | ||
acl = self._apply_callables( | ||
acl=self._item_acl, | ||
obj=item) | ||
if acl is None: | ||
acl = self.__acl__() | ||
return acl | ||
|
||
def item_acl(self, item): | ||
""" Apply callables to `self._item_acl` and return result. """ | ||
return self.generate_item_acl(item) | ||
|
||
def item_db_id(self, key): | ||
# ``self`` can be used for current authenticated user key | ||
if key != 'self': | ||
return key | ||
user = getattr(self.request, 'user', None) | ||
if user is None or not isinstance(user, self.item_model): | ||
return key | ||
return getattr(user, user.pk_field()) | ||
|
||
def __getitem__(self, key): | ||
""" Get item using method depending on value of `self.es_based` """ | ||
if not self.es_based: | ||
return super(BaseACL, self).__getitem__(key) | ||
return self.getitem_es(self.item_db_id(key)) | ||
|
||
def getitem_es(self, key): | ||
es = ES(self.item_model.__name__) | ||
obj = es.get_item(id=key) | ||
obj.__acl__ = self.item_acl(obj) | ||
obj.__parent__ = self | ||
obj.__name__ = key | ||
return obj | ||
|
||
|
||
class DatabaseACLMixin(object): | ||
""" Mixin to be used when ACLs are stored in database. """ | ||
|
||
def item_acl(self, item): | ||
""" Objectify ACL if ES is used or call item.get_acl() if | ||
db is used. | ||
""" | ||
if self.es_based: | ||
from nefertari_guards.elasticsearch import get_es_item_acl | ||
return get_es_item_acl(item) | ||
return super(DatabaseACLMixin, self).item_acl(item) | ||
|
||
def getitem_es(self, key): | ||
""" Override to support ACL filtering. | ||
To do so: passes `self.request` to `get_item` and uses | ||
`ACLFilterES`. | ||
""" | ||
from nefertari_guards.elasticsearch import ACLFilterES | ||
es = ACLFilterES(self.item_model.__name__) | ||
params = { | ||
'id': key, | ||
'request': self.request, | ||
} | ||
obj = es.get_item(**params) | ||
obj.__acl__ = self.item_acl(obj) | ||
obj.__parent__ = self | ||
obj.__name__ = key | ||
return obj | ||
|