瀏覽代碼

[minor] auth log for login and logout feed (#2624)

* [minor] Authentication log for login & logout

* [minor] added test cases for authentication log
version-14
Makarand Bauskar 8 年之前
committed by Rushabh Mehta
父節點
當前提交
4c95e3e5ef
共有 9 個文件被更改,包括 452 次插入19 次删除
  1. +10
    -8
      frappe/auth.py
  2. +0
    -0
      frappe/core/doctype/authentication_log/__init__.py
  3. +8
    -0
      frappe/core/doctype/authentication_log/authentication_log.js
  4. +341
    -0
      frappe/core/doctype/authentication_log/authentication_log.json
  5. +22
    -0
      frappe/core/doctype/authentication_log/authentication_log.py
  6. +8
    -0
      frappe/core/doctype/authentication_log/authentication_log_list.js
  7. +48
    -0
      frappe/core/doctype/authentication_log/test_authentication_log.py
  8. +7
    -11
      frappe/core/doctype/communication/feed.py
  9. +8
    -0
      frappe/desk/page/activity/activity.js

+ 10
- 8
frappe/auth.py 查看文件

@@ -15,6 +15,7 @@ from frappe.sessions import Session, clear_sessions, delete_session
from frappe.modules.patch_handler import check_session_stopped from frappe.modules.patch_handler import check_session_stopped
from frappe.translate import get_lang_code from frappe.translate import get_lang_code
from frappe.utils.password import check_password from frappe.utils.password import check_password
from frappe.core.doctype.authentication_log.authentication_log import add_authentication_log


from urllib import quote from urllib import quote


@@ -59,10 +60,6 @@ class HTTPRequest:
# check status # check status
check_session_stopped() check_session_stopped()


# run login triggers
if frappe.form_dict.get('cmd')=='login':
frappe.local.login_manager.run_trigger('on_session_creation')

def validate_csrf_token(self): def validate_csrf_token(self):
if frappe.local.request and frappe.local.request.method=="POST": if frappe.local.request and frappe.local.request.method=="POST":
if not frappe.local.session.data.csrf_token \ if not frappe.local.session.data.csrf_token \
@@ -103,6 +100,9 @@ class LoginManager:
if frappe.local.form_dict.get('cmd')=='login' or frappe.local.request.path=="/api/method/login": if frappe.local.form_dict.get('cmd')=='login' or frappe.local.request.path=="/api/method/login":
self.login() self.login()
self.resume = False self.resume = False

# run login triggers
self.run_trigger('on_session_creation')
else: else:
try: try:
self.resume = True self.resume = True
@@ -183,7 +183,7 @@ class LoginManager:
if not (user and pwd): if not (user and pwd):
user, pwd = frappe.form_dict.get('usr'), frappe.form_dict.get('pwd') user, pwd = frappe.form_dict.get('usr'), frappe.form_dict.get('pwd')
if not (user and pwd): if not (user and pwd):
self.fail('Incomplete login details')
self.fail('Incomplete login details', user=user)


self.check_if_enabled(user) self.check_if_enabled(user)
self.user = self.check_password(user, pwd) self.user = self.check_password(user, pwd)
@@ -192,7 +192,7 @@ class LoginManager:
"""raise exception if user not enabled""" """raise exception if user not enabled"""
if user=='Administrator': return if user=='Administrator': return
if not cint(frappe.db.get_value('User', user, 'enabled')): if not cint(frappe.db.get_value('User', user, 'enabled')):
self.fail('User disabled or missing')
self.fail('User disabled or missing', user=user)


def check_password(self, user, pwd): def check_password(self, user, pwd):
"""check password""" """check password"""
@@ -200,10 +200,12 @@ class LoginManager:
# returns user in correct case # returns user in correct case
return check_password(user, pwd) return check_password(user, pwd)
except frappe.AuthenticationError: except frappe.AuthenticationError:
self.fail('Incorrect password')
self.fail('Incorrect password', user=user)


def fail(self, message):
def fail(self, message, user="NA"):
frappe.local.response['message'] = message frappe.local.response['message'] = message
add_authentication_log(message, user, status="Failed")
frappe.db.commit()
raise frappe.AuthenticationError raise frappe.AuthenticationError


def run_trigger(self, event='on_login'): def run_trigger(self, event='on_login'):


+ 0
- 0
frappe/core/doctype/authentication_log/__init__.py 查看文件


+ 8
- 0
frappe/core/doctype/authentication_log/authentication_log.js 查看文件

@@ -0,0 +1,8 @@
// Copyright (c) 2016, Frappe Technologies and contributors
// For license information, please see license.txt

frappe.ui.form.on('Authentication Log', {
refresh: function(frm) {

}
});

+ 341
- 0
frappe/core/doctype/authentication_log/authentication_log.json 查看文件

@@ -0,0 +1,341 @@
{
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2017-01-23 16:56:25.875531",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Setup",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "user_details",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "User Details",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "user",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "User",
"length": 0,
"no_copy": 0,
"options": "User",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "full_name",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Full Name",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_8",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "date",
"fieldtype": "Datetime",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Date",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_6",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "subject",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Subject",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_2",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "operation",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Operation",
"length": 0,
"no_copy": 0,
"options": "\nLogin\nLogout",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "status",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Status",
"length": 0,
"no_copy": 0,
"options": "\nSuccess\nFailed",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}
],
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 1,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-01-23 18:10:35.785334",
"modified_by": "Administrator",
"module": "Core",
"name": "Authentication Log",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 0,
"delete": 0,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "All",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 0
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "subject",
"track_seen": 0
}

