Skip to content

Commit

Permalink
Merge pull request #177 from clarkperkins/feature/auth-app
Browse files Browse the repository at this point in the history
Feature/auth app
  • Loading branch information
clarkperkins committed Jul 14, 2015
2 parents 769b21e + 22b2419 commit b60983b
Show file tree
Hide file tree
Showing 39 changed files with 1,286 additions and 337 deletions.
32 changes: 15 additions & 17 deletions stackdio/api_v1/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,19 @@ class APIRootView(APIView):

def get(self, request, format=None):
api = OrderedDict((
('core', {
'settings': reverse('usersettings-detail',
request=request,
format=format),
'change_password': reverse('change_password',
request=request,
format=format),
'version': reverse('version',
request=request,
format=format),
}),
('version', reverse('version',
request=request,
format=format)),
('users', reverse('user-list',
request=request,
format=format)),
('groups', reverse('group-list',
request=request,
format=format)),
('current_user', reverse('currentuser-detail',
request=request,
format=format)),

('cloud', OrderedDict((
('provider_types', reverse('cloudprovidertype-list',
request=request,
Expand Down Expand Up @@ -94,22 +96,18 @@ def get(self, request, format=None):
format=format)),
))

if request.user.is_staff:
api['core']['users'] = reverse('user-list',
request=request,
format=format)

return Response(api)


urlpatterns = patterns(
'',

url(r'^$', APIRootView.as_view()),
url(r'^$', APIRootView.as_view(), name='api-root'),

##
# IMPORTS URLS FROM ALL APPS
##
url(r'^', include('users.urls')),
url(r'^', include('core.urls')),
url(r'^', include('cloud.urls')),
url(r'^', include('stacks.urls')),
Expand Down
10 changes: 0 additions & 10 deletions stackdio/blueprints/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,6 @@
StackdioModelPermissions,
StackdioObjectPermissions,
)
from core.serializers import (
StackdioUserModelPermissionsSerializer,
StackdioGroupModelPermissionsSerializer,
StackdioUserObjectPermissionsSerializer,
StackdioGroupObjectPermissionsSerializer,
)
from core.viewsets import (
StackdioModelUserPermissionsViewSet,
StackdioModelGroupPermissionsViewSet,
Expand Down Expand Up @@ -118,26 +112,22 @@ class BlueprintPropertiesAPIView(mixins.BlueprintRelatedMixin, generics.Retrieve


class BlueprintModelUserPermissionsViewSet(StackdioModelUserPermissionsViewSet):
serializer_class = StackdioUserModelPermissionsSerializer
permission_classes = (permissions.BlueprintPermissionsModelPermissions,)
model_cls = models.Blueprint


class BlueprintModelGroupPermissionsViewSet(StackdioModelGroupPermissionsViewSet):
serializer_class = StackdioGroupModelPermissionsSerializer
permission_classes = (permissions.BlueprintPermissionsModelPermissions,)
model_cls = models.Blueprint


class BlueprintObjectUserPermissionsViewSet(mixins.BlueprintRelatedMixin,
StackdioObjectUserPermissionsViewSet):
serializer_class = StackdioUserObjectPermissionsSerializer
permission_classes = (permissions.BlueprintPermissionsObjectPermissions,)


class BlueprintObjectGroupPermissionsViewSet(mixins.BlueprintRelatedMixin,
StackdioObjectGroupPermissionsViewSet):
serializer_class = StackdioGroupObjectPermissionsSerializer
permission_classes = (permissions.BlueprintPermissionsObjectPermissions,)


Expand Down
18 changes: 0 additions & 18 deletions stackdio/cloud/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,6 @@
from blueprints.serializers import BlueprintSerializer
from core.exceptions import BadRequest, ResourceConflict
from core.permissions import StackdioModelPermissions, StackdioObjectPermissions
from core.serializers import (
StackdioUserModelPermissionsSerializer,
StackdioGroupModelPermissionsSerializer,
StackdioUserObjectPermissionsSerializer,
StackdioGroupObjectPermissionsSerializer,
)
from core.viewsets import (
StackdioModelUserPermissionsViewSet,
StackdioModelGroupPermissionsViewSet,
Expand Down Expand Up @@ -122,26 +116,22 @@ def destroy(self, request, *args, **kwargs):


class CloudProviderModelUserPermissionsViewSet(StackdioModelUserPermissionsViewSet):
serializer_class = StackdioUserModelPermissionsSerializer
permission_classes = (permissions.CloudProviderPermissionsModelPermissions,)
model_cls = models.CloudProvider


class CloudProviderModelGroupPermissionsViewSet(StackdioModelGroupPermissionsViewSet):
serializer_class = StackdioGroupModelPermissionsSerializer
permission_classes = (permissions.CloudProviderPermissionsModelPermissions,)
model_cls = models.CloudProvider


class CloudProviderObjectUserPermissionsViewSet(mixins.CloudProviderRelatedMixin,
StackdioObjectUserPermissionsViewSet):
serializer_class = StackdioUserObjectPermissionsSerializer
permission_classes = (permissions.CloudProviderPermissionsObjectPermissions,)


class CloudProviderObjectGroupPermissionsViewSet(mixins.CloudProviderRelatedMixin,
StackdioObjectGroupPermissionsViewSet):
serializer_class = StackdioGroupObjectPermissionsSerializer
permission_classes = (permissions.CloudProviderPermissionsObjectPermissions,)


Expand Down Expand Up @@ -285,26 +275,22 @@ def destroy(self, request, *args, **kwargs):


class CloudProfileModelUserPermissionsViewSet(StackdioModelUserPermissionsViewSet):
serializer_class = StackdioUserModelPermissionsSerializer
permission_classes = (permissions.CloudProfilePermissionsModelPermissions,)
model_cls = models.CloudProfile


class CloudProfileModelGroupPermissionsViewSet(StackdioModelGroupPermissionsViewSet):
serializer_class = StackdioGroupModelPermissionsSerializer
permission_classes = (permissions.CloudProfilePermissionsModelPermissions,)
model_cls = models.CloudProfile


class CloudProfileObjectUserPermissionsViewSet(mixins.CloudProfileRelatedMixin,
StackdioObjectUserPermissionsViewSet):
serializer_class = StackdioUserObjectPermissionsSerializer
permission_classes = (permissions.CloudProfilePermissionsObjectPermissions,)


class CloudProfileObjectGroupPermissionsViewSet(mixins.CloudProfileRelatedMixin,
StackdioObjectGroupPermissionsViewSet):
serializer_class = StackdioGroupObjectPermissionsSerializer
permission_classes = (permissions.CloudProfilePermissionsObjectPermissions,)


Expand All @@ -322,26 +308,22 @@ class SnapshotDetailAPIView(generics.RetrieveUpdateDestroyAPIView):


class SnapshotModelUserPermissionsViewSet(StackdioModelUserPermissionsViewSet):
serializer_class = StackdioUserModelPermissionsSerializer
permission_classes = (permissions.SnapshotPermissionsModelPermissions,)
model_cls = models.Snapshot


class SnapshotModelGroupPermissionsViewSet(StackdioModelGroupPermissionsViewSet):
serializer_class = StackdioGroupModelPermissionsSerializer
permission_classes = (permissions.SnapshotPermissionsModelPermissions,)
model_cls = models.Snapshot


class SnapshotObjectUserPermissionsViewSet(mixins.SnapshotRelatedMixin,
StackdioObjectUserPermissionsViewSet):
serializer_class = StackdioUserObjectPermissionsSerializer
permission_classes = (permissions.SnapshotPermissionsObjectPermissions,)


class SnapshotObjectGroupPermissionsViewSet(mixins.SnapshotRelatedMixin,
StackdioObjectGroupPermissionsViewSet):
serializer_class = StackdioGroupObjectPermissionsSerializer
permission_classes = (permissions.SnapshotPermissionsObjectPermissions,)


Expand Down
74 changes: 1 addition & 73 deletions stackdio/core/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,81 +16,9 @@
#


from django.contrib.auth import get_user_model
from rest_framework import generics, permissions, views
from rest_framework.filters import DjangoFilterBackend
from rest_framework import permissions, views
from rest_framework.response import Response

from .exceptions import BadRequest
from . import models, serializers


class UserListAPIView(generics.ListAPIView):
queryset = get_user_model().objects.exclude(username__in=('__stackdio__', 'AnonymousUser'))
serializer_class = serializers.UserSerializer
permission_classes = (permissions.IsAdminUser,)
filter_backends = (DjangoFilterBackend,)
lookup_field = 'username'


class UserDetailAPIView(generics.RetrieveAPIView):
queryset = get_user_model().objects.exclude(username__in=('__stackdio__', 'AnonymousUser'))
serializer_class = serializers.UserSerializer
permission_classes = (permissions.IsAdminUser,)
filter_backends = (DjangoFilterBackend,)
lookup_field = 'username'


class UserSettingsDetailAPIView(generics.RetrieveUpdateAPIView):
queryset = models.UserSettings.objects.all()
serializer_class = serializers.UserSettingsSerializer
permission_classes = (permissions.IsAuthenticated,)

def get_object(self):
return self.request.user.settings


class ChangePasswordAPIView(views.APIView):
"""
API that handles changing your account password. Note that
only PUT requests are available on this endpoint. Below
are the required parameters of the JSON object you will PUT.
@curent_password: Your current password.
@new_password: Your new password you want to change to.
@confirm_password: Confirm your new password.
"""
permission_classes = (permissions.IsAuthenticated,)

def put(self, request, *args, **kwargs):
current_password = request.DATA.get('current_password')
new_password = request.DATA.get('new_password')
confirm_password = request.DATA.get('confirm_password')

errors = []
if not current_password:
errors.append('Current password field is required.')
if not new_password:
errors.append('New password field is required.')
if not confirm_password:
errors.append('New password confirmation field is required.')
if errors:
raise BadRequest(dict(errors=errors))

if not request.user.check_password(current_password):
errors.append('You entered an incorrect current password value.')
if new_password != confirm_password:
errors.append('Your new password and password confirmation fields '
'do not match.')
if errors:
raise BadRequest(dict(errors=errors))

# change the password
request.user.set_password(new_password)
request.user.save()

return Response()


class VersionAPIView(views.APIView):
"""
Expand Down
2 changes: 1 addition & 1 deletion stackdio/core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class StackdioConfig(dict):
'db_dsn',
'storage_root',
'django_secret_key',
'create_ssh_users',
)

def __init__(self):
Expand Down Expand Up @@ -67,7 +68,6 @@ def _load_stackdio_config(self):
self.salt_master_config = os.path.join(self.salt_config_root, 'master')
self.salt_cloud_config = os.path.join(self.salt_config_root, 'cloud')
self.salt_core_states = os.path.join(self.storage_root, 'core_states')
self.salt_user_states = os.path.join(self.storage_root, 'user_states')
self.salt_providers_dir = os.path.join(self.salt_config_root,
'cloud.providers.d')
self.salt_profiles_dir = os.path.join(self.salt_config_root,
Expand Down
74 changes: 57 additions & 17 deletions stackdio/core/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@
# limitations under the License.
#

import logging

from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group
from django.db import models
from rest_framework.fields import Field
from rest_framework import relations

logger = logging.getLogger(__name__)


class DeletingFileField(models.FileField):
Expand Down Expand Up @@ -50,25 +51,64 @@ def delete_file(self, instance, sender, **kwargs):
file.close()


class UserField(Field):
class HyperlinkedField(relations.HyperlinkedIdentityField):
"""
Sometimes we want to have a link field that doesn't have a lookup_field.
This allows for that to happen.
"""
def get_url(self, obj, view_name, request, format):
return self.reverse(view_name, request=request, format=format)

def get_object(self, view_name, view_args, view_kwargs):
raise relations.ObjectDoesNotExist('%s should never have an associated object.'
% self.__class__.__name__)


class HyperlinkedPermissionsField(relations.HyperlinkedIdentityField):

def __init__(self, view_name, permission_lookup_field, **kwargs):
assert permission_lookup_field is not None, (
'The `permission_lookup_field` argument is required.'
)
self.permission_lookup_field = permission_lookup_field
self.permission_lookup_url_kwarg = kwargs.pop('permission_lookup_url_kwarg',
self.permission_lookup_field)
super(HyperlinkedPermissionsField, self).__init__(view_name, **kwargs)

def get_object(self, view_name, view_args, view_kwargs):
raise relations.ObjectDoesNotExist('%s is a read only field, so the object isn\'t needed.'
% self.__class__.__name__)

class HyperlinkedModelPermissionsField(HyperlinkedPermissionsField):

def get_url(self, obj, view_name, request, format):
# This is a little hack-y. Not sure if I like it.
permission_lookup_obj = obj.get('user') or obj.get('group')
permission_lookup_value = getattr(permission_lookup_obj, self.permission_lookup_field)

kwargs = {
self.permission_lookup_url_kwarg: permission_lookup_value,
}

def __init__(self, **kwargs):
super(UserField, self).__init__(**kwargs)
return self.reverse(view_name, kwargs=kwargs, request=request, format=format)

def to_internal_value(self, data):
return get_user_model().objects.get(username=data)

def to_representation(self, value):
return value.username
class HyperlinkedObjectPermissionsField(HyperlinkedPermissionsField):

def get_url(self, obj, view_name, request, format):

class GroupField(Field):
lookup_value = getattr(self.get_parent_object(), self.lookup_field)
# This is a little hack-y. Not sure if I like it.
permission_lookup_obj = obj.get('user') or obj.get('group')
permission_lookup_value = getattr(permission_lookup_obj, self.permission_lookup_field)

def __init__(self, **kwargs):
super(GroupField, self).__init__(**kwargs)
kwargs = {
self.lookup_url_kwarg: lookup_value,
self.permission_lookup_url_kwarg: permission_lookup_value,
}

def to_internal_value(self, data):
return Group.objects.get(name=data)
return self.reverse(view_name, kwargs=kwargs, request=request, format=format)

def to_representation(self, value):
return value.name
def get_parent_object(self):
view = self.context['view']
return view.get_permissioned_object()

0 comments on commit b60983b

Please sign in to comment.