mirror of
https://github.com/ansible/awx.git
synced 2026-05-17 22:37:41 -02:30
Merge pull request #1435 from anoek/user-activity-stream-updates
Added activity stream events for User
This commit is contained in:
@@ -3,9 +3,11 @@
|
|||||||
|
|
||||||
# Python
|
# Python
|
||||||
import urllib
|
import urllib
|
||||||
|
import logging
|
||||||
|
|
||||||
# Django
|
# Django
|
||||||
from django.utils.timezone import now as tz_now
|
from django.utils.timezone import now as tz_now
|
||||||
|
from django.utils.encoding import smart_text
|
||||||
|
|
||||||
# Django REST Framework
|
# Django REST Framework
|
||||||
from rest_framework import authentication
|
from rest_framework import authentication
|
||||||
@@ -16,6 +18,8 @@ from rest_framework import HTTP_HEADER_ENCODING
|
|||||||
from awx.main.models import UnifiedJob, AuthToken
|
from awx.main.models import UnifiedJob, AuthToken
|
||||||
from awx.main.conf import tower_settings
|
from awx.main.conf import tower_settings
|
||||||
|
|
||||||
|
logger = logging.getLogger('awx.api.authentication')
|
||||||
|
|
||||||
class TokenAuthentication(authentication.TokenAuthentication):
|
class TokenAuthentication(authentication.TokenAuthentication):
|
||||||
'''
|
'''
|
||||||
Custom token authentication using tokens that expire and are associated
|
Custom token authentication using tokens that expire and are associated
|
||||||
@@ -93,7 +97,7 @@ class TokenAuthentication(authentication.TokenAuthentication):
|
|||||||
if not token.in_valid_tokens(now=now):
|
if not token.in_valid_tokens(now=now):
|
||||||
token.invalidate(reason='limit_reached')
|
token.invalidate(reason='limit_reached')
|
||||||
raise exceptions.AuthenticationFailed(AuthToken.reason_long('limit_reached'))
|
raise exceptions.AuthenticationFailed(AuthToken.reason_long('limit_reached'))
|
||||||
|
|
||||||
# If the user is inactive, then return an error.
|
# If the user is inactive, then return an error.
|
||||||
if not token.user.is_active:
|
if not token.user.is_active:
|
||||||
raise exceptions.AuthenticationFailed('User inactive or deleted')
|
raise exceptions.AuthenticationFailed('User inactive or deleted')
|
||||||
@@ -116,6 +120,16 @@ class TokenGetAuthentication(TokenAuthentication):
|
|||||||
return super(TokenGetAuthentication, self).authenticate(request)
|
return super(TokenGetAuthentication, self).authenticate(request)
|
||||||
|
|
||||||
|
|
||||||
|
class LoggedBasicAuthentication(authentication.BasicAuthentication):
|
||||||
|
|
||||||
|
def authenticate(self, request):
|
||||||
|
ret = super(LoggedBasicAuthentication, self).authenticate(request)
|
||||||
|
if ret:
|
||||||
|
username = ret[0].username if ret[0] else '<none>'
|
||||||
|
logger.debug(smart_text(u"User {} performed a {} to {} through the API".format(username, request.method, request.path)))
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
class TaskAuthentication(authentication.BaseAuthentication):
|
class TaskAuthentication(authentication.BaseAuthentication):
|
||||||
'''
|
'''
|
||||||
Custom authentication used for views accessed by the inventory and callback
|
Custom authentication used for views accessed by the inventory and callback
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import time
|
|||||||
import socket
|
import socket
|
||||||
import sys
|
import sys
|
||||||
import errno
|
import errno
|
||||||
|
import logging
|
||||||
from base64 import b64encode
|
from base64 import b64encode
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
@@ -22,7 +23,7 @@ from django.core.exceptions import FieldError
|
|||||||
from django.db.models import Q, Count
|
from django.db.models import Q, Count
|
||||||
from django.db import IntegrityError, transaction
|
from django.db import IntegrityError, transaction
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import smart_text, force_text
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
@@ -71,6 +72,8 @@ from awx.api.metadata import RoleMetadata
|
|||||||
from awx.main.utils import emit_websocket_notification
|
from awx.main.utils import emit_websocket_notification
|
||||||
from awx.main.conf import tower_settings
|
from awx.main.conf import tower_settings
|
||||||
|
|
||||||
|
logger = logging.getLogger('awx.api.views')
|
||||||
|
|
||||||
def api_exception_handler(exc, context):
|
def api_exception_handler(exc, context):
|
||||||
'''
|
'''
|
||||||
Override default API exception handler to catch IntegrityError exceptions.
|
Override default API exception handler to catch IntegrityError exceptions.
|
||||||
@@ -528,9 +531,13 @@ class AuthTokenView(APIView):
|
|||||||
expires__gt=now(),
|
expires__gt=now(),
|
||||||
reason='')[0]
|
reason='')[0]
|
||||||
token.refresh()
|
token.refresh()
|
||||||
|
if 'username' in request.data:
|
||||||
|
logger.info(smart_text(u"User {} logged in".format(request.data['username'])))
|
||||||
except IndexError:
|
except IndexError:
|
||||||
token = AuthToken.objects.create(user=serializer.validated_data['user'],
|
token = AuthToken.objects.create(user=serializer.validated_data['user'],
|
||||||
request_hash=request_hash)
|
request_hash=request_hash)
|
||||||
|
if 'username' in request.data:
|
||||||
|
logger.info(smart_text(u"User {} logged in".format(request.data['username'])))
|
||||||
# Get user un-expired tokens that are not invalidated that are
|
# Get user un-expired tokens that are not invalidated that are
|
||||||
# over the configured limit.
|
# over the configured limit.
|
||||||
# Mark them as invalid and inform the user
|
# Mark them as invalid and inform the user
|
||||||
@@ -549,6 +556,8 @@ class AuthTokenView(APIView):
|
|||||||
'Auth-Token-Timeout': int(tower_settings.AUTH_TOKEN_EXPIRATION)
|
'Auth-Token-Timeout': int(tower_settings.AUTH_TOKEN_EXPIRATION)
|
||||||
}
|
}
|
||||||
return Response({'token': token.key, 'expires': token.expires}, headers=headers)
|
return Response({'token': token.key, 'expires': token.expires}, headers=headers)
|
||||||
|
if 'username' in request.data:
|
||||||
|
logger.warning(smart_text(u"Login failed for user {}".format(request.data['username'])))
|
||||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
class OrganizationList(ListCreateAPIView):
|
class OrganizationList(ListCreateAPIView):
|
||||||
|
|||||||
@@ -85,3 +85,4 @@ activity_stream_registrar.connect(TowerSettings)
|
|||||||
activity_stream_registrar.connect(Notifier)
|
activity_stream_registrar.connect(Notifier)
|
||||||
activity_stream_registrar.connect(Notification)
|
activity_stream_registrar.connect(Notification)
|
||||||
activity_stream_registrar.connect(Label)
|
activity_stream_registrar.connect(Label)
|
||||||
|
activity_stream_registrar.connect(User)
|
||||||
|
|||||||
@@ -284,6 +284,22 @@ def update_scm_url(scm_type, url, username=True, password=True,
|
|||||||
return new_url
|
return new_url
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def get_allowed_fields(obj, serializer_mapping):
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
|
if serializer_mapping is not None and obj.__class__ in serializer_mapping:
|
||||||
|
serializer_actual = serializer_mapping[obj.__class__]()
|
||||||
|
allowed_fields = [x for x in serializer_actual.fields if not serializer_actual.fields[x].read_only] + ['id']
|
||||||
|
else:
|
||||||
|
allowed_fields = [x.name for x in obj._meta.fields]
|
||||||
|
|
||||||
|
if isinstance(obj, User):
|
||||||
|
field_blacklist = ['last_login']
|
||||||
|
allowed_fields = [f for f in allowed_fields if f not in field_blacklist]
|
||||||
|
|
||||||
|
return allowed_fields
|
||||||
|
|
||||||
def model_instance_diff(old, new, serializer_mapping=None):
|
def model_instance_diff(old, new, serializer_mapping=None):
|
||||||
"""
|
"""
|
||||||
Calculate the differences between two model instances. One of the instances may be None (i.e., a newly
|
Calculate the differences between two model instances. One of the instances may be None (i.e., a newly
|
||||||
@@ -301,11 +317,7 @@ def model_instance_diff(old, new, serializer_mapping=None):
|
|||||||
|
|
||||||
diff = {}
|
diff = {}
|
||||||
|
|
||||||
if serializer_mapping is not None and new.__class__ in serializer_mapping:
|
allowed_fields = get_allowed_fields(new, serializer_mapping)
|
||||||
serializer_actual = serializer_mapping[new.__class__]()
|
|
||||||
allowed_fields = [x for x in serializer_actual.fields if not serializer_actual.fields[x].read_only] + ['id']
|
|
||||||
else:
|
|
||||||
allowed_fields = [x.name for x in new._meta.fields]
|
|
||||||
|
|
||||||
for field in allowed_fields:
|
for field in allowed_fields:
|
||||||
old_value = getattr(old, field, None)
|
old_value = getattr(old, field, None)
|
||||||
@@ -334,11 +346,9 @@ def model_to_dict(obj, serializer_mapping=None):
|
|||||||
"""
|
"""
|
||||||
from awx.main.models.credential import Credential
|
from awx.main.models.credential import Credential
|
||||||
attr_d = {}
|
attr_d = {}
|
||||||
if serializer_mapping is not None and obj.__class__ in serializer_mapping:
|
|
||||||
serializer_actual = serializer_mapping[obj.__class__]()
|
allowed_fields = get_allowed_fields(obj, serializer_mapping)
|
||||||
allowed_fields = [x for x in serializer_actual.fields if not serializer_actual.fields[x].read_only] + ['id']
|
|
||||||
else:
|
|
||||||
allowed_fields = [x.name for x in obj._meta.fields]
|
|
||||||
for field in obj._meta.fields:
|
for field in obj._meta.fields:
|
||||||
if field.name not in allowed_fields:
|
if field.name not in allowed_fields:
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -202,7 +202,7 @@ REST_FRAMEWORK = {
|
|||||||
'PAGE_SIZE': 25,
|
'PAGE_SIZE': 25,
|
||||||
'DEFAULT_AUTHENTICATION_CLASSES': (
|
'DEFAULT_AUTHENTICATION_CLASSES': (
|
||||||
'awx.api.authentication.TokenAuthentication',
|
'awx.api.authentication.TokenAuthentication',
|
||||||
'rest_framework.authentication.BasicAuthentication',
|
'awx.api.authentication.LoggedBasicAuthentication',
|
||||||
#'rest_framework.authentication.SessionAuthentication',
|
#'rest_framework.authentication.SessionAuthentication',
|
||||||
),
|
),
|
||||||
'DEFAULT_PERMISSION_CLASSES': (
|
'DEFAULT_PERMISSION_CLASSES': (
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
# Python
|
# Python
|
||||||
import urllib
|
import urllib
|
||||||
|
import logging
|
||||||
|
|
||||||
# Django
|
# Django
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
@@ -10,6 +11,7 @@ from django.http import HttpResponse
|
|||||||
from django.utils.timezone import now, utc
|
from django.utils.timezone import now, utc
|
||||||
from django.views.generic import View
|
from django.views.generic import View
|
||||||
from django.views.generic.base import RedirectView
|
from django.views.generic.base import RedirectView
|
||||||
|
from django.utils.encoding import smart_text
|
||||||
|
|
||||||
# Django REST Framework
|
# Django REST Framework
|
||||||
from rest_framework.renderers import JSONRenderer
|
from rest_framework.renderers import JSONRenderer
|
||||||
@@ -18,6 +20,7 @@ from rest_framework.renderers import JSONRenderer
|
|||||||
from awx.main.models import AuthToken
|
from awx.main.models import AuthToken
|
||||||
from awx.api.serializers import UserSerializer
|
from awx.api.serializers import UserSerializer
|
||||||
|
|
||||||
|
logger = logging.getLogger('awx.sso.views')
|
||||||
|
|
||||||
class BaseRedirectView(RedirectView):
|
class BaseRedirectView(RedirectView):
|
||||||
|
|
||||||
@@ -45,9 +48,11 @@ class CompleteView(BaseRedirectView):
|
|||||||
request_hash=request_hash,
|
request_hash=request_hash,
|
||||||
expires__gt=now())[0]
|
expires__gt=now())[0]
|
||||||
token.refresh()
|
token.refresh()
|
||||||
|
logger.info(smart_text(u"User {} logged in".format(self.request.user.username)))
|
||||||
except IndexError:
|
except IndexError:
|
||||||
token = AuthToken.objects.create(user=request.user,
|
token = AuthToken.objects.create(user=request.user,
|
||||||
request_hash=request_hash)
|
request_hash=request_hash)
|
||||||
|
logger.info(smart_text(u"User {} logged in".format(self.request.user.username)))
|
||||||
request.session['auth_token_key'] = token.key
|
request.session['auth_token_key'] = token.key
|
||||||
token_key = urllib.quote('"%s"' % token.key)
|
token_key = urllib.quote('"%s"' % token.key)
|
||||||
response.set_cookie('token', token_key)
|
response.set_cookie('token', token_key)
|
||||||
|
|||||||
Reference in New Issue
Block a user