Merge lp:~ensoft-opensource/ensoft-sextant/auditing into lp:ensoft-sextant

Proposed by Patrick Stevens
Status: Merged
Approved by: James
Approved revision: 15
Merged at revision: 11
Proposed branch: lp:~ensoft-opensource/ensoft-sextant/auditing
Merge into: lp:ensoft-sextant
Diff against target: 208 lines (+77/-10)
4 files modified
src/sextant/__main__.py (+22/-2)
src/sextant/db_api.py (+37/-3)
src/sextant/query.py (+7/-0)
src/sextant/update_db.py (+11/-5)
To merge this branch: bzr merge lp:~ensoft-opensource/ensoft-sextant/auditing
Reviewer Review Type Date Requested Status
James Approve
Review via email: mp+231772@code.launchpad.net

Commit message

Add query `sextant audit`, and add metadata to Sextant programs (user id, username, date).

Description of the change

Add simple auditing of who is doing what with Sextant, by attaching to each program node a username (derived from the operating system), a user-id as returned by getuid, and a date-of-upload.
Add a new query, `sextant audit`, to make use of this.

To post a comment you must log in.
11. By Patrick Stevens <email address hidden>

Add sextant audit command

12. By Patrick Stevens <email address hidden>

Remove an obsolete line from a docstring

13. By Patrick Stevens <email address hidden>

Fix too-early error handling when server couldn't be found in Audit, and give a message if Audit sees an empty database.

Revision history for this message
James (jamesh-f) wrote :

Couple of minor style new lines sorry :)
Also wit the neo4j connection ensure if you remove the nargs that you also remove the indexing

14. By Patrick Stevens <email address hidden>

Remove extraneous newline

Revision history for this message
James (jamesh-f) wrote :

Good Good

review: Approve
15. By Patrick Stevens <email address hidden>

Merge better-program-names, fix file permissions

Revision history for this message
James (jamesh-f) wrote :

All looks good to me

