浏览代码

[useradmin] Add UserProfile.last_activity, metric, and middleware

Erick Tryzelaar 10 年之前
父节点
当前提交
417d09a

+ 2 - 0
apps/useradmin/src/useradmin/__init__.py

@@ -13,3 +13,5 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
+
+import useradmin.metrics

+ 31 - 0
apps/useradmin/src/useradmin/metrics.py

@@ -0,0 +1,31 @@
+# Licensed to Cloudera, Inc. under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  Cloudera, Inc. licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from datetime import datetime, timedelta
+
+from desktop.lib.metrics import global_registry
+
+def active_users():
+  from useradmin.models import UserProfile
+  return UserProfile.objects.filter(last_activity__gt=datetime.now() - timedelta(hours=1)).count()
+
+global_registry().gauge_callback(
+    name='users.active',
+    callback=active_users,
+    label='Number of active users',
+    description='Number of active users in the last hour',
+    numerator='active users',
+)

+ 22 - 1
apps/useradmin/src/useradmin/middleware.py

@@ -16,12 +16,13 @@
 # limitations under the License.
 
 import logging
+from datetime import datetime
 
 from django.contrib.auth.models import User
 
 from desktop.conf import LDAP
 
-from models import UserProfile
+from models import UserProfile, get_profile
 from views import import_ldap_users
 
 import ldap_access
@@ -57,3 +58,23 @@ class LdapSynchronizationMiddleware(object):
 
       request.session[self.USER_CACHE_NAME] = True
       request.session.modified = True
+
+
+class UpdateLastActivityMiddleware(object):
+  """
+  Middleware to track the last activity of a user.
+  """
+
+  def process_request(self, request):
+    user = request.user
+
+    if not user or not user.is_authenticated():
+      return
+
+    profile = get_profile(user)
+    profile.last_activity = datetime.now()
+
+    try:
+      profile.save()
+    except DatabaseError:
+      log.exception('Error saving profile information')

+ 89 - 0
apps/useradmin/src/useradmin/migrations/0005_auto__add_field_userprofile_last_activity.py

@@ -0,0 +1,89 @@
+# -*- coding: utf-8 -*-
+from south.utils import datetime_utils as datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        # Adding field 'UserProfile.last_activity'
+        db.add_column(u'useradmin_userprofile', 'last_activity',
+                      self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime(1969, 12, 31, 0, 0)),
+                      keep_default=False)
+
+
+    def backwards(self, orm):
+        # Deleting field 'UserProfile.last_activity'
+        db.delete_column(u'useradmin_userprofile', 'last_activity')
+
+
+    models = {
+        u'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        u'auth.permission': {
+            'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        u'auth.user': {
+            'Meta': {'object_name': 'User'},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        u'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        u'useradmin.grouppermission': {
+            'Meta': {'object_name': 'GroupPermission'},
+            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.Group']"}),
+            'hue_permission': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['useradmin.HuePermission']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+        },
+        u'useradmin.huepermission': {
+            'Meta': {'object_name': 'HuePermission'},
+            'action': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'app': ('django.db.models.fields.CharField', [], {'max_length': '30'}),
+            'description': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'through': u"orm['useradmin.GroupPermission']", 'symmetrical': 'False'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+        },
+        u'useradmin.ldapgroup': {
+            'Meta': {'object_name': 'LdapGroup'},
+            'group': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'group'", 'to': u"orm['auth.Group']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+        },
+        u'useradmin.userprofile': {
+            'Meta': {'object_name': 'UserProfile'},
+            'creation_method': ('django.db.models.fields.CharField', [], {'default': "'HUE'", 'max_length': '64'}),
+            'first_login': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'home_directory': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_activity': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(1969, 12, 31, 0, 0)'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'unique': 'True'})
+        }
+    }
+
+    complete_apps = ['useradmin']

+ 3 - 1
apps/useradmin/src/useradmin/models.py

@@ -49,8 +49,9 @@ what models you may or may not edit, and there are elaborations (especially
 in Django 1.2) to manipulate this row by row.  This does not map nicely
 onto actions which may not relate to database models.
 """
-from enum import Enum
 import logging
+from datetime import datetime
+from enum import Enum
 
 from django.db import connection, models
 from django.contrib.auth import models as auth_models
@@ -95,6 +96,7 @@ class UserProfile(models.Model):
   creation_method = models.CharField(editable=True, null=False, max_length=64, default=str(CreationMethod.HUE))
   first_login = models.BooleanField(default=True, verbose_name=_t('First Login'),
                                    help_text=_t('If this is users first login.'))
+  last_activity = models.DateTimeField(default=datetime.fromtimestamp(0))
 
   def get_groups(self):
     return self.user.groups.all()

+ 6 - 1
apps/useradmin/src/useradmin/tests.py

@@ -24,7 +24,7 @@ import urllib
 
 from nose.plugins.attrib import attr
 from nose.plugins.skip import SkipTest
-from nose.tools import assert_true, assert_equal, assert_false
+from nose.tools import assert_true, assert_equal, assert_false, assert_not_equal
 
 import desktop.conf
 from desktop.lib.django_test_util import make_logged_in_client
@@ -835,3 +835,8 @@ def test_get_connection_bind_password_script():
     useradmin.ldap_access.LdapConnection = OriginalLdapConnection
     for f in reset:
       f()
+
+def test_last_activity():
+  c = make_logged_in_client(username="test", is_superuser=True)
+  profile = UserProfile.objects.get(user__username='test')
+  assert_not_equal(profile.last_activity, 0)

+ 4 - 0
desktop/core/src/desktop/settings.py

@@ -412,6 +412,10 @@ USE_X_FORWARDED_HOST = desktop.conf.USE_X_FORWARDED_HOST.get()
 if desktop.conf.SECURE_PROXY_SSL_HEADER.get():
   SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTOCOL', 'https')
 
+# Add last activity tracking.
+if 'useradmin' in appmanager.DESKTOP_APPS:
+  MIDDLEWARE_CLASSES.append('useradmin.middleware.UpdateLastActivityMiddleware')
+
 ############################################################
 
 # Necessary for South to not fuzz with tests.  Fixed in South 0.7.1