raylu 11 жил өмнө
parent
commit
fa76911e18
6 өөрчлөгдсөн 63 нэмэгдсэн , 44 устгасан
  1. 5 0
      api/db.py
  2. 19 15
      api/server.py
  3. 21 16
      client/sysvitals_client
  4. 11 7
      web/db.py
  5. 6 5
      web/schema.sql
  6. 1 1
      web/templates/home.html

+ 5 - 0
api/db.py

@@ -65,6 +65,11 @@ def query_one(cur, sql, *args):
 		raise Exception('got more than one value for query', sql, args)
 	return rval
 
+def get_api_key(group_id):
+	with cursor() as cur:
+		api_key = query_one(cur, 'SELECT api_key FROM groups WHERE id = %s', group_id)[0]
+	return api_key
+
 def create_server(group_id, hostname):
 	with cursor() as cur:
 		server_id = query_one(cur, 'INSERT INTO servers (group_id, hostname) VALUES(%s, %s) RETURNING id',

+ 19 - 15
api/server.py

@@ -88,14 +88,14 @@ def application(environ, start_response):
 
 def get_raw(split, query, environ):
 	try:
-		group = int(split[1])
+		group_id = int(split[1])
 		server_id = int(split[3])
 		start = datetime.datetime.strptime(query['start'], '%Y-%m-%d').date()
 		end = datetime.datetime.strptime(query['end'], '%Y-%m-%d').date()
 	except (IndexError, KeyError, ValueError):
 		raise HTTPException(400, '')
 
-	server_dir = path.join(DATA_DIR, str(group), str(server_id))
+	server_dir = path.join(DATA_DIR, str(group_id), str(server_id))
 	rval = {}
 	c = start
 	while c <= end:
@@ -179,8 +179,8 @@ def get_stats(split, query, environ):
 	return stats
 
 def post_datum(split, query, environ):
+	group_id = get_group(split, environ)
 	try:
-		group = int(split[1])
 		server_id = int(split[3])
 	except (IndexError, ValueError):
 		raise HTTPException(400, '')
@@ -189,7 +189,7 @@ def post_datum(split, query, environ):
 		diff = set(body.keys()).symmetric_difference(set(fileio.TEMPLATE.keys()))
 		raise HTTPException(400, 'post body had missing or extra keys: ' + ','.join(diff))
 
-	server_dir = path.join(DATA_DIR, str(group), str(server_id))
+	server_dir = path.join(DATA_DIR, str(group_id), str(server_id))
 	try:
 		os.makedirs(server_dir)
 	except OSError as e:
@@ -232,27 +232,31 @@ def post_datum(split, query, environ):
 	return {'status': 'ok'}
 
 def get_servers(split, query, environ):
-	try:
-		group = int(split[1])
-	except (IndexError, ValueError):
-		raise HTTPException(400, '')
-
-	return db.get_servers(group)
+	group_id = get_group(split, environ)
+	return db.get_servers(group_id)
 
 def register_server(split, query, environ):
-	try:
-		group = int(split[1])
-	except (IndexError, ValueError):
-		raise HTTPException(400, '')
+	group_id = get_group(split, environ)
 	body = load_json_body(environ)
 	try:
 		hostname = body['hostname']
 	except KeyError:
 		raise HTTPException(400, 'post body didn\'t contain "hostname" key')
 
-	server_id = db.create_server(group, hostname)
+	server_id = db.create_server(group_id, hostname)
 	return {'server_id': server_id}
 
+def get_group(split, environ):
+	try:
+		group_id = int(split[1])
+	except (IndexError, ValueError):
+		raise HTTPException(400, '/v1/[group_id] - group_id was not valid int')
+	if 'HTTP_AUTHORIZATION' not in environ:
+		raise HTTPException(401, 'no api key passed in Authorization header')
+	if db.get_api_key(group_id) != environ['HTTP_AUTHORIZATION']:
+		raise HTTPException(403, 'api key did not match')
+	return group_id
+
 def load_json_body(environ):
 	try:
 		body = json.load(environ['wsgi.input'])

+ 21 - 16
client/sysvitals_client

@@ -16,6 +16,7 @@ if is_root:
 	cfg_path = '/etc/sysvitals.cfg'
 else:
 	cfg_path = path.join(path.dirname(path.abspath(__file__)), 'sysvitals.cfg')
+default_api_server = 'http://localhost:8892'
 
 def post():
 	if not config.read(cfg_path):
@@ -43,11 +44,10 @@ def post():
 			'used': usage.used,
 		}
 
-	api_server = config.get('client', 'api_server')
+	api_key = config.get('client', 'api_key')
 	group_id = config.get('client', 'group_id')
 	server_id = config.get('client', 'server_id')
-	url = '%s/v1/%s/datum/%s' % (api_server, group_id, server_id)
-	urllib2.urlopen(url, json.dumps(datum))
+	__query(group_id, 'datum/' + server_id, api_key, datum)
 
 def configure():
 	if not is_root:
@@ -59,7 +59,6 @@ def configure():
 		if raw_input('are you sure you want to continue? [y/N] ') != 'y':
 			return
 
-	default_api_server = 'http://localhost:8892'
 	config.add_section('client')
 	config.set('client', 'api_server', default_api_server)
 	group_id = None
@@ -71,14 +70,11 @@ def configure():
 			group_id = None
 			print 'group_id should be numeric'
 	config.set('client', 'group_id', group_id)
+	api_key = raw_input('api_key: ')
+	config.set('client', 'api_key', api_key)
 
 	print 'checking existing servers'
