diff --git a/photoapp/api.py b/photoapp/api.py index 680690b..c43f3a6 100644 --- a/photoapp/api.py +++ b/photoapp/api.py @@ -54,3 +54,21 @@ class PhotosApiV1(object): raise cherrypy.HTTPError(404) return cherrypy.lib.static.serve_file(os.path.abspath(os.path.join("./library", f.path)), f.format)#TODO no hardcode path + + @cherrypy.expose + @cherrypy.tools.json_out() + def user(self, username=None, password_hash=None): + if username is None: # list all users + return [u.to_json() for u in db().query(User).all()] + elif username and cherrypy.request.method == "DELETE": # delete user + u = db().query(User).filter(User.name == username).first() + if not u: + raise cherrypy.HTTPError(404) + db().delete(u) + elif username and password_hash: # create/update user + u = db().query(User).filter(User.name == username).first() + if u: + u.password = password_hash + else: + db().add(User(name=username, password=password_hash)) + return "ok" diff --git a/photoapp/cli.py b/photoapp/cli.py index c18b066..d841102 100644 --- a/photoapp/cli.py +++ b/photoapp/cli.py @@ -3,6 +3,7 @@ import requests from requests.exceptions import HTTPError from photoapp.utils import get_extension from photoapp.types import known_extensions +from photoapp.common import pwhash class PhotoApiClient(object): @@ -14,12 +15,31 @@ class PhotoApiClient(object): return self.get("byhash", params={"sha": sha}).json() def get(self, url, **params): - resp = self.session.get(self.base_url + "/api/v1/" + url, **params) + return self.do("get", url, **params) + + def post(self, url, **params): + return self.do("post", url, **params) + + def delete(self, url, **params): + return self.do("delete", url, **params) + + def do(self, method, url, **params): + resp = getattr(self.session, method)(self.base_url + "/api/v1/" + url, **params) resp.raise_for_status() return resp + def create_user(self, username, password): + return self.post("user", data={"username": username, + "password_hash": pwhash(password)}) -def main(): + def list_users(self): + return self.get("user") + + def delete_user(self, username): + return self.delete("user", params={"username": username}) + + +def get_args(): parser = argparse.ArgumentParser(description="photo library cli") parser.add_argument("-s", "--host", required=True, help="photo library server address") @@ -33,7 +53,24 @@ def main(): p_ingest.add_argument("files", nargs="+", help="files to import") p_ingest.add_argument("-c", "--copy-of", help="existing uuid the imported images will be placed under") - args = parser.parse_args() + p_adduser = sp_action.add_parser("user", help="user manipulation functions") + p_useraction = p_adduser.add_subparsers(dest="action_user", help="action to take") + + p_create = p_useraction.add_parser('create', help='create user') + p_create.add_argument("-u", "--username", help="username", required=True) + p_create.add_argument("-p", "--password", help="password", required=True) + + p_useraction.add_parser('list', help='list users') + + p_delete = p_useraction.add_parser('delete', help='delete users') + p_delete.add_argument("-u", "--username", help="username", required=True) + + return parser.parse_args() + + +def main(): + args = get_args() + print(args) client = PhotoApiClient(args.host) @@ -68,6 +105,18 @@ def main(): else: raise + elif args.action == "ingest": + pass + + elif args.action == "user": + if args.action_user == "create": + print(client.create_user(args.username, args.password).json()) + elif args.action_user == "list": + for u in client.list_users().json(): + print(f"{u['name']}: {u['status']}") + elif args.action_user == "delete": + print(client.delete_user(args.username).json()) + if __name__ == "__main__": main() diff --git a/photoapp/types.py b/photoapp/types.py index 932c247..4e12351 100644 --- a/photoapp/types.py +++ b/photoapp/types.py @@ -121,3 +121,6 @@ class User(Base): name = Column(String(length=64), unique=True) password = Column(String(length=64)) # sha256 status = Column(Enum(UserStatus), default=UserStatus.normal) + + def to_json(self): + return dict(name=self.name, status=self.status.name)