URL: https://github.com/freeipa/freeipa/pull/174 Author: shanyin Title: #174: add log module Action: opened
PR body: """ add log module on the ipa-4-3 branch, the function of log module is to record any of our operation. """ To pull the PR as Git branch: git remote add ghfreeipa https://github.com/freeipa/freeipa git fetch ghfreeipa pull/174/head:pr174 git checkout pr174
From 74c4ef4331482c8763dcd9b86e9209cd2054686b Mon Sep 17 00:00:00 2001 From: shanyin <[email protected]> Date: Thu, 20 Oct 2016 14:56:11 +0800 Subject: [PATCH] add log module --- install/ui/src/freeipa/app.js | 1 + install/ui/src/freeipa/facet.js | 9 +- install/ui/src/freeipa/log.js | 79 +++++++++ install/ui/src/freeipa/navigation/menu_spec.js | 7 + ipalib/errors.py | 16 ++ ipalib/plugins/config.py | 3 +- ipalib/plugins/log.py | 227 +++++++++++++++++++++++++ ipaserver/install/cainstance.py | 5 + ipaserver/rpcserver.py | 48 ++++++ 9 files changed, 392 insertions(+), 3 deletions(-) create mode 100644 install/ui/src/freeipa/log.js create mode 100644 ipalib/plugins/log.py diff --git a/install/ui/src/freeipa/app.js b/install/ui/src/freeipa/app.js index daf17b7..4a132ee 100644 --- a/install/ui/src/freeipa/app.js +++ b/install/ui/src/freeipa/app.js @@ -52,6 +52,7 @@ define([ './trust', './topology', './user', + './log', './stageuser', 'dojo/domReady!' ],function(app_container) { diff --git a/install/ui/src/freeipa/facet.js b/install/ui/src/freeipa/facet.js index 9d71487..91a1474 100644 --- a/install/ui/src/freeipa/facet.js +++ b/install/ui/src/freeipa/facet.js @@ -1105,7 +1105,8 @@ exp.facet = IPA.facet = function(spec, no_init) { name: 'facet_actions', 'class': 'dropdown facet-actions', right_aligned: true, - toggle_text: 'Actions ', + //toggle_text: 'Actions ', + toggle_text: text.get('@i18n:actions.title'), toggle_class: 'btn btn-default', toggle_icon: 'fa fa-angle-down' }); @@ -2086,7 +2087,11 @@ exp.table_facet = IPA.table_facet = function(spec, no_init) { // sort map based on primary keys if (that.sort_enabled) { - records_map = records_map.sort(); + //records_map = records_map.sort(); + if (that.managed_entity.metadata.name == 'log') + ; + else + records_map = records_map.sort(); } // trim map leaving the entries visible in the current page only diff --git a/install/ui/src/freeipa/log.js b/install/ui/src/freeipa/log.js new file mode 100644 index 0000000..5344cb5 --- /dev/null +++ b/install/ui/src/freeipa/log.js @@ -0,0 +1,79 @@ +/* Authors: + * Zheng Lei <[email protected]> + * + * Copyright (C) 2010 Red Hat + * see file 'COPYING' for use and warranty information + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +define([ + './builder', + './ipa', + './jquery', + './phases', + './reg', + './rpc', + './text', + './dialogs/password', + './details', + './search', + './association', + './entity', + './certificate'], + function(builder, IPA, $, phases, reg, rpc, text, password_dialog) { + +/** + * Log module + * @class log + * @alternateClassName IPA.log + * @singleton + */ +var exp = IPA.log = {}; + +var make_spec = function() { +return { + name: 'log', + label: '日志', + facets: [ + { + label: '日志', + $type: 'search', + // setting no_update value(ture) to control add and remove buttons invisible + no_update: true, + // setting selectable value(false) to control checkbox invisible + selectable: false, + columns: [ + { name: 'logtime', label: "时间" }, + { name: 'loglevel', label: "级别" }, + { name: 'loguser', label: "用户" }, + { name: 'logip', label: "远程地址" }, + { name: 'logstatus', label: "状态" }, + { name: 'logmessage', label: "信息" } + ], + }, + ], +} +}; + +exp.entity_spec = make_spec(); +exp.register = function() { + var e = reg.entity; + e.register({type: 'log', spec: exp.entity_spec}); +}; + +phases.on('registration', exp.register); + +return exp; +}); diff --git a/install/ui/src/freeipa/navigation/menu_spec.js b/install/ui/src/freeipa/navigation/menu_spec.js index fb64cca..7dc21f2 100644 --- a/install/ui/src/freeipa/navigation/menu_spec.js +++ b/install/ui/src/freeipa/navigation/menu_spec.js @@ -239,6 +239,13 @@ var nav = {}; }, { entity: 'config' } ] + }, + { + name: 'log', + label: 'IPA日志', + children: [ + { entity: 'log' } + ] } ] }; diff --git a/ipalib/errors.py b/ipalib/errors.py index 6d1b328..bc94af5 100644 --- a/ipalib/errors.py +++ b/ipalib/errors.py @@ -1752,6 +1752,22 @@ class CertificateInvalidError(CertificateError): errno = 4310 format = _('%(name)s certificate is not valid') +# add log primary key error judge +class NotInt(ExecutionError): + """ + **4311** Raised when an pkey is not int. + + For example: + + >>> raise NotInt(reason='not logline') + Traceback (most recent call last): + ... + NotInt: not logline + + """ + + errno = 4311 + format = _('%(reason)s') class DNSError(ExecutionError): """ diff --git a/ipalib/plugins/config.py b/ipalib/plugins/config.py index 0f4d289..aaf70ec 100644 --- a/ipalib/plugins/config.py +++ b/ipalib/plugins/config.py @@ -164,7 +164,8 @@ class config(LDAPObject): ), IA5Str('ipagroupsearchfields', cli_name='groupsearch', - label='Group search fields', + #label='Group search fields', + label=_('Group search fields'), doc=_('A comma-separated list of fields to search in when searching for groups'), ), Bool('ipamigrationenabled', diff --git a/ipalib/plugins/log.py b/ipalib/plugins/log.py new file mode 100644 index 0000000..c5c5ea1 --- /dev/null +++ b/ipalib/plugins/log.py @@ -0,0 +1,227 @@ +from ipalib import api, Object, Str, _, ngettext, Int, Flag, crud, errors, output, create_api +from ipalib.parameters import Any +from ipalib.output import Output +from ipalib.plugins.baseldap import * +from ipalib.plugins import baseldap +from ipalib.plugable import Registry +from ipalib.cli import to_cli +from ipapython.version import API_VERSION +from ipalib.capabilities import client_has_capability + +register = Registry() + +def gen_pkey_only_option(cli_name): + return Flag('pkey_only?', + label=_('Primary key only'), + doc=_('Results should contain primary key attribute only ("%s")') \ + % to_cli(cli_name),) + +def pkey_to_value(key, options): + version = options.get('version', API_VERSION) + if client_has_capability(version, 'primary_key_types'): + return key + return pkey_to_unicode(key) + +@register() +class log(LDAPObject): + + default_attributes = [ + 'logtime', 'loglevel', 'loguser', 'logip', 'logstatus', 'logmessage' + ] + + search_display_attributes = [ + 'logtime', 'loglevel', 'loguser', 'logip', 'logstatus', 'logmessage', + ] + + takes_params = ( + Str('logline', primary_key=True), + Str('logtime', cli_name="time", label='time'), + Str('loglevel', cli_name="level", label='level'), + Str('loguser', cli_name="user", label='user'), + Str('logip', cli_name="ip", label='ip'), + Str('logstatus', cli_name="status", label='status'), + Str('logmessage', cli_name="message", label='message'), + ) + + parent_object = '' + object_not_found_msg = _('%(pkey)s: not found') + + def get_ancestor_primary_keys(self): + if self.parent_object: + parent_obj = self.api.Object[self.parent_object] + for key in parent_obj.get_ancestor_primary_keys(): + yield key + if parent_obj.primary_key: + pkey = parent_obj.primary_key + yield pkey.clone_rename( + parent_obj.name + pkey.name, required=True, query=True, + cli_name=parent_obj.name, label=pkey.label + ) + + def handle_not_found(self, *keys): + pkey = '' + if self.primary_key: + pkey = keys[-1] + raise errors.NotFound( + reason=self.object_not_found_msg % { + 'pkey': pkey, + } + ) + +@register() +class log_show(crud.Retrieve): + __doc__ = _('Display information about a log.') + + takes_options = ( + Flag('rights', + label=_('Rights'), + doc=_('Display the access rights of this entry (requires --all). See ipa man page for details.'), + ), + ) + + def get_args(self): + for key in self.obj.get_ancestor_primary_keys(): + yield key + for arg in super(crud.Retrieve, self).get_args(): + yield arg + + # list of attributes we want exported to JSON + json_friendly_attributes = ( + 'takes_args', + ) + + def execute(self, *keys, **options): + flag=keys[-1] + entry_list=[] + + if options.get('all', False): + attrs_list = ['*'] + self.obj.default_attributes + else: + attrs_list = set(self.obj.default_attributes) + if options.get('no_members', False): + attrs_list.difference_update(self.obj.attribute_members) + attrs_list = list(attrs_list) + + linenumber = 0 + for line in open("/var/log/ipa/ipa.log"): + line_ignore_enter = line.strip('\n') + line_list = line_ignore_enter.split('\t') + (logtime, loglevel, loguser, logip, logstatus, logmessage) = line_list + entry_attrs=dict() + linenumber += 1 + entry_attrs[u'logline'] = unicode(linenumber) + i = 0 + for param in self.obj.default_attributes: + entry_attrs[unicode(param)] = unicode(line_list[i]).split("\n", 1) + i += 1 + entry_list.append(entry_attrs) + + try: + int(flag) + except(ValueError): + raise errors.NotInt( + reason=_('%(pkey)s: not logline') % { + 'pkey': flag, + } + ) + + results = dict() + for entry in entry_list: + if flag == entry['logline']: + results = entry + break + if self.obj.primary_key: + pkey = keys[-1] + else: + pkey = None + if not results: + self.obj.handle_not_found(*keys) + + return dict(result=results, value=pkey_to_value(pkey, options)) + +@register() +class log_find(crud.Search): + __doc__ = _('search for log.') + + takes_options = ( + Int('timelimit?', + label=_('Time Limit'), + doc=_('Time limit of search in seconds'), + flags=['no_display'], + minvalue=0, + autofill=False, + ), + Int('sizelimit?', + label=_('Size Limit'), + doc=_('Maximum number of entries returned'), + flags=['no_display'], + minvalue=0, + autofill=False, + ), + ) + + member_attributes = [] + + def get_member_options(self, attr): + for ldap_obj_name in self.obj.attribute_members[attr]: + ldap_obj = self.api.Object[ldap_obj_name] + relationship = self.obj.relationships.get( + attr, ['member', '', 'no_'] + ) + doc = self.member_param_incl_doc % dict( + searched_object=self.obj.object_name_plural, + relationship=relationship[0].lower(), + ldap_object=ldap_obj.object_name_plural + ) + name = '%s%s' % (relationship[1], to_cli(ldap_obj_name)) + yield Str( + '%s*' % name, cli_name='%ss' % name, doc=doc, + label=ldap_obj.object_name, csv=True + ) + doc = self.member_param_excl_doc % dict( + searched_object=self.obj.object_name_plural, + relationship=relationship[0].lower(), + ldap_object=ldap_obj.object_name_plural + ) + name = '%s%s' % (relationship[2], to_cli(ldap_obj_name)) + yield Str( + '%s*' % name, cli_name='%ss' % name, doc=doc, + label=ldap_obj.object_name, csv=True + ) + + def get_options(self): + for option in super(crud.Search, self).get_options(): + yield option + if self.obj.primary_key and \ + 'no_output' not in self.obj.primary_key.flags: + yield gen_pkey_only_option(self.obj.primary_key.cli_name) + for attr in self.member_attributes: + for option in self.get_member_options(attr): + yield option + + def execute(self, *args, **options): + flag = args[-1] + entries=[] + linenumber = 0 + truncated = False + + for line in open("/var/log/ipa/ipa.log"): + line_ignore_enter = line.strip('\n') + line_list = line_ignore_enter.split('\t') + (logtime, loglevel, loguser, logip, logstatus, logmessage) = line_list + + entry_dict=dict() + linenumber += 1 + entry_dict[u'logline'] = unicode(linenumber) + i = 0 + for params in self.obj.default_attributes: + entry_dict[unicode(params)] = unicode(line_list[i]).split("\n", 1) + i += 1 + if flag: + if flag in line: + entries.append(entry_dict) + else: + entries = entries + else: + entries.append(entry_dict) + return dict(result=entries[::-1], count=len(entries), truncated=truncated) diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py index aba5f8c..c6b57d0 100644 --- a/ipaserver/install/cainstance.py +++ b/ipaserver/install/cainstance.py @@ -573,6 +573,11 @@ def __spawn_instance(self): with open(cfg_file, "wb") as f: config.write(f) + # Create parent directory for pki-tomcatd service file + PKI_SERVICE_LOCATION="/etc/systemd/system/pki-tomcatd.target.wants" + if not os.path.exists(PKI_SERVICE_LOCATION): + os.mkdir(PKI_SERVICE_LOCATION) + self.backup_state('installed', True) try: DogtagInstance.spawn_instance(self, cfg_file) diff --git a/ipaserver/rpcserver.py b/ipaserver/rpcserver.py index 64620b4..a2f96a4 100644 --- a/ipaserver/rpcserver.py +++ b/ipaserver/rpcserver.py @@ -31,6 +31,7 @@ import traceback import gssapi import time +import logging import ldap.controls from pyasn1.type import univ, namedtype @@ -304,6 +305,10 @@ class WSGIExecutioner(Executioner): _system_commands = {} + # add kydc log + LOGGING_FORMAT='\t'.join(['%(asctime)s', '%(levelname)s', '%(message)s',]) + logging.basicConfig(filename='/var/log/ipa/ipa.log', level=logging.INFO, format=LOGGING_FORMAT, datefmt='%Y-%m-%d %H:%M:%S') + def _on_finalize(self): self.url = self.env.mount_ipa + self.key super(WSGIExecutioner, self)._on_finalize() @@ -374,6 +379,43 @@ def wsgi_execute(self, environ): result_string = type(e).__name__ else: result_string = 'SUCCESS' + # handle batch + if name == 'batch': + for param in args: + result_param = dict() + method = None + if 'method' not in param: + raise errors.RequirementError(method='method') + if 'params' not in param: + raise errors.RequirementError(method='params') + method = param['method'] + if method not in self.Command: + raise errors.CommandError(method=method) + if method == 'log_show' or method == 'log_find': + continue + a, kw = param['params'] + newkw = dict((str(k), v) for k, v in kw.iteritems()) + result_param = self.Command[method].args_options_2_params(*a, **newkw) + newkw.setdefault('version', options['version']) + + logging.info('%s\t%s\t%s\t%s(%s)', + principal.split('@')[0], + environ.get('REMOTE_ADDR'), + result_string, + method, + #', '.join(result_param)) + ', '.join(self.Command[method]._repr_iter(**result_param))) + else: + if name == 'log_show' or name == 'log_find': + pass + else: + logging.info('%s\t%s\t%s\t%s(%s)', + principal.split('@')[0], + environ.get('REMOTE_ADDR'), + result_string, + name, + ', '.join(self.Command[name]._repr_iter(**params))) + self.info('[%s] %s: %s(%s): %s', type(self).__name__, principal, @@ -946,9 +988,15 @@ def __call__(self, environ, start_response): try: self.kinit(user, self.api.env.realm, password, ipa_ccache_name) except PasswordExpired as e: + # add log information + logging.info("%s\t%s\t%s\t%s", user, environ.get("REMOTE_ADDR"), 'FAILURE', 'password-expired') return self.unauthorized(environ, start_response, str(e), 'password-expired') except InvalidSessionPassword as e: + logging.info("%s\t%s\t%s\t%s", user, environ.get("REMOTE_ADDR"), 'FAILURE', 'invalid-password') return self.unauthorized(environ, start_response, str(e), 'invalid-password') + else: + self.info("%s logined from %s\n", user, environ.get("REMOTE_ADDR")) + logging.info("%s\t%s\t%s\t%s", user, environ.get("REMOTE_ADDR"), 'SUCCESS', 'session_login()') return self.finalize_kerberos_acquisition('login_password', ipa_ccache_name, environ, start_response)
-- Manage your subscription for the Freeipa-devel mailing list: https://www.redhat.com/mailman/listinfo/freeipa-devel Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code
