##// END OF EJS Templates
audit-logs: fixed search cases with special chars such as `-`....
dan -
r4135:9b536d10 default
parent child
Show More
@@ -1,126 +1,127
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2019 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22
22
23 from whoosh.qparser.default import QueryParser, query
23 from whoosh.qparser.default import QueryParser, query
24 from whoosh.qparser.dateparse import DateParserPlugin
24 from whoosh.qparser.dateparse import DateParserPlugin
25 from whoosh.fields import (TEXT, Schema, DATETIME)
25 from whoosh.fields import (TEXT, Schema, DATETIME, KEYWORD)
26 from sqlalchemy.sql.expression import or_, and_, not_, func
26 from sqlalchemy.sql.expression import or_, and_, not_, func
27
27
28 from rhodecode.model.db import UserLog
28 from rhodecode.model.db import UserLog
29 from rhodecode.lib.utils2 import remove_prefix, remove_suffix, safe_unicode
29 from rhodecode.lib.utils2 import remove_prefix, remove_suffix, safe_unicode
30
30
31 # JOURNAL SCHEMA used only to generate queries in journal. We use whoosh
31 # JOURNAL SCHEMA used only to generate queries in journal. We use whoosh
32 # querylang to build sql queries and filter journals
32 # querylang to build sql queries and filter journals
33 JOURNAL_SCHEMA = Schema(
33 AUDIT_LOG_SCHEMA = Schema(
34 username=TEXT(),
34 username=KEYWORD(),
35 repository=KEYWORD(),
36
35 date=DATETIME(),
37 date=DATETIME(),
36 action=TEXT(),
38 action=TEXT(),
37 repository=TEXT(),
38 ip=TEXT(),
39 ip=TEXT(),
39 )
40 )
40
41
41 log = logging.getLogger(__name__)
42 log = logging.getLogger(__name__)
42
43
43
44
44 def user_log_filter(user_log, search_term):
45 def user_log_filter(user_log, search_term):
45 """
46 """
46 Filters sqlalchemy user_log based on search_term with whoosh Query language
47 Filters sqlalchemy user_log based on search_term with whoosh Query language
47 http://packages.python.org/Whoosh/querylang.html
48 http://packages.python.org/Whoosh/querylang.html
48
49
49 :param user_log:
50 :param user_log:
50 :param search_term:
51 :param search_term:
51 """
52 """
52 log.debug('Initial search term: %r', search_term)
53 log.debug('Initial search term: %r', search_term)
53 qry = None
54 qry = None
54 if search_term:
55 if search_term:
55 qp = QueryParser('repository', schema=JOURNAL_SCHEMA)
56 qp = QueryParser('repository', schema=AUDIT_LOG_SCHEMA)
56 qp.add_plugin(DateParserPlugin())
57 qp.add_plugin(DateParserPlugin())
57 qry = qp.parse(safe_unicode(search_term))
58 qry = qp.parse(safe_unicode(search_term))
58 log.debug('Filtering using parsed query %r', qry)
59 log.debug('Filtering using parsed query %r', qry)
59
60
60 def wildcard_handler(col, wc_term):
61 def wildcard_handler(col, wc_term):
61 if wc_term.startswith('*') and not wc_term.endswith('*'):
62 if wc_term.startswith('*') and not wc_term.endswith('*'):
62 # postfix == endswith
63 # postfix == endswith
63 wc_term = remove_prefix(wc_term, prefix='*')
64 wc_term = remove_prefix(wc_term, prefix='*')
64 return func.lower(col).endswith(wc_term)
65 return func.lower(col).endswith(wc_term)
65 elif wc_term.startswith('*') and wc_term.endswith('*'):
66 elif wc_term.startswith('*') and wc_term.endswith('*'):
66 # wildcard == ilike
67 # wildcard == ilike
67 wc_term = remove_prefix(wc_term, prefix='*')
68 wc_term = remove_prefix(wc_term, prefix='*')
68 wc_term = remove_suffix(wc_term, suffix='*')
69 wc_term = remove_suffix(wc_term, suffix='*')
69 return func.lower(col).contains(wc_term)
70 return func.lower(col).contains(wc_term)
70
71
71 def get_filterion(field, val, term):
72 def get_filterion(field, val, term):
72
73
73 if field == 'repository':
74 if field == 'repository':
74 field = getattr(UserLog, 'repository_name')
75 field = getattr(UserLog, 'repository_name')
75 elif field == 'ip':
76 elif field == 'ip':
76 field = getattr(UserLog, 'user_ip')
77 field = getattr(UserLog, 'user_ip')
77 elif field == 'date':
78 elif field == 'date':
78 field = getattr(UserLog, 'action_date')
79 field = getattr(UserLog, 'action_date')
79 elif field == 'username':
80 elif field == 'username':
80 field = getattr(UserLog, 'username')
81 field = getattr(UserLog, 'username')
81 else:
82 else:
82 field = getattr(UserLog, field)
83 field = getattr(UserLog, field)
83 log.debug('filter field: %s val=>%s', field, val)
84 log.debug('filter field: %s val=>%s', field, val)
84
85
85 # sql filtering
86 # sql filtering
86 if isinstance(term, query.Wildcard):
87 if isinstance(term, query.Wildcard):
87 return wildcard_handler(field, val)
88 return wildcard_handler(field, val)
88 elif isinstance(term, query.Prefix):
89 elif isinstance(term, query.Prefix):
89 return func.lower(field).startswith(func.lower(val))
90 return func.lower(field).startswith(func.lower(val))
90 elif isinstance(term, query.DateRange):
91 elif isinstance(term, query.DateRange):
91 return and_(field >= val[0], field <= val[1])
92 return and_(field >= val[0], field <= val[1])
92 elif isinstance(term, query.Not):
93 elif isinstance(term, query.Not):
93 return not_(field == val)
94 return not_(field == val)
94 return func.lower(field) == func.lower(val)
95 return func.lower(field) == func.lower(val)
95
96
96 if isinstance(qry, (query.And, query.Not, query.Term, query.Prefix,
97 if isinstance(qry, (query.And, query.Not, query.Term, query.Prefix,
97 query.Wildcard, query.DateRange)):
98 query.Wildcard, query.DateRange)):
98 if not isinstance(qry, query.And):
99 if not isinstance(qry, query.And):
99 qry = [qry]
100 qry = [qry]
100
101
101 for term in qry:
102 for term in qry:
102 if isinstance(term, query.Not):
103 if isinstance(term, query.Not):
103 not_term = [z for z in term.leaves()][0]
104 not_term = [z for z in term.leaves()][0]
104 field = not_term.fieldname
105 field = not_term.fieldname
105 val = not_term.text
106 val = not_term.text
106 elif isinstance(term, query.DateRange):
107 elif isinstance(term, query.DateRange):
107 field = term.fieldname
108 field = term.fieldname
108 val = [term.startdate, term.enddate]
109 val = [term.startdate, term.enddate]
109 elif isinstance(term, query.NullQuery.__class__):
110 elif isinstance(term, query.NullQuery.__class__):
110 field = ''
111 field = ''
111 val = ''
112 val = ''
112 else:
113 else:
113 field = term.fieldname
114 field = term.fieldname
114 val = term.text
115 val = term.text
115 if field:
116 if field:
116 user_log = user_log.filter(get_filterion(field, val, term))
117 user_log = user_log.filter(get_filterion(field, val, term))
117 elif isinstance(qry, query.Or):
118 elif isinstance(qry, query.Or):
118 filters = []
119 filters = []
119 for term in qry:
120 for term in qry:
120 field = term.fieldname
121 field = term.fieldname
121 val = (term.text if not isinstance(term, query.DateRange)
122 val = (term.text if not isinstance(term, query.DateRange)
122 else [term.startdate, term.enddate])
123 else [term.startdate, term.enddate])
123 filters.append(get_filterion(field, val, term))
124 filters.append(get_filterion(field, val, term))
124 user_log = user_log.filter(or_(*filters))
125 user_log = user_log.filter(or_(*filters))
125
126
126 return user_log
127 return user_log
General Comments 0
You need to be logged in to leave comments. Login now