+ 22
- 0
frappe/core/doctype/authentication_log/authentication_log.py 查看文件

@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt

from __future__ import unicode_literals
import frappe
from frappe.utils import get_fullname, now
from frappe.model.document import Document

class AuthenticationLog(Document):
def before_insert(self):
self.full_name = get_fullname(self.user)
self.date = now()

def add_authentication_log(subject, user, operation="Login", status="Success"):
frappe.get_doc({
"doctype": "Authentication Log",
"user": user,
"status": status,
"subject": subject,
"operation": operation,
}).insert(ignore_permissions=True)

+ 8
- 0
frappe/core/doctype/authentication_log/authentication_log_list.js 查看文件

@@ -0,0 +1,8 @@
frappe.listview_settings['Authentication Log'] = {
get_indicator: function(doc) {
if(doc.operation == "Login" && doc.status == "Success")
return [__(doc.status), "green"];
else if(doc.operation == "Login" && doc.status == "Failed")
return [__(doc.status), "red"];
}
};

+ 48
- 0
frappe/core/doctype/authentication_log/test_authentication_log.py 查看文件

@@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies and Contributors
# See license.txt
from __future__ import unicode_literals

import frappe
import unittest

# test_records = frappe.get_test_records('Authentication Log')

class TestAuthenticationLog(unittest.TestCase):
def test_authentication_log(self):
from frappe.auth import LoginManager, CookieManager

# test user login log
frappe.local.form_dict = { 'cmd': 'login' }

frappe.form_dict = {
'sid': 'Guest',
'pwd': 'admin',
'usr': 'Administrator'
}

frappe.local.cookie_manager = CookieManager()
frappe.local.login_manager = LoginManager()

auth_log = self.get_auth_log()
self.assertEquals(auth_log.status, 'Success')

# test user logout log
frappe.local.login_manager.logout()
auth_log = self.get_auth_log(operation='Logout')
self.assertEquals(auth_log.status, 'Success')

# test invalid login
frappe.form_dict.update({ 'pwd': 'password' })
self.assertRaises(frappe.AuthenticationError, LoginManager)
auth_log = self.get_auth_log()
self.assertEquals(auth_log.status, 'Failed')

def get_auth_log(self, operation='Login'):
names = frappe.db.sql_list("""select name from `tabAuthentication Log`
where user='Administrator' and operation='{operation}' order by
creation desc""".format(operation=operation))

name = names[0]
auth_log = frappe.get_doc('Authentication Log', name)
return auth_log

+ 7
- 11
frappe/core/doctype/communication/feed.py 查看文件

@@ -9,6 +9,7 @@ from frappe.model.document import Document
from frappe.utils import get_fullname from frappe.utils import get_fullname
from frappe import _ from frappe import _
from frappe.core.doctype.communication.comment import add_info_comment from frappe.core.doctype.communication.comment import add_info_comment
from frappe.core.doctype.authentication_log.authentication_log import add_authentication_log


def update_feed(doc, method=None): def update_feed(doc, method=None):
"adds a new communication with comment_type='Updated'" "adds a new communication with comment_type='Updated'"
@@ -53,19 +54,14 @@ def update_feed(doc, method=None):
}).insert(ignore_permissions=True) }).insert(ignore_permissions=True)


def login_feed(login_manager): def login_feed(login_manager):
add_info_comment(**{
"subject": _("{0} logged in").format(get_fullname(login_manager.user)),
"full_name": get_fullname(login_manager.user)
})
if login_manager.user != "Guest":
subject = _("{0} logged in").format(get_fullname(login_manager.user))
add_authentication_log(subject, login_manager.user)


def logout_feed(user, reason): def logout_feed(user, reason):
if not user:
return

add_info_comment(**{
"subject": _("{0} logged out: <b>{1}</b>").format(get_fullname(user), reason),
"full_name": get_fullname(user),
})
if user and user != "Guest":
subject = _("{0} logged out: {1}").format(get_fullname(user), frappe.bold(reason))
add_authentication_log(subject, user, operation="Logout")


def get_feed_match_conditions(user=None, force=True): def get_feed_match_conditions(user=None, force=True):
if not user: user = frappe.session.user if not user: user = frappe.session.user


+ 8
- 0
frappe/desk/page/activity/activity.js 查看文件

@@ -72,6 +72,14 @@ frappe.pages['activity'].on_page_load = function(wrapper) {
}, 'fa fa-th') }, 'fa fa-th')
} }


this.page.add_menu_item(__('Authentication Log'), function() {
frappe.route_options = {
"user": user
}

frappe.set_route('Report', "Authentication Log");
}, 'fa fa-th')

this.page.add_menu_item(__('Show Likes'), function() { this.page.add_menu_item(__('Show Likes'), function() {
frappe.route_options = { frappe.route_options = {
show_likes: true show_likes: true


Loading…
取消
儲存