From 646b5518bbd33c6979cac548adca2416d401833a Mon Sep 17 00:00:00 2001 From: Tom Galloway Date: Wed, 16 Jan 2013 13:08:48 +0000 Subject: [PATCH 1/3] withsqlite is now retrieved from github. Manage User & Groups pages now display correctly but don't do anything yet. --- .gitignore | 1 + Makefile | 9 ++++++++- modules/installed/system/users.py | 19 ++++++++++--------- start.sh | 1 - 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index 3b943e1bb..d5be6856d 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,4 @@ cherrypy.config data/users.sqlite3 predepend build/ +*.pid \ No newline at end of file diff --git a/Makefile b/Makefile index 72d916965..f5bf865b9 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,7 @@ #SHELL := /bin/bash MAKE=make BUILD_DIR = build +VENDOR_DIR = vendor #TODO: add install target @@ -10,12 +11,15 @@ COMPRESSED_CSS := $(patsubst %.css,%.tiny.css,$(CSS)) PWD=`pwd` ## Catch-all tagets -default: predepend cfg cherrypy.config dirs template css docs dbs $(BUILD_DIR)/exmachina #$(BUILD_DIR)/bjsonrpc +default: predepend cfg cherrypy.config dirs template css docs dbs $(BUILD_DIR)/exmachina $(VENDOR_DIR)/withsqlite #$(BUILD_DIR)/bjsonrpc all: default build: mkdir -p $(BUILD_DIR) +vendor: + mkdir -p $(VENDOR_DIR) + predepend: sudo sh -c "apt-get install augeas-tools python-bjsonrpc python-augeas python-simplejson pandoc python-cheetah" touch predepend @@ -23,6 +27,9 @@ predepend: $(BUILD_DIR)/exmachina: build git clone git://github.com/tomgalloway/exmachina $(BUILD_DIR)/exmachina +$(VENDOR_DIR)/withsqlite: vendor + git clone git://github.com/jvasile/withsqlite.git $(VENDOR_DIR)/withsqlite + $(BUILD_DIR)/bjsonrpc: build git clone git://github.com/deavid/bjsonrpc.git $(BUILD_DIR)/bjsonrpc diff --git a/modules/installed/system/users.py b/modules/installed/system/users.py index c4ac97771..81e747b2f 100644 --- a/modules/installed/system/users.py +++ b/modules/installed/system/users.py @@ -5,22 +5,23 @@ from plugin_mount import PagePlugin, FormPlugin import cfg from forms import Form from util import * +from pprint import pprint class users(PagePlugin): order = 20 # order of running init in PagePlugins def __init__(self, *args, **kwargs): PagePlugin.__init__(self, *args, **kwargs) self.register_page("sys.users") + self.register_page("sys.users.add") + self.register_page("sys.users.edit") @cherrypy.expose @require() def index(self): - parts = self.forms('/sys/config') - parts['title']=_("Manage Users and Groups") - return self.fill_template(**parts) + return self.fill_template(title="Manage Users and Groups", sidebar_right="""Add User
Edit Users""") class add(FormPlugin, PagePlugin): - url = ["/sys/users"] + url = ["/sys/users/add"] order = 30 sidebar_left = '' @@ -63,10 +64,10 @@ class add(FormPlugin, PagePlugin): msg = add_message(msg, "%s saved." % username) main = self.make_form(username, name, email, message=msg) - return self.fill_template(title="", main=main, sidebar_left=self.sidebar_left, sidebar_right=self.sidebar_right) + return self.fill_template(title="Manage Users and Groups", main=main, sidebar_left=self.sidebar_left, sidebar_right=self.sidebar_right) class edit(FormPlugin, PagePlugin): - url = ["/sys/users"] + url = ["/sys/users/edit"] order = 35 sidebar_left = '' @@ -77,7 +78,7 @@ class edit(FormPlugin, PagePlugin): system.

Deleting users is permanent!

""" % (cfg.product_name, cfg.box_name)) def main(self, msg=''): - users = cfg.users.keys() + users = cfg.users add_form = Form(title=_("Edit or Delete User"), action="/sys/users/edit", message=msg) add_form.html('Delete
') for uname in sorted(users.keys()): @@ -114,7 +115,7 @@ class edit(FormPlugin, PagePlugin): else: msg.add = _("Must specify at least one valid, existing user.") main = self.make_form(msg=msg.text) - return self.fill_template(title="", main=main, sidebar_left=self.sidebar_left, sidebar_right=self.sidebar_right) + return self.fill_template(title="Manage Users and Groups", main=main, sidebar_left=self.sidebar_left, sidebar_right=self.sidebar_right) sidebar_right = '' u = cfg.users[kwargs['username']] @@ -125,4 +126,4 @@ class edit(FormPlugin, PagePlugin): main = _("""Edit User '%s'""" % u['username']) sidebar_right = '' - return self.fill_template(title="", main=main, sidebar_left=self.sidebar_left, sidebar_right=sidebar_right) + return self.fill_template(title="Manage Users and Groups", main=main, sidebar_left=self.sidebar_left, sidebar_right=sidebar_right) diff --git a/start.sh b/start.sh index 1a16d1a5f..4be20c1b6 100755 --- a/start.sh +++ b/start.sh @@ -1,7 +1,6 @@ #! /bin/sh PYTHONPATH=build/exmachina:$PYTHONPATH -PYTHONPATH=build/bjsonrpc:$PYTHONPATH export PYTHONPATH From a312b6d288dc2f978237c1d5e309972d3c873327 Mon Sep 17 00:00:00 2001 From: Tom Galloway Date: Wed, 16 Jan 2013 15:05:04 +0000 Subject: [PATCH 2/3] Removed changes to get withsqlite from github as it's a different version? --- Makefile | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/Makefile b/Makefile index f5bf865b9..72d916965 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,6 @@ #SHELL := /bin/bash MAKE=make BUILD_DIR = build -VENDOR_DIR = vendor #TODO: add install target @@ -11,15 +10,12 @@ COMPRESSED_CSS := $(patsubst %.css,%.tiny.css,$(CSS)) PWD=`pwd` ## Catch-all tagets -default: predepend cfg cherrypy.config dirs template css docs dbs $(BUILD_DIR)/exmachina $(VENDOR_DIR)/withsqlite #$(BUILD_DIR)/bjsonrpc +default: predepend cfg cherrypy.config dirs template css docs dbs $(BUILD_DIR)/exmachina #$(BUILD_DIR)/bjsonrpc all: default build: mkdir -p $(BUILD_DIR) -vendor: - mkdir -p $(VENDOR_DIR) - predepend: sudo sh -c "apt-get install augeas-tools python-bjsonrpc python-augeas python-simplejson pandoc python-cheetah" touch predepend @@ -27,9 +23,6 @@ predepend: $(BUILD_DIR)/exmachina: build git clone git://github.com/tomgalloway/exmachina $(BUILD_DIR)/exmachina -$(VENDOR_DIR)/withsqlite: vendor - git clone git://github.com/jvasile/withsqlite.git $(VENDOR_DIR)/withsqlite - $(BUILD_DIR)/bjsonrpc: build git clone git://github.com/deavid/bjsonrpc.git $(BUILD_DIR)/bjsonrpc From c4cddbfc0e42afb8e69dce08b561ac00b0f07b35 Mon Sep 17 00:00:00 2001 From: Tom Galloway Date: Mon, 21 Jan 2013 10:30:52 +0000 Subject: [PATCH 3/3] Changes to get user management screens started. Updated UserStore to add all expected functions. Added tests for these functions. --- model.py | 9 +-- modules/installed/lib/user_store.py | 38 +++++++++++- modules/installed/system/users.py | 24 ++++---- test.sh | 9 +++ tests/test_user_store.py | 86 ++++++++++++++++++++++++++++ tests/testdata/users.sqlite3 | Bin 0 -> 3072 bytes 6 files changed, 148 insertions(+), 18 deletions(-) create mode 100755 test.sh create mode 100644 tests/test_user_store.py create mode 100644 tests/testdata/users.sqlite3 diff --git a/model.py b/model.py index d3807d0b1..e0b3cd20d 100644 --- a/model.py +++ b/model.py @@ -1,14 +1,15 @@ class User(dict): - """ Every user must have keys for a username, name, password (this + """ Every user must have keys for a username, name, passphrase (this is a md5 hash of the password), groups, and an email address. They can be blank or None, but the keys must exist. """ def __init__(self, dict=None): - for key in ['username', 'name', 'password', 'email']: + for key in ['username', 'name', 'passphrase', 'email']: self[key] = '' for key in ['groups']: self[key] = [] - for key in dict: - self[key] = dict[key] + if dict: + for key in dict: + self[key] = dict[key] def __getattr__(self, attr): return None diff --git a/modules/installed/lib/user_store.py b/modules/installed/lib/user_store.py index a4042c102..236b73a8f 100644 --- a/modules/installed/lib/user_store.py +++ b/modules/installed/lib/user_store.py @@ -12,10 +12,42 @@ class UserStore(UserStoreModule, sqlite_db): self.db_file = cfg.user_db sqlite_db.__init__(self, self.db_file, autocommit=True) self.__enter__() + def close(self): - self.__exit__() - def expert(self): - return False + self.__exit__(None,None,None) + + def expert(self, username=None): + groups = self.attr(username,"groups") + if not groups: + return False + return 'expert' in groups + + def attr(self, username=None, field=None): + return self.get(username)[field] + + def get(self,username=None): + return User(sqlite_db.get(self,username)) + + def exists(self, username=None): + try: + user = self.get(username) + if not user: + return False + elif user["username"]=='': + return False + return True + except TypeError: + return False + + def remove(self,username=None): + self.__delitem__(username) + self.commit() + + def get_all(self): + return self.items() + + def set(self,username=None,user=None): + sqlite_db.__setitem__(self,username, user) class UserStoreOld(): #class UserStore(UserStoreModule): diff --git a/modules/installed/system/users.py b/modules/installed/system/users.py index 81e747b2f..63d9c7631 100644 --- a/modules/installed/system/users.py +++ b/modules/installed/system/users.py @@ -47,23 +47,25 @@ class add(FormPlugin, PagePlugin): return form.render() def process_form(self, username=None, name=None, email=None, md5_password=None, **kwargs): - msg = '' + msg = Message() - if not username: msg = add_message(msg, _("Must specify a username!")) - if not md5_password: msg = add_message(msg, _("Must specify a password!")) + if not username: msg.add = _("Must specify a username!") + if not md5_password: msg.add = _("Must specify a password!") - if username in cfg.users: - msg = add_message(msg, _("User already exists!")) + if username in cfg.users.keys(): + msg.add = _("User already exists!") else: try: - cfg.users[username]= User(dict={'username':username, 'name':name, 'email':email, 'password':md5_password}) + di = {'username':username, 'name':name, 'email':email, 'passphrase':md5_password} + new_user = User(dict=di) + cfg.users.set(username,new_user) except: - msg = add_message(msg, _("Error storing user!")) + msg.add = _("Error storing user!") if not msg: - msg = add_message(msg, "%s saved." % username) - - main = self.make_form(username, name, email, message=msg) + msg.add = _("%s saved." % username) + cfg.log(msg.text) + #main = self.make_form(username, name, email, msg=msg.text) return self.fill_template(title="Manage Users and Groups", main=main, sidebar_left=self.sidebar_left, sidebar_right=self.sidebar_right) class edit(FormPlugin, PagePlugin): @@ -114,7 +116,7 @@ class edit(FormPlugin, PagePlugin): msg.add(_("User %s does not exist." % username)) else: msg.add = _("Must specify at least one valid, existing user.") - main = self.make_form(msg=msg.text) + #main = self.make_form(msg=msg.text) return self.fill_template(title="Manage Users and Groups", main=main, sidebar_left=self.sidebar_left, sidebar_right=self.sidebar_right) sidebar_right = '' diff --git a/test.sh b/test.sh new file mode 100755 index 000000000..4b6f701d4 --- /dev/null +++ b/test.sh @@ -0,0 +1,9 @@ +#! /bin/sh + +PYTHONPATH=build/exmachina:$PYTHONPATH +PYTHONPATH=modules/installed/lib:$PYTHONPATH +PYTHONPATH=vendor:$PYTHONPATH + +export PYTHONPATH + +python tests/test_user_store.py diff --git a/tests/test_user_store.py b/tests/test_user_store.py new file mode 100644 index 000000000..e6a3e8433 --- /dev/null +++ b/tests/test_user_store.py @@ -0,0 +1,86 @@ +#! /usr/bin/env python +# -*- mode: python; mode: auto-fill; fill-column: 80 -*- + +import user_store +from logger import Logger +import cfg +import unittest +import cherrypy +import plugin_mount +import os +from model import User +cfg.log = Logger() + +cherrypy.log.access_file = None + +class UserStore(unittest.TestCase): + """Test each function of user_store to confirm they work as expected""" + + def setUp(self): + cfg.user_db = os.path.join(cfg.file_root, "tests/testdata/users"); + self.userstore = plugin_mount.UserStoreModule.get_plugins()[0] + + def tearDown(self): + for user in self.userstore.get_all(): + self.userstore.remove(user[0]) + self.userstore.close() + + def test_user_does_not_exist(self): + self.assertEqual(self.userstore.exists("notausername"),False) + + def test_user_does_exist(self): + self.add_user("isausername", False) + self.assertEqual(self.userstore.exists("isausername"),True) + + def test_add_user(self): + self.assertEqual(len(self.userstore.items()),0) + self.add_user("test_user", False) + self.assertEqual(len(self.userstore.items()),1) + + def test_user_is_in_expert_group(self): + self.add_user("test_user", True) + self.assertEqual(self.userstore.expert("test_user"),True) + + def test_user_is_not_in_expert_group(self): + self.add_user("test_user", False) + self.assertEqual(self.userstore.expert("test_user"),False) + + def test_user_removal(self): + self.assertEqual(len(self.userstore.items()),0) + self.add_user("test_user", False) + self.assertEqual(len(self.userstore.items()),1) + self.userstore.remove("test_user") + self.assertEqual(len(self.userstore.items()),0) + + def test_get_user_email_attribute(self): + self.add_user("test_user", False,"test@home") + self.assertEqual(self.userstore.attr("test_user","email"),"test@home") + + def test_get_user(self): + test_user = self.add_user("test_user", False) + self.assertEqual(self.userstore.get("test_user"),test_user) + + def test_get_all_users(self): + self.add_user("test_user1", False) + self.add_user("test_user2", False) + self.assertEqual(len(self.userstore.get_all()),2) + + def add_user(self, test_username, add_to_expert_group, email=''): + test_user = self.create_user(test_username, email) + if add_to_expert_group: + test_user = self.add_user_to_expert_group(test_user) + self.userstore.set(test_username,test_user) + return test_user + + def create_user(self, username, email=''): + test_user = User() + test_user["username"] = username + test_user["email"] = email + return test_user + + def add_user_to_expert_group(self, user): + user["groups"] = ["expert"] + return user + +if __name__ == "__main__": + unittest.main() \ No newline at end of file diff --git a/tests/testdata/users.sqlite3 b/tests/testdata/users.sqlite3 new file mode 100644 index 0000000000000000000000000000000000000000..03782a7d7f4282049ab931c01f0df26b5534545f GIT binary patch literal 3072 zcmWFz^vNtqRY=P(%1ta$FlJz3U}R))P*7lCU`%FUU|@ zBSl{3KMYLF-3-j#%oouVjB-aqU^E0EA>hKuCN3_{m|2pTl#^Oql3xTNn4E)L9Yb6d zLL8lZTou6L3L4p|l?o-P6(tI#d6|W!sX7W}i8){aO-&{?aa(alhRnQ_)QaN59ANH_ zPb@9T2lL~>+T#tu6ew@A@Ph1S0cP|*W(kNhM#<3-7!3hn2)v+qD70a*p$Ll@4TZQs eW`pwoC+0^qv}V-k(GVC7fq@DEIW&v;5PSg0W;EIW literal 0 HcmV?d00001