review: Approve
Revision history for this message
ChrisD (gingerchris) :
Revision history for this message
Patrick Stevens (patrickas) :
Revision history for this message
ChrisD (gingerchris) :

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'doc/Program_upload_docs.mkd' (properties changed: +x to -x)
2=== modified file 'doc/wiki/Reference' (properties changed: +x to -x)
3=== modified file 'resources/sextant/web/interface.html' (properties changed: +x to -x)
4=== modified file 'resources/sextant/web/queryjavascript.js' (properties changed: +x to -x)
5=== modified file 'src/sextant/__main__.py' (properties changed: +x to -x)
6--- src/sextant/__main__.py 2014-08-26 11:01:03 +0000
7+++ src/sextant/__main__.py 2014-08-26 11:05:54 +0000
8@@ -151,11 +151,30 @@
9 help='port on which to serve Sextant Web',
10 default=web_port)
11
12+parsers['audit'] = subparsers.add_parser('audit', help='view usage of Sextant')
13+
14 for parser_key in parsers:
15- parsers[parser_key].add_argument('--remote-neo4j', metavar="URL", nargs=1,
16+ parsers[parser_key].add_argument('--remote-neo4j', metavar="URL",
17 help="URL of neo4j server", type=str,
18 default=remote_neo4j)
19
20+def _audit(args):
21+ try:
22+ audited = query.audit(args.remote_neo4j)
23+ except requests.exceptions.ConnectionError as e:
24+ logging.error('Connection error to server {}: {}'.format(args.remote_neo4j, e))
25+
26+ if not audited:
27+ print('No programs on database at {}.'.format(args.remote_neo4j))
28+ else:
29+ for program in audited:
30+ st = 'Program {} with {} functions uploaded by {} (id {}) on {}.'
31+ print(st.format(program.program_name, program.number,
32+ program.uploader, program.uploader_id,
33+ program.date))
34+
35+parsers['audit'].set_defaults(func=_audit)
36+
37 def _start_web(args):
38 # Don't import at top level -- this makes twisted dependency semi-optional,
39 # allowing non-web functionality to work with Python 3.
40@@ -182,7 +201,8 @@
41 try:
42 update_db.upload_program(namespace.input_file[0],
43 namespace.remote_neo4j,
44- alternative_name, not_object_file)
45+ alternative_name,
46+ not_object_file)
47 except requests.exceptions.ConnectionError as e:
48 logging.error('Connection error to server {}: {}'.format(namespace.remote_neo4j,
49 e))
50
51=== modified file 'src/sextant/db_api.py'
52--- src/sextant/db_api.py 2014-08-19 09:20:45 +0000
53+++ src/sextant/db_api.py 2014-08-26 11:05:54 +0000
54@@ -10,6 +10,10 @@
55
56 import re # for validation of function/program names
57 import logging
58+from datetime import datetime
59+import os
60+import getpass
61+from collections import namedtuple
62
63 from neo4jrestclient.client import GraphDatabase
64 import neo4jrestclient.client as client
65@@ -45,12 +49,16 @@
66 class AddToDatabase():
67 """Updates the database, adding functions/calls to a given program"""
68
69- def __init__(self, program_name='', sextant_connection=None):
70+ def __init__(self, program_name='', sextant_connection=None,
71+ uploader='', uploader_id='', date=None):
72 """
73 Object which can be used to add functions and calls to a new program
74 :param program_name: the name of the new program to be created
75 (must already be validated against Validator)
76 :param sextant_connection: the SextantConnection to use for connections
77+ :param uploader: string identifier of user who is uploading
78+ :param uploader_id: string Unix user-id of logged-in user
79+ :param date: string date of today
80 """
81 # program_name must be alphanumeric, to avoid injection attacks easily
82 if not Validator.validate(program_name):
83@@ -65,7 +73,11 @@
84 # we'll locally use db for short
85 db = self.parent_database_connection._db
86
87- parent_function = db.nodes.create(name=program_name, type='program')
88+ parent_function = db.nodes.create(name=program_name,
89+ type='program',
90+ uploader=uploader,
91+ uploader_id=uploader_id,
92+ date=date)
93 self._parent_id = parent_function.id
94
95 self._new_tx = db.transaction(using_globals=False, for_query=True)
96@@ -360,6 +372,10 @@
97 It can be used to create/delete/query programs.
98 """
99
100+ ProgramWithMetadata = namedtuple('ProgramWithMetadata',
101+ ['uploader', 'uploader_id',
102+ 'program_name', 'date', 'number'])
103+
104 def __init__(self, url):
105 self.url = url
106 self._db = GraphDatabase(url)
107@@ -381,9 +397,14 @@
108 if not Validator.validate(name_of_program):
109 raise ValueError(
110 "{} is not a valid program name".format(name_of_program))
111+
112+ uploader = getpass.getuser()
113+ uploader_id = os.getuid()
114
115 return AddToDatabase(sextant_connection=self,
116- program_name=name_of_program)
117+ program_name=name_of_program,
118+ uploader=uploader, uploader_id=uploader_id,
119+ date=str(datetime.now()))
120
121 def delete_program(self, name_of_program):
122 """
123@@ -435,6 +456,19 @@
124
125 return set(result)
126
127+ def programs_with_metadata(self):
128+ """
129+ Returns a set of namedtuples which represent the current database.
130+ The namedtuples have .uploader, .uploader_id, .program_name, .date,
131+ .number (of functions)
132+ :return: set of namedtuples
133+ """
134+ q = ("MATCH (base) WHERE base.type = 'program' "
135+ "MATCH (base)-[:subject]->(n)"
136+ "RETURN base.uploader, base.uploader_id, base.name, base.date, count(n)")
137+ result = self._db.query(q)
138+ return {self.ProgramWithMetadata(*res) for res in result}
139+
140 def check_program_exists(self, program_name):
141 """
142 Execute query to check whether a program with the given name exists.
143
144=== modified file 'src/sextant/export.py' (properties changed: +x to -x)
145=== modified file 'src/sextant/query.py' (properties changed: +x to -x)
146--- src/sextant/query.py 2014-08-26 11:01:03 +0000
147+++ src/sextant/query.py 2014-08-26 11:05:54 +0000
148@@ -13,6 +13,7 @@
149 from . import db_api
150 from .export import ProgramConverter
151
152+
153 def query(remote_neo4j, input_query, program_name=None, argument_1=None, argument_2=None, suppress_common=False):
154
155 try:
156@@ -94,5 +95,11 @@
157 else:
158 print('Nothing was returned from the query.')
159
160+
161+def audit(remote_neo4j):
162+ db = db_api.SextantConnection(remote_neo4j)
163+
164+ return db.programs_with_metadata()
165+
166 if __name__ == '__main__':
167 main()
168
169=== modified file 'src/sextant/update_db.py'
170--- src/sextant/update_db.py 2014-08-18 14:23:06 +0000
171+++ src/sextant/update_db.py 2014-08-26 11:05:54 +0000
172@@ -13,7 +13,9 @@
173 import logging
174
175
176-def upload_program(file_path, db_url, alternative_name=None, not_object_file=False):
177+def upload_program(file_path, db_url,
178+ alternative_name=None,
179+ not_object_file=False):
180 """
181 Uploads a program to the remote database.
182 Raises requests.exceptions.ConnectionError if the server didn't exist.
183@@ -40,15 +42,19 @@
184 "please delete the previous one with the same name"
185 " and retry, or rename the input file.")
186
187- parsed_objects = get_parsed_objects(filepath=file_path, sections_to_view=['.text'],
188- not_object_file=not_object_file, ignore_function_pointers=False)
189+ parsed_objects = get_parsed_objects(filepath=file_path,
190+ sections_to_view=['.text'],
191+ not_object_file=not_object_file,
192+ ignore_function_pointers=False)
193
194 logging.info('Objdump has parsed!')
195
196 if alternative_name is None:
197- program_representation = connection.new_program(Validator.sanitise(file_path))
198+ program_representation = connection.new_program(
199+ Validator.sanitise(file_path))
200 else:
201- program_representation = connection.new_program(Validator.sanitise(alternative_name))
202+ program_representation = connection.new_program(
203+ Validator.sanitise(alternative_name))
204
205 for obj in parsed_objects:
206 for called in obj.what_do_i_call:
207
208=== modified file 'src/sextant/web/server.py' (properties changed: +x to -x)

Subscribers

People subscribed via source and target branches