From f17d53296e1307204a074e9126947a191d803ea4 Mon Sep 17 00:00:00 2001 From: dave Date: Tue, 15 Aug 2017 20:26:03 -0700 Subject: [PATCH] Support auth --- pysonic/daemon.py | 16 +++++++++++++++- pysonic/database.py | 26 ++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/pysonic/daemon.py b/pysonic/daemon.py index 8101c45..658f320 100644 --- a/pysonic/daemon.py +++ b/pysonic/daemon.py @@ -14,6 +14,9 @@ def main(): parser.add_argument('-p', '--port', default=8080, type=int, help="tcp port to listen on") parser.add_argument('-d', '--dirs', required=True, nargs='+', help="new music dirs to share") + parser.add_argument('-u', '--user', nargs='+', type=lambda x: x.split(":"), default=[], + help="user:password pairs for auth") + parser.add_argument('--disable-auth', action="store_true", help="disable authentication") parser.add_argument('-s', '--database-path', default="./db.sqlite", help="path to persistent sqlite database") parser.add_argument('--debug', action="store_true", help="enable development options") @@ -35,11 +38,22 @@ def main(): pass library.update() + for username, password in args.user: + db.add_user(username, password) + logging.warning("Libraries: {}".format([i["name"] for i in library.get_libraries()])) logging.warning("Artists: {}".format([i["name"] for i in library.get_artists()])) logging.warning("Albums: {}".format(len(library.get_albums()))) - cherrypy.tree.mount(PysonicApi(db, library, args), '/rest/', {'/': {}}) + api_config = {} + if args.disable_auth: + logging.warning("starting up with auth disabled") + else: + api_config.update({'tools.auth_basic.on': True, + 'tools.auth_basic.realm': 'pysonic', + 'tools.auth_basic.checkpassword': db.validate_password}) + cherrypy.tree.mount(PysonicApi(db, library, args), '/rest/', {'/': api_config}) + cherrypy.config.update({ 'sessionFilter.on': True, 'tools.sessions.on': True, diff --git a/pysonic/database.py b/pysonic/database.py index 650bdea..d2d410e 100644 --- a/pysonic/database.py +++ b/pysonic/database.py @@ -2,6 +2,7 @@ import os import json import sqlite3 import logging +from hashlib import sha512 from itertools import chain from contextlib import closing @@ -58,6 +59,17 @@ class PysonicDatabase(object): else: # Migrate if old db exists version = int(cursor.execute("SELECT * FROM meta WHERE key='db_version';").fetchone()['value']) + if version < 1: + logging.warning("migrating database to v1 from %s", version) + users_table = """CREATE TABLE 'users' ( + 'id' INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + 'username' TEXT UNIQUE NOT NULL, + 'password' TEXT NOT NULL, + 'admin' BOOLEAN DEFAULT 0, + 'email' TEXT)""" + cursor.execute(users_table) + version = 1 + cursor.execute("""UPDATE meta SET value=? WHERE key="db_version";""", (str(version), )) logging.warning("db schema is version {}".format(version)) # Virtual file tree @@ -115,3 +127,17 @@ class PysonicDatabase(object): if metadata: return json.loads(metadata) return {} + + def hashit(self, unicode_string): + return sha512(unicode_string.encode('UTF-8')).hexdigest() + + def validate_password(self, realm, username, password): + with closing(self.db.cursor()) as cursor: + users = cursor.execute("SELECT * FROM users WHERE username=? AND password=?;", + (username, self.hashit(password))).fetchall() + return bool(users) + + def add_user(self, username, password, is_admin=False): + with closing(self.db.cursor()) as cursor: + cursor.execute("REPLACE INTO users (username, password, admin) VALUES (?, ?, ?)", + (username, self.hashit(password), is_admin)).fetchall()