-	try:
-		url = '%s/v1/%s/servers' % (default_api_server, group_id)
-		r = urllib2.urlopen(url)
-		servers = json.load(r)
-	except Exception as e:
-		sys.exit('an error occurred while fetching existing servers:\n%r' % e)
+	servers = __query(group_id, 'servers', api_key)
 	our_hostname = socket.getfqdn()
 	server_id = None
 	matches = 0
@@ -98,18 +94,27 @@ def configure():
 
 	if server_id is None:
 		print 'getting a brand new server id'
-		try:
-			url = '%s/v1/%s/register_server' % (default_api_server, group_id)
-			r = urllib2.urlopen(url, json.dumps({'hostname': our_hostname}))
-			server_id = json.load(r)['server_id']
-		except Exception as e:
-			sys.exit('an error occurred while getting the new server id:\n%r' % e)
+		response = __query(group_id, 'register_server', api_key, {'hostname': our_hostname})
+		server_id = response['server_id']
 	config.set('client', 'server_id', server_id)
 
 	print 'writing config to', cfg_path
 	with open(cfg_path, 'w') as f:
 		config.write(f)
 
+def __query(group_id, endpoint, api_key, data=None):
+	api_server = config.get('client', 'api_server')
+	url = '%s/v1/%s/%s' % (api_server, group_id, endpoint)
+	if data is not None:
+		data = json.dumps(data)
+	req = urllib2.Request(url, data, {'Authorization': api_key})
+	try:
+		return json.load(urllib2.urlopen(req))
+	except urllib2.HTTPError as e:
+		sys.exit('http error (%d):\n%s' % (e.code, e.read()))
+	except Exception as e:
+		sys.exit('an error occurred while querying%s:\n%r' % (endpoint, e))
+
 if len(sys.argv) > 1 and sys.argv[1] == '--configure':
 	configure()
 else:

+ 11 - 7
web/db.py

@@ -1,8 +1,9 @@
+import binascii
 from collections import defaultdict
 import hashlib
 import hmac
-import binascii
 import os
+import time
 
 import tornado.gen
 import psycopg2
@@ -29,14 +30,14 @@ class MomokoDB:
 	@tornado.gen.coroutine
 	def create_user(self, username, password):
 		hashed_password, salt = hash_pw(password)
-		query = 'INSERT INTO users (username, password, salt) VALUES (%s, %s, %s) RETURNING id;'
-		cursor = yield self.execute(query, username, hashed_password, salt)
+		sql = 'INSERT INTO users (username, password, salt) VALUES (%s, %s, %s) RETURNING id;'
+		cursor = yield self.execute(sql, username, hashed_password, salt)
 		return cursor.fetchone()['id']
 
 	@tornado.gen.coroutine
 	def check_user(self, username, password):
-		query = 'SELECT id, username, password, salt FROM users WHERE username=%s;'
-		cursor = yield self.execute(query, username)
+		sql = 'SELECT id, username, password, salt FROM users WHERE username=%s;'
+		cursor = yield self.execute(sql, username)
 		user = cursor.fetchone()
 		if not user:
 			return
@@ -47,7 +48,10 @@ class MomokoDB:
 
 	@tornado.gen.coroutine
 	def create_group(self, user_id, group_name):
-		cursor = yield self.execute('INSERT INTO groups (name) VALUES(%s) RETURNING id;', group_name)
+		hmac_msg = ('%d%s%d' % (user_id, group_name, time.time())).encode('utf-8')
+		h = hmac.new(config.cookie_secret.encode('utf-8'), hmac_msg, hashlib.sha1)
+		sql = 'INSERT INTO groups (name, api_key) VALUES(%s, %s) RETURNING id;'
+		cursor = yield self.execute(sql, group_name, h.hexdigest())
 		group_id = cursor.fetchone()['id']
 		yield self.execute('INSERT INTO user_groups (user_id, group_id) VALUES(%s, %s);', user_id, group_id)
 		return group_id
@@ -55,7 +59,7 @@ class MomokoDB:
 	@tornado.gen.coroutine
 	def get_groups(self, user_id):
 		cursor = yield self.execute('''
-			SELECT groups.id, groups.name FROM user_groups
+			SELECT groups.id, groups.name, api_key FROM user_groups
 			JOIN groups ON user_groups.group_id = groups.id
 			WHERE user_id = %s;
 		''', user_id)

+ 6 - 5
web/schema.sql

@@ -3,11 +3,6 @@ DROP TABLE IF EXISTS users;
 DROP TABLE IF EXISTS servers;
 DROP TABLE IF EXISTS groups;
 
-CREATE TABLE groups (
-	id serial PRIMARY KEY,
-	name varchar(32) NOT NULL
-);
-
 CREATE TABLE users (
 	id serial PRIMARY KEY,
 	username varchar(32) NOT NULL,
@@ -16,6 +11,12 @@ CREATE TABLE users (
 	UNIQUE (username)
 );
 
+CREATE TABLE groups (
+	id serial PRIMARY KEY,
+	name varchar(32) NOT NULL,
+	api_key char(40) NOT NULL
+);
+
 CREATE TABLE user_groups (
 	user_id integer NOT NULL references users(id),
 	group_id integer NOT NULL references groups(id),

+ 1 - 1
web/templates/home.html

@@ -4,7 +4,7 @@
 	{% if current_user %}
 		{% for group in groups %}
 			<p>
-				{{ group['id'] }}: {{ group['name'] }}
+				{{ group['id'] }}: {{ group['name'] }} ({{ group['api_key'] }})
 				{% for server in servers[group['id']] %}
 					<br>
 					<a href="/stats/{{ group['id'] }}/{{ server['id'] }}">{{ server['hostname'] }}</a>