mirror of https://github.com/jumpserver/jumpserver
commit
d6debde566
@ -1,7 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
|
||||
from .backends import *
|
||||
from .middleware import *
|
||||
from .utils import *
|
||||
from .decorator import *
|
@ -1,82 +0,0 @@
|
||||
# coding:utf-8
|
||||
#
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.conf import settings
|
||||
|
||||
from common.utils import get_logger
|
||||
from .utils import new_client
|
||||
from .models import OIDT_ACCESS_TOKEN
|
||||
|
||||
UserModel = get_user_model()
|
||||
|
||||
logger = get_logger(__file__)
|
||||
client = new_client()
|
||||
|
||||
|
||||
__all__ = [
|
||||
'OpenIDAuthorizationCodeBackend', 'OpenIDAuthorizationPasswordBackend',
|
||||
]
|
||||
|
||||
|
||||
class BaseOpenIDAuthorizationBackend(object):
|
||||
@staticmethod
|
||||
def user_can_authenticate(user):
|
||||
"""
|
||||
Reject users with is_active=False. Custom user models that don't have
|
||||
that attribute are allowed.
|
||||
"""
|
||||
is_valid = getattr(user, 'is_valid', None)
|
||||
return is_valid or is_valid is None
|
||||
|
||||
def get_user(self, user_id):
|
||||
try:
|
||||
user = UserModel._default_manager.get(pk=user_id)
|
||||
except UserModel.DoesNotExist:
|
||||
return None
|
||||
|
||||
return user if self.user_can_authenticate(user) else None
|
||||
|
||||
|
||||
class OpenIDAuthorizationCodeBackend(BaseOpenIDAuthorizationBackend):
|
||||
def authenticate(self, request, **kwargs):
|
||||
logger.info('Authentication OpenID code backend')
|
||||
code = kwargs.get('code')
|
||||
redirect_uri = kwargs.get('redirect_uri')
|
||||
if not code or not redirect_uri:
|
||||
logger.info('Authenticate failed: No code or No redirect uri')
|
||||
return None
|
||||
try:
|
||||
oidt_profile = client.update_or_create_from_code(
|
||||
code=code, redirect_uri=redirect_uri
|
||||
)
|
||||
except Exception as e:
|
||||
logger.info('Authenticate failed: get oidt_profile: {}'.format(e))
|
||||
return None
|
||||
else:
|
||||
# Check openid user single logout or not with access_token
|
||||
request.session[OIDT_ACCESS_TOKEN] = oidt_profile.access_token
|
||||
user = oidt_profile.user
|
||||
logger.info('Authenticate success: user -> {}'.format(user))
|
||||
return user if self.user_can_authenticate(user) else None
|
||||
|
||||
|
||||
class OpenIDAuthorizationPasswordBackend(BaseOpenIDAuthorizationBackend):
|
||||
def authenticate(self, request, username=None, password=None, **kwargs):
|
||||
logger.info('Authentication OpenID password backend')
|
||||
if not username:
|
||||
logger.info('Authenticate failed: Not username')
|
||||
return None
|
||||
try:
|
||||
oidt_profile = client.update_or_create_from_password(
|
||||
username=username, password=password
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(e, exc_info=True)
|
||||
logger.info('Authenticate failed: get oidt_profile: {}'.format(e))
|
||||
return None
|
||||
else:
|
||||
user = oidt_profile.user
|
||||
logger.info('Authenticate success: user -> {}'.format(user))
|
||||
return user if self.user_can_authenticate(user) else None
|
||||
|
@ -1,57 +0,0 @@
|
||||
# coding: utf-8
|
||||
#
|
||||
|
||||
import warnings
|
||||
import contextlib
|
||||
|
||||
import requests
|
||||
from urllib3.exceptions import InsecureRequestWarning
|
||||
from django.conf import settings
|
||||
|
||||
__all__ = [
|
||||
'ssl_verification',
|
||||
]
|
||||
|
||||
old_merge_environment_settings = requests.Session.merge_environment_settings
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def no_ssl_verification():
|
||||
"""
|
||||
https://stackoverflow.com/questions/15445981/
|
||||
how-do-i-disable-the-security-certificate-check-in-python-requests
|
||||
"""
|
||||
opened_adapters = set()
|
||||
|
||||
def merge_environment_settings(self, url, proxies, stream, verify, cert):
|
||||
# Verification happens only once per connection so we need to close
|
||||
# all the opened adapters once we're done. Otherwise, the effects of
|
||||
# verify=False persist beyond the end of this context manager.
|
||||
opened_adapters.add(self.get_adapter(url))
|
||||
_settings = old_merge_environment_settings(
|
||||
self, url, proxies, stream, verify, cert
|
||||
)
|
||||
_settings['verify'] = False
|
||||
return _settings
|
||||
|
||||
requests.Session.merge_environment_settings = merge_environment_settings
|
||||
try:
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter('ignore', InsecureRequestWarning)
|
||||
yield
|
||||
finally:
|
||||
requests.Session.merge_environment_settings = old_merge_environment_settings
|
||||
for adapter in opened_adapters:
|
||||
try:
|
||||
adapter.close()
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
def ssl_verification(func):
|
||||
def wrapper(*args, **kwargs):
|
||||
if not settings.AUTH_OPENID_IGNORE_SSL_VERIFICATION:
|
||||
return func(*args, **kwargs)
|
||||
with no_ssl_verification():
|
||||
return func(*args, **kwargs)
|
||||
return wrapper
|
@ -1,41 +0,0 @@
|
||||
# coding:utf-8
|
||||
#
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth import logout
|
||||
from django.utils.deprecation import MiddlewareMixin
|
||||
from django.contrib.auth import BACKEND_SESSION_KEY
|
||||
|
||||
from common.utils import get_logger
|
||||
from .utils import new_client
|
||||
from .models import OIDT_ACCESS_TOKEN
|
||||
|
||||
BACKEND_OPENID_AUTH_CODE = 'OpenIDAuthorizationCodeBackend'
|
||||
logger = get_logger(__file__)
|
||||
__all__ = ['OpenIDAuthenticationMiddleware']
|
||||
|
||||
|
||||
class OpenIDAuthenticationMiddleware(MiddlewareMixin):
|
||||
"""
|
||||
Check openid user single logout (with access_token)
|
||||
"""
|
||||
def process_request(self, request):
|
||||
# Don't need openid auth if AUTH_OPENID is False
|
||||
if not settings.AUTH_OPENID:
|
||||
return
|
||||
# Don't need openid auth if no shared session enabled
|
||||
if not settings.AUTH_OPENID_SHARE_SESSION:
|
||||
return
|
||||
# Don't need check single logout if user not authenticated
|
||||
if not request.user.is_authenticated:
|
||||
return
|
||||
elif not request.session[BACKEND_SESSION_KEY].endswith(
|
||||
BACKEND_OPENID_AUTH_CODE):
|
||||
return
|
||||
# Check openid user single logout or not with access_token
|
||||
try:
|
||||
client = new_client()
|
||||
client.get_userinfo(token=request.session.get(OIDT_ACCESS_TOKEN))
|
||||
except Exception as e:
|
||||
logout(request)
|
||||
logger.error(e)
|
@ -1,185 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
|
||||
from django.db import transaction
|
||||
from django.contrib.auth import get_user_model
|
||||
from keycloak.realm import KeycloakRealm
|
||||
from keycloak.keycloak_openid import KeycloakOpenID
|
||||
from users.utils import construct_user_email
|
||||
|
||||
from .signals import post_create_or_update_openid_user
|
||||
from .decorator import ssl_verification
|
||||
|
||||
OIDT_ACCESS_TOKEN = 'oidt_access_token'
|
||||
|
||||
|
||||
class Nonce(object):
|
||||
"""
|
||||
The openid-login is stored in cache as a temporary object, recording the
|
||||
user's redirect_uri and next_pat
|
||||
"""
|
||||
def __init__(self, redirect_uri, next_path):
|
||||
import uuid
|
||||
self.state = uuid.uuid4()
|
||||
self.redirect_uri = redirect_uri
|
||||
self.next_path = next_path
|
||||
|
||||
|
||||
class OpenIDTokenProfile(object):
|
||||
def __init__(self, user, access_token, refresh_token):
|
||||
"""
|
||||
:param user: User object
|
||||
:param access_token:
|
||||
:param refresh_token:
|
||||
"""
|
||||
self.user = user
|
||||
self.access_token = access_token
|
||||
self.refresh_token = refresh_token
|
||||
|
||||
def __str__(self):
|
||||
return "{}'s OpenID token profile".format(self.user.username)
|
||||
|
||||
|
||||
class Client(object):
|
||||
def __init__(self, server_url, realm_name, client_id, client_secret):
|
||||
self.server_url = server_url
|
||||
self.realm_name = realm_name
|
||||
self.client_id = client_id
|
||||
self.client_secret = client_secret
|
||||
self._openid_client = None
|
||||
self._realm = None
|
||||
self._openid_connect_client = None
|
||||
|
||||
@property
|
||||
def realm(self):
|
||||
if self._realm is None:
|
||||
self._realm = KeycloakRealm(
|
||||
server_url=self.server_url,
|
||||
realm_name=self.realm_name,
|
||||
headers={}
|
||||
)
|
||||
return self._realm
|
||||
|
||||
@property
|
||||
def openid_connect_client(self):
|
||||
"""
|
||||
:rtype: keycloak.openid_connect.KeycloakOpenidConnect
|
||||
"""
|
||||
if self._openid_connect_client is None:
|
||||
self._openid_connect_client = self.realm.open_id_connect(
|
||||
client_id=self.client_id,
|
||||
client_secret=self.client_secret
|
||||
)
|
||||
return self._openid_connect_client
|
||||
|
||||
@property
|
||||
def openid_client(self):
|
||||
"""
|
||||
:rtype: keycloak.keycloak_openid.KeycloakOpenID
|
||||
"""
|
||||
if self._openid_client is None:
|
||||
self._openid_client = KeycloakOpenID(
|
||||
server_url='%sauth/' % self.server_url,
|
||||
realm_name=self.realm_name,
|
||||
client_id=self.client_id,
|
||||
client_secret_key=self.client_secret,
|
||||
)
|
||||
return self._openid_client
|
||||
|
||||
@ssl_verification
|
||||
def get_url(self, name):
|
||||
return self.openid_connect_client.get_url(name=name)
|
||||
|
||||
def get_url_end_session_endpoint(self):
|
||||
return self.get_url(name='end_session_endpoint')
|
||||
|
||||
@ssl_verification
|
||||
def get_authorization_url(self, redirect_uri, scope, state):
|
||||
url = self.openid_connect_client.authorization_url(
|
||||
redirect_uri=redirect_uri, scope=scope, state=state
|
||||
)
|
||||
return url
|
||||
|
||||
@ssl_verification
|
||||
def get_userinfo(self, token):
|
||||
user_info = self.openid_connect_client.userinfo(token=token)
|
||||
return user_info
|
||||
|
||||
@ssl_verification
|
||||
def authorization_code(self, code, redirect_uri):
|
||||
token_response = self.openid_connect_client.authorization_code(
|
||||
code=code, redirect_uri=redirect_uri
|
||||
)
|
||||
return token_response
|
||||
|
||||
@ssl_verification
|
||||
def authorization_password(self, username, password):
|
||||
token_response = self.openid_client.token(
|
||||
username=username, password=password
|
||||
)
|
||||
return token_response
|
||||
|
||||
def update_or_create_from_code(self, code, redirect_uri):
|
||||
"""
|
||||
Update or create an user based on an authentication code.
|
||||
Response as specified in:
|
||||
https://tools.ietf.org/html/rfc6749#section-4.1.4
|
||||
:param str code: authentication code
|
||||
:param str redirect_uri:
|
||||
:rtype: OpenIDTokenProfile
|
||||
"""
|
||||
token_response = self.authorization_code(code, redirect_uri)
|
||||
return self._update_or_create(token_response=token_response)
|
||||
|
||||
def update_or_create_from_password(self, username, password):
|
||||
"""
|
||||
Update or create an user based on an authentication username and password.
|
||||
:param str username: authentication username
|
||||
:param str password: authentication password
|
||||
:return: OpenIDTokenProfile
|
||||
"""
|
||||
token_response = self.authorization_password(username, password)
|
||||
return self._update_or_create(token_response=token_response)
|
||||
|
||||
def _update_or_create(self, token_response):
|
||||
"""
|
||||
Update or create an user based on a token response.
|
||||
`token_response` contains the items returned by the OpenIDConnect Token API
|
||||
end-point:
|
||||
- id_token
|
||||
- access_token
|
||||
- expires_in
|
||||
- refresh_token
|
||||
- refresh_expires_in
|
||||
:param dict token_response:
|
||||
:rtype: OpenIDTokenProfile
|
||||
"""
|
||||
userinfo = self.get_userinfo(token=token_response['access_token'])
|
||||
with transaction.atomic():
|
||||
name = userinfo.get('name', '')
|
||||
username = userinfo.get('preferred_username', '')
|
||||
email = userinfo.get('email', '')
|
||||
email = construct_user_email(username, email)
|
||||
|
||||
user, created = get_user_model().objects.update_or_create(
|
||||
username=username,
|
||||
defaults={
|
||||
'name': name, 'email': email,
|
||||
'first_name': userinfo.get('given_name', ''),
|
||||
'last_name': userinfo.get('family_name', ''),
|
||||
}
|
||||
)
|
||||
oidt_profile = OpenIDTokenProfile(
|
||||
user=user,
|
||||
access_token=token_response['access_token'],
|
||||
refresh_token=token_response['refresh_token'],
|
||||
)
|
||||
if user:
|
||||
post_create_or_update_openid_user.send(
|
||||
sender=user.__class__, user=user, created=created
|
||||
)
|
||||
|
||||
return oidt_profile
|
||||
|
||||
def __str__(self):
|
||||
return self.client_id
|
@ -1,5 +0,0 @@
|
||||
from django.dispatch import Signal
|
||||
|
||||
|
||||
post_create_or_update_openid_user = Signal(providing_args=('user',))
|
||||
post_openid_login_success = Signal(providing_args=('user', 'request'))
|
@ -1,11 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
from django.urls import path
|
||||
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path('login/', views.OpenIDLoginView.as_view(), name='openid-login'),
|
||||
path('login/complete/', views.OpenIDLoginCompleteView.as_view(),
|
||||
name='openid-login-complete'),
|
||||
]
|
@ -1,19 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
|
||||
from django.conf import settings
|
||||
from .models import Client
|
||||
|
||||
__all__ = ['new_client']
|
||||
|
||||
|
||||
def new_client():
|
||||
"""
|
||||
:return: authentication.models.Client
|
||||
"""
|
||||
return Client(
|
||||
server_url=settings.AUTH_OPENID_SERVER_URL,
|
||||
realm_name=settings.AUTH_OPENID_REALM_NAME,
|
||||
client_id=settings.AUTH_OPENID_CLIENT_ID,
|
||||
client_secret=settings.AUTH_OPENID_CLIENT_SECRET
|
||||
)
|
@ -1,71 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
|
||||
import logging
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.cache import cache
|
||||
from django.views.generic.base import RedirectView
|
||||
from django.contrib.auth import authenticate, login
|
||||
from django.http.response import (
|
||||
HttpResponseBadRequest,
|
||||
HttpResponseServerError,
|
||||
HttpResponseRedirect
|
||||
)
|
||||
|
||||
from .utils import new_client
|
||||
from .models import Nonce
|
||||
from .signals import post_openid_login_success
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
client = new_client()
|
||||
|
||||
__all__ = ['OpenIDLoginView', 'OpenIDLoginCompleteView']
|
||||
|
||||
|
||||
class OpenIDLoginView(RedirectView):
|
||||
def get_redirect_url(self, *args, **kwargs):
|
||||
redirect_uri = settings.BASE_SITE_URL + \
|
||||
str(settings.AUTH_OPENID_LOGIN_COMPLETE_URL)
|
||||
nonce = Nonce(
|
||||
redirect_uri=redirect_uri,
|
||||
next_path=self.request.GET.get('next')
|
||||
)
|
||||
cache.set(str(nonce.state), nonce, 24*3600)
|
||||
|
||||
self.request.session['openid_state'] = str(nonce.state)
|
||||
authorization_url = client.get_authorization_url(
|
||||
redirect_uri=nonce.redirect_uri,
|
||||
scope='code',
|
||||
state=str(nonce.state)
|
||||
)
|
||||
return authorization_url
|
||||
|
||||
|
||||
class OpenIDLoginCompleteView(RedirectView):
|
||||
def get(self, request, *args, **kwargs):
|
||||
if 'error' in request.GET:
|
||||
return HttpResponseServerError(self.request.GET['error'])
|
||||
if 'code' not in self.request.GET and 'state' not in self.request.GET:
|
||||
return HttpResponseBadRequest(content='Code or State is empty')
|
||||
if self.request.GET['state'] != self.request.session['openid_state']:
|
||||
return HttpResponseBadRequest(content='State invalid')
|
||||
nonce = cache.get(self.request.GET['state'])
|
||||
if not nonce:
|
||||
return HttpResponseBadRequest(content='State failure')
|
||||
|
||||
user = authenticate(
|
||||
request=self.request,
|
||||
code=self.request.GET['code'],
|
||||
redirect_uri=nonce.redirect_uri
|
||||
)
|
||||
cache.delete(str(nonce.state))
|
||||
if not user:
|
||||
return HttpResponseBadRequest(content='Authenticate user failed')
|
||||
|
||||
login(self.request, user)
|
||||
post_openid_login_success.send(
|
||||
sender=self.__class__, user=user, request=self.request
|
||||
)
|
||||
return HttpResponseRedirect(nonce.next_path or '/')
|
||||
|
@ -1,54 +1,15 @@
|
||||
from django.http.request import QueryDict
|
||||
from django.conf import settings
|
||||
from django.dispatch import receiver
|
||||
from django.contrib.auth.signals import user_logged_out
|
||||
from django_auth_ldap.backend import populate_user
|
||||
|
||||
from users.models import User
|
||||
from .backends.openid import new_client
|
||||
from .backends.openid.signals import (
|
||||
post_create_or_update_openid_user, post_openid_login_success
|
||||
)
|
||||
from .signals import post_auth_success
|
||||
from jms_oidc_rp.signals import openid_user_login_failed, openid_user_login_success
|
||||
|
||||
from .signals import post_auth_success, post_auth_failed
|
||||
|
||||
@receiver(user_logged_out)
|
||||
def on_user_logged_out(sender, request, user, **kwargs):
|
||||
if not settings.AUTH_OPENID:
|
||||
return
|
||||
if not settings.AUTH_OPENID_SHARE_SESSION:
|
||||
return
|
||||
query = QueryDict('', mutable=True)
|
||||
query.update({
|
||||
'redirect_uri': settings.BASE_SITE_URL
|
||||
})
|
||||
client = new_client()
|
||||
openid_logout_url = "%s?%s" % (
|
||||
client.get_url_end_session_endpoint(),
|
||||
query.urlencode()
|
||||
)
|
||||
request.COOKIES['next'] = openid_logout_url
|
||||
|
||||
|
||||
@receiver(post_create_or_update_openid_user)
|
||||
def on_post_create_or_update_openid_user(sender, user=None, created=True, **kwargs):
|
||||
if created and user and user.username != 'admin':
|
||||
user.source = user.SOURCE_OPENID
|
||||
user.save()
|
||||
|
||||
|
||||
@receiver(post_openid_login_success)
|
||||
def on_openid_login_success(sender, user=None, request=None, **kwargs):
|
||||
post_auth_success.send(sender=sender, user=user, request=request)
|
||||
|
||||
|
||||
@receiver(populate_user)
|
||||
def on_ldap_create_user(sender, user, ldap_user, **kwargs):
|
||||
if user and user.username not in ['admin']:
|
||||
exists = User.objects.filter(username=user.username).exists()
|
||||
if not exists:
|
||||
user.source = user.SOURCE_LDAP
|
||||
user.save()
|
||||
|
||||
@receiver(openid_user_login_success)
|
||||
def on_oidc_user_login_success(sender, request, user, **kwargs):
|
||||
post_auth_success.send(sender, user=user, request=request)
|
||||
|
||||
|
||||
@receiver(openid_user_login_failed)
|
||||
def on_oidc_user_login_failed(sender, username, request, reason, **kwargs):
|
||||
post_auth_failed.send(sender, username=username, request=request, reason=reason)
|
||||
|
@ -0,0 +1,295 @@
|
||||
from django.core.cache import cache
|
||||
from django.utils import timezone
|
||||
from django.utils.timesince import timesince
|
||||
from django.db.models import Count, Max
|
||||
from django.http.response import JsonResponse
|
||||
from rest_framework.views import APIView
|
||||
from collections import Counter
|
||||
|
||||
from users.models import User
|
||||
from assets.models import Asset
|
||||
from terminal.models import Session
|
||||
from orgs.utils import current_org
|
||||
from common.permissions import IsOrgAdmin
|
||||
from common.utils import lazyproperty
|
||||
|
||||
__all__ = ['IndexApi']
|
||||
|
||||
|
||||
class MonthLoginMetricMixin:
|
||||
|
||||
@lazyproperty
|
||||
def session_month(self):
|
||||
month_ago = timezone.now() - timezone.timedelta(days=30)
|
||||
session_month = Session.objects.filter(date_start__gt=month_ago)
|
||||
return session_month
|
||||
|
||||
@lazyproperty
|
||||
def session_month_dates(self):
|
||||
dates = self.session_month.dates('date_start', 'day')
|
||||
return dates
|
||||
|
||||
def get_month_metrics_date(self):
|
||||
month_metrics_date = [d.strftime('%m-%d') for d in self.session_month_dates] or ['0']
|
||||
return month_metrics_date
|
||||
|
||||
@staticmethod
|
||||
def get_cache_key(date, tp):
|
||||
date_str = date.strftime("%Y%m%d")
|
||||
key = "SESSION_MONTH_{}_{}_{}".format(current_org.id, tp, date_str)
|
||||
return key
|
||||
|
||||
def __get_data_from_cache(self, date, tp):
|
||||
if date == timezone.now().date():
|
||||
return None
|
||||
cache_key = self.get_cache_key(date, tp)
|
||||
count = cache.get(cache_key)
|
||||
return count
|
||||
|
||||
def __set_data_to_cache(self, date, tp, count):
|
||||
cache_key = self.get_cache_key(date, tp)
|
||||
cache.set(cache_key, count, 3600*24*7)
|
||||
|
||||
@staticmethod
|
||||
def get_date_start_2_end(d):
|
||||
time_min = timezone.datetime.min.time()
|
||||
time_max = timezone.datetime.max.time()
|
||||
tz = timezone.get_current_timezone()
|
||||
ds = timezone.datetime.combine(d, time_min).replace(tzinfo=tz)
|
||||
de = timezone.datetime.combine(d, time_max).replace(tzinfo=tz)
|
||||
return ds, de
|
||||
|
||||
def get_date_login_count(self, date):
|
||||
tp = "LOGIN"
|
||||
count = self.__get_data_from_cache(date, tp)
|
||||
if count is not None:
|
||||
return count
|
||||
ds, de = self.get_date_start_2_end(date)
|
||||
count = Session.objects.filter(date_start__range=(ds, de)).count()
|
||||
self.__set_data_to_cache(date, tp, count)
|
||||
return count
|
||||
|
||||
def get_month_metrics_total_count_login(self):
|
||||
data = []
|
||||
for d in self.session_month_dates:
|
||||
count = self.get_date_login_count(d)
|
||||
data.append(count)
|
||||
if len(data) == 0:
|
||||
data = [0]
|
||||
return data
|
||||
|
||||
def get_date_user_count(self, date):
|
||||
tp = "USER"
|
||||
count = self.__get_data_from_cache(date, tp)
|
||||
if count is not None:
|
||||
return count
|
||||
ds, de = self.get_date_start_2_end(date)
|
||||
count = len(set(Session.objects.filter(date_start__range=(ds, de)).values_list('user', flat=True)))
|
||||
self.__set_data_to_cache(date, tp, count)
|
||||
return count
|
||||
|
||||
def get_month_metrics_total_count_active_users(self):
|
||||
data = []
|
||||
for d in self.session_month_dates:
|
||||
count = self.get_date_user_count(d)
|
||||
data.append(count)
|
||||
return data
|
||||
|
||||
def get_date_asset_count(self, date):
|
||||
tp = "ASSET"
|
||||
count = self.__get_data_from_cache(date, tp)
|
||||
if count is not None:
|
||||
return count
|
||||
ds, de = self.get_date_start_2_end(date)
|
||||
count = len(set(Session.objects.filter(date_start__range=(ds, de)).values_list('asset', flat=True)))
|
||||
self.__set_data_to_cache(date, tp, count)
|
||||
return count
|
||||
|
||||
def get_month_metrics_total_count_active_assets(self):
|
||||
data = []
|
||||
for d in self.session_month_dates:
|
||||
count = self.get_date_asset_count(d)
|
||||
data.append(count)
|
||||
return data
|
||||
|
||||
@lazyproperty
|
||||
def month_total_count_active_users(self):
|
||||
count = len(set(self.session_month.values_list('user', flat=True)))
|
||||
return count
|
||||
|
||||
@lazyproperty
|
||||
def month_total_count_inactive_users(self):
|
||||
total = current_org.get_org_members().count()
|
||||
active = self.month_total_count_active_users
|
||||
count = total - active
|
||||
if count < 0:
|
||||
count = 0
|
||||
return count
|
||||
|
||||
@lazyproperty
|
||||
def month_total_count_disabled_users(self):
|
||||
return current_org.get_org_members().filter(is_active=False).count()
|
||||
|
||||
@lazyproperty
|
||||
def month_total_count_active_assets(self):
|
||||
return len(set(self.session_month.values_list('asset', flat=True)))
|
||||
|
||||
@lazyproperty
|
||||
def month_total_count_inactive_assets(self):
|
||||
total = Asset.objects.all().count()
|
||||
active = self.month_total_count_active_assets
|
||||
count = total - active
|
||||
if count < 0:
|
||||
count = 0
|
||||
return count
|
||||
|
||||
@lazyproperty
|
||||
def month_total_count_disabled_assets(self):
|
||||
return Asset.objects.filter(is_active=False).count()
|
||||
|
||||
|
||||
class WeekSessionMetricMixin:
|
||||
session_week = None
|
||||
|
||||
@lazyproperty
|
||||
def session_week(self):
|
||||
week_ago = timezone.now() - timezone.timedelta(weeks=1)
|
||||
session_week = Session.objects.filter(date_start__gt=week_ago)
|
||||
return session_week
|
||||
|
||||
def get_week_login_times_top5_users(self):
|
||||
users = self.session_week.values_list('user', flat=True)
|
||||
users = [
|
||||
{'user': user, 'total': total}
|
||||
for user, total in Counter(users).most_common(5)
|
||||
]
|
||||
return users
|
||||
|
||||
def get_week_total_count_login_users(self):
|
||||
return len(set(self.session_week.values_list('user', flat=True)))
|
||||
|
||||
def get_week_total_count_login_times(self):
|
||||
return self.session_week.count()
|
||||
|
||||
def get_week_login_times_top10_assets(self):
|
||||
assets = self.session_week.values("asset")\
|
||||
.annotate(total=Count("asset"))\
|
||||
.annotate(last=Max("date_start")).order_by("-total")[:10]
|
||||
for asset in assets:
|
||||
asset['last'] = str(asset['last'])
|
||||
return list(assets)
|
||||
|
||||
def get_week_login_times_top10_users(self):
|
||||
users = self.session_week.values("user") \
|
||||
.annotate(total=Count("user")) \
|
||||
.annotate(last=Max("date_start")).order_by("-total")[:10]
|
||||
for user in users:
|
||||
user['last'] = str(user['last'])
|
||||
return list(users)
|
||||
|
||||
def get_week_login_record_top10_sessions(self):
|
||||
sessions = self.session_week.order_by('-date_start')[:10]
|
||||
for session in sessions:
|
||||
session.avatar_url = User.get_avatar_url("")
|
||||
sessions = [
|
||||
{
|
||||
'user': session.user,
|
||||
'asset': session.asset,
|
||||
'is_finished': session.is_finished,
|
||||
'date_start': str(session.date_start),
|
||||
'timesince': timesince(session.date_start)
|
||||
}
|
||||
for session in sessions
|
||||
]
|
||||
return sessions
|
||||
|
||||
|
||||
class TotalCountMixin:
|
||||
@staticmethod
|
||||
def get_total_count_users():
|
||||
return current_org.get_org_members().count()
|
||||
|
||||
@staticmethod
|
||||
def get_total_count_assets():
|
||||
return Asset.objects.all().count()
|
||||
|
||||
@staticmethod
|
||||
def get_total_count_online_users():
|
||||
count = len(set(Session.objects.filter(is_finished=False).values_list('user', flat=True)))
|
||||
return count
|
||||
|
||||
@staticmethod
|
||||
def get_total_count_online_sessions():
|
||||
return Session.objects.filter(is_finished=False).count()
|
||||
|
||||
|
||||
class IndexApi(TotalCountMixin, WeekSessionMetricMixin, MonthLoginMetricMixin, APIView):
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
http_method_names = ['get']
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
data = {}
|
||||
|
||||
query_params = self.request.query_params
|
||||
|
||||
_all = query_params.get('all')
|
||||
|
||||
if _all or query_params.get('total_count'):
|
||||
data.update({
|
||||
'total_count_assets': self.get_total_count_assets(),
|
||||
'total_count_users': self.get_total_count_users(),
|
||||
'total_count_online_users': self.get_total_count_online_users(),
|
||||
'total_count_online_sessions': self.get_total_count_online_sessions(),
|
||||
})
|
||||
|
||||
if _all or query_params.get('month_metrics'):
|
||||
data.update({
|
||||
'month_metrics_date': self.get_month_metrics_date(),
|
||||
'month_metrics_total_count_login': self.get_month_metrics_total_count_login(),
|
||||
'month_metrics_total_count_active_users': self.get_month_metrics_total_count_active_users(),
|
||||
'month_metrics_total_count_active_assets': self.get_month_metrics_total_count_active_assets(),
|
||||
})
|
||||
|
||||
if _all or query_params.get('month_total_count_users'):
|
||||
data.update({
|
||||
'month_total_count_active_users': self.month_total_count_active_users,
|
||||
'month_total_count_inactive_users': self.month_total_count_inactive_users,
|
||||
'month_total_count_disabled_users': self.month_total_count_disabled_users,
|
||||
})
|
||||
|
||||
if _all or query_params.get('month_total_count_assets'):
|
||||
data.update({
|
||||
'month_total_count_active_assets': self.month_total_count_active_assets,
|
||||
'month_total_count_inactive_assets': self.month_total_count_inactive_assets,
|
||||
'month_total_count_disabled_assets': self.month_total_count_disabled_assets,
|
||||
})
|
||||
|
||||
if _all or query_params.get('week_total_count'):
|
||||
data.update({
|
||||
'week_total_count_login_users': self.get_week_total_count_login_users(),
|
||||
'week_total_count_login_times': self.get_week_total_count_login_times(),
|
||||
})
|
||||
|
||||
if _all or query_params.get('week_login_times_top5_users'):
|
||||
data.update({
|
||||
'week_login_times_top5_users': self.get_week_login_times_top5_users(),
|
||||
})
|
||||
|
||||
if _all or query_params.get('week_login_times_top10_assets'):
|
||||
data.update({
|
||||
'week_login_times_top10_assets': self.get_week_login_times_top10_assets(),
|
||||
})
|
||||
|
||||
if _all or query_params.get('week_login_times_top10_users'):
|
||||
data.update({
|
||||
'week_login_times_top10_users': self.get_week_login_times_top10_users(),
|
||||
})
|
||||
|
||||
if _all or query_params.get('week_login_record_top10_sessions'):
|
||||
data.update({
|
||||
'week_login_record_top10_sessions': self.get_week_login_record_top10_sessions()
|
||||
})
|
||||
|
||||
return JsonResponse(data, status=200)
|
||||
|
||||
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -1,9 +1,13 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
from .. import utils
|
||||
from users.models import User
|
||||
|
||||
|
||||
class UserQuerysetMixin:
|
||||
def get_queryset(self):
|
||||
queryset = utils.get_current_org_members()
|
||||
if self.request.query_params.get('all'):
|
||||
queryset = User.objects.exclude(role=User.ROLE_APP)
|
||||
else:
|
||||
queryset = utils.get_current_org_members()
|
||||
return queryset
|
||||
|
Loading…
Reference in new issue