Author: tmckay
Date: 2012-04-11 19:36:57 +0000 (Wed, 11 Apr 2012)
New Revision: 5295
Added:
trunk/cumin/model/access/
trunk/cumin/model/access/persona.xml
trunk/cumin/python/cumin/persona.py
Modified:
trunk/cumin/Makefile
trunk/cumin/bin/cumin-admin
trunk/cumin/bin/cumin-web
trunk/cumin/etc/cumin.conf
trunk/cumin/python/cumin/account/widgets.py
trunk/cumin/python/cumin/account/widgets.strings
trunk/cumin/python/cumin/admin.py
trunk/cumin/python/cumin/config.py
trunk/cumin/python/cumin/grid/main.py
trunk/cumin/python/cumin/grid/pool.py
trunk/cumin/python/cumin/inventory/main.py
trunk/cumin/python/cumin/inventory/system.py
trunk/cumin/python/cumin/main.py
trunk/cumin/python/cumin/messaging/main.py
trunk/cumin/python/cumin/usergrid/main.py
trunk/cumin/python/cumin/widgets.py
trunk/wooly/python/wooly/__init__.py
trunk/wooly/python/wooly/pages.py
trunk/wooly/python/wooly/table.py
trunk/wooly/python/wooly/widgets.py
Log:
Provide enforcement in the UI for user roles.
BZ769753
Modified: trunk/cumin/Makefile
===================================================================
--- trunk/cumin/Makefile 2012-04-11 18:09:44 UTC (rev 5294)
+++ trunk/cumin/Makefile 2012-04-11 19:36:57 UTC (rev 5295)
@@ -27,8 +27,10 @@
install -pm 0644 ../wooly/LICENSE-for-wsgiserver ${CUMIN_HOME}/doc
install -pm 0644 ../wooly/COPYING-for-wsgiserver ${CUMIN_HOME}/doc
install -d ${CUMIN_HOME}/model/upgrades
+ install -d ${CUMIN_HOME}/model/access
install -pm 0644 model/*.xml ${CUMIN_HOME}/model
- install -pm 0755 model/upgrades/* ${CUMIN_HOME}/model/upgrades/
+ install -pm 0644 model/access/* ${CUMIN_HOME}/model/access/
install -d ${CUMIN_HOME}/resources
install -pm 0644 ../wooly/resources/*.css ${CUMIN_HOME}/resources
install -pm 0644 ../wooly/resources/*.js ${CUMIN_HOME}/resources
Modified: trunk/cumin/bin/cumin-admin
===================================================================
--- trunk/cumin/bin/cumin-admin 2012-04-11 18:09:44 UTC (rev 5294)
+++ trunk/cumin/bin/cumin-admin 2012-04-11 19:36:57 UTC (rev 5295)
@@ -77,7 +77,7 @@
# cumin-admin is the mechanism for fixing the schema so we
# have to avoid the check here!
try:
- app.init(schema_version_check=False)
+ app.db_init(schema_version_check=False)
except OperationalError:
print "Can't talk to the database"
error("Run 'cumin-database check' as root for more
information")
@@ -402,6 +402,26 @@
print("\nNo users removed.")
+def handle_add_role(app, cursor, opts, args):
+ print("\nNot implemented.")
+ return
+
+ # We may allow this later
+ try:
+ name = args[0]
+ except IndexError:
+ error("ROLE is required")
+
+ if app.admin.get_role(cursor, name):
+ error("A role called '%s' already exists" % name)
+
+ try:
+ app.admin.add_role(cursor, name)
+ except IntegrityError:
+ error("A role called '%s' already exists" % name)
+
+ print "Role '%s' is added" % name
+
def handle_add_user(app, cursor, opts, args):
try:
name = args[0]
@@ -542,7 +562,6 @@
def handle_remove_assignment(app, cursor, opts, args):
user, role = get_user_and_role(app, cursor, args)
-
assignment = app.admin.get_assignment(cursor, user, role)
if not assignment:
Modified: trunk/cumin/bin/cumin-web
===================================================================
--- trunk/cumin/bin/cumin-web 2012-04-11 18:09:44 UTC (rev 5294)
+++ trunk/cumin/bin/cumin-web 2012-04-11 19:36:57 UTC (rev 5295)
@@ -41,7 +41,24 @@
cumin.wallaby_refresh = values.wallaby_refresh
if cumin.wallaby_refresh == 0:
cumin.wallaby_refresh = None
-
+
+def set_authorize_config(cumin, values, access_root):
+
+ # Allow this to be turned on/off.
+ # Default will be off.
+ cumin.do_authorize = values.authorize
+
+ # Allow this to be disabled via "None"
+ if values.auth_path == "None":
+ cumin.access_path = None
+ else:
+ # If there is no initial dir, prepend home
+ dir_name = os.path.split(values.auth_path)[0]
+ if len(dir_name) == 0:
+ cumin.access_path = os.path.join(access_root, values.auth_path)
+ else:
+ cumin.access_path = values.auth_path
+
def adjust_return(passed_init, ret):
# Shift non-zer0 return codes left 1 bit
# and OR in whether or not init passed
@@ -138,6 +155,15 @@
set_aviary_configs(cumin, values)
set_wallaby_configs(cumin, values, broker_uris)
+ # Not used right now
+ #cumin.auth_create_ondemand = values.auth_create_ondemand
+ #cumin.auth_proxy = values.auth_proxy
+
+ # Someday we may let this be configurable, for now it will
+ # be hardwired
+ values.auth_path = "persona.xml"
+ set_authorize_config(cumin, values, config.get_access_root())
+
cumin.debug = opts.debug
cumin.user = values.user
cumin.update_interval = values.update_interval
Modified: trunk/cumin/etc/cumin.conf
===================================================================
--- trunk/cumin/etc/cumin.conf 2012-04-11 18:09:44 UTC (rev 5294)
+++ trunk/cumin/etc/cumin.conf 2012-04-11 19:36:57 UTC (rev 5295)
@@ -96,6 +96,7 @@
# update-interval: 10
# max-qmf-table-sort: 1000
# persona: grid
+# authorize: False
#notification-timeout: 180
# Number of seconds that a message in the yellow notification banner
@@ -248,6 +249,13 @@
## The "default" persona contains content for both grid and
## messaging.
+## authorize: False
+## Controls whether or not roles are enforced.
+## Roles are assigned to users with cumin-admin.
+## If roles are enforced, users with only the 'user'
+## role will not see the 'Administrator' tab.
+## The default role for a new user is 'user'.
+
## [data]
## expire-enabled: True
Copied: trunk/cumin/model/access/persona.xml (from rev 5294,
branches/roles_II/cumin/model/access/persona.xml)
===================================================================
--- trunk/cumin/model/access/persona.xml (rev 0)
+++ trunk/cumin/model/access/persona.xml 2012-04-11 19:36:57 UTC (rev 5295)
@@ -0,0 +1,54 @@
+<PersonaMap>
+ <Persona name="default" auth="True">
+ <Module name="account"/>
+ <Module name="configuration"/>
+ <Module name="grid"/>
+ <Module name="inventory"/>
+ <Module name="messaging"/>
+ <Module name="usergrid"/>
+
+ <GroupAccess name="nogroup">
+ <MainPage name="login.html"/>
+ </GroupAccess>
+
+ <GroupAccess name="user">
+ <MainPage name="usergrid.html"/>
+ <ModuleAccess name="account"/>
+ <ModuleAccess name="usergrid"/>
+ </GroupAccess>
+
+ <GroupAccess name="admin">
+ <MainPage name="index.html"/>
+ <ModuleAccess name="*"/>
+ </GroupAccess>
+ </Persona>
+
+ <Persona name="grid" auth="True">
+ <Module name="account"/>
+ <Module name="configuration"/>
+ <Module name="grid"/>
+ <Module name="inventory"/>
+ <Module name="usergrid"/>
+
+ <GroupAccess name="nogroup">
+ <MainPage name="login.html"/>
+ </GroupAccess>
+
+ <GroupAccess name="user">
+ <MainPage name="usergrid.html"/>
+ <ModuleAccess name="account"/>
+ <ModuleAccess name="usergrid"/>
+ </GroupAccess>
+
+ <GroupAccess name="admin">
+ <MainPage name="index.html"/>
+ <ModuleAccess name="*"/>
+ </GroupAccess>
+ </Persona>
+
+ <Persona name="messaging" auth="False">
+ <Module name="account"/>
+ <Module name="inventory"/>
+ <Module name="messaging"/>
+ </Persona>
+</PersonaMap>
\ No newline at end of file
Modified: trunk/cumin/python/cumin/account/widgets.py
===================================================================
--- trunk/cumin/python/cumin/account/widgets.py 2012-04-11 18:09:44 UTC (rev 5294)
+++ trunk/cumin/python/cumin/account/widgets.py 2012-04-11 19:36:57 UTC (rev 5295)
@@ -124,19 +124,27 @@
user = cls.get_object(cursor, name=name)
if not user:
- self.login_invalid.set(session, True)
+ self.login_invalid.set(session, "credentials")
return
authenticated = self.app.authenticator.authenticate(name,password)
if authenticated:
- # You're in!
- login = LoginSession(self.app, user)
+ # You're in! almost...
+ # Check for a valid group if group authorization is on
+ if self.app.authorizator.is_enforcing():
+ roles = self.app.admin.get_roles_for_user(cursor, user)
+ if not self.app.authorizator.contains_valid_group(roles):
+ self.login_invalid.set(session, "roles")
+ return
+ else:
+ roles = []
+ login = LoginSession(self.app, user, roles)
session.client_session.attributes["login_session"] = login
url = self.page.origin.get(session)
self.page.redirect.set(session, url)
else:
- self.login_invalid.set(session, True)
+ self.login_invalid.set(session, "credentials")
def render_operator_link(self, session):
email = self.app.operator_email
@@ -147,7 +155,10 @@
return "site operator"
def render_login_invalid(self, session):
- if self.login_invalid.get(session):
+ reason = self.login_invalid.get(session)
+ if reason == "roles":
+ return self.get_string("roles_invalid")
+ elif reason == "credentials":
return self.get_string("login_invalid")
class Submit(FormButton):
Modified: trunk/cumin/python/cumin/account/widgets.strings
===================================================================
--- trunk/cumin/python/cumin/account/widgets.strings 2012-04-11 18:09:44 UTC (rev 5294)
+++ trunk/cumin/python/cumin/account/widgets.strings 2012-04-11 19:36:57 UTC (rev 5295)
@@ -77,3 +77,7 @@
<p class="login_invalid">The user name and password you entered do not
match any account.</p>
+[LoginForm.roles_invalid]
+<p class="login_invalid">There are no valid roles
+configured for this account.</p>
+
Modified: trunk/cumin/python/cumin/admin.py
===================================================================
--- trunk/cumin/python/cumin/admin.py 2012-04-11 18:09:44 UTC (rev 5294)
+++ trunk/cumin/python/cumin/admin.py 2012-04-11 19:36:57 UTC (rev 5295)
@@ -1,6 +1,8 @@
from StringIO import StringIO
from util import *
+from rosemary.sqlquery import SqlQuery, SqlInnerJoin
+from rosemary.sqlfilter import SqlComparisonFilter
from psycopg2 import IntegrityError, ProgrammingError
log = logging.getLogger("cumin.admin")
@@ -135,3 +137,26 @@
return mapping
+ def get_roles_for_user(self, cursor, user):
+ user_cls = self.app.model.com_redhat_cumin.User
+ role_cls = self.app.model.com_redhat_cumin.Role
+ mapping_cls = self.app.model.com_redhat_cumin.UserRoleMapping
+
+ query = SqlQuery(role_cls.sql_table)
+
+ SqlInnerJoin(query,
+ mapping_cls.sql_table,
+ mapping_cls.role.sql_column,
+ role_cls._id.sql_column)
+
+ SqlInnerJoin(query,
+ user_cls.sql_table,
+ user_cls._id.sql_column,
+ mapping_cls.user.sql_column)
+ #TODO: one column ?
+ cols = [ role_cls.name.sql_column ]
+ filt = SqlComparisonFilter(user_cls._id.sql_column, user._id, '=')
+ query.add_filter(filt)
+ sql = query.emit(cols)
+ cursor.execute(sql)
+ return [x[0] for x in cursor.fetchall()]
Modified: trunk/cumin/python/cumin/config.py
===================================================================
--- trunk/cumin/python/cumin/config.py 2012-04-11 18:09:44 UTC (rev 5294)
+++ trunk/cumin/python/cumin/config.py 2012-04-11 19:36:57 UTC (rev 5295)
@@ -51,7 +51,7 @@
param = ConfigParameter(web, "persona", str)
param.default = "grid"
-
+
param = ConfigParameter(web, "fast-view-attributes", str)
param.default =
"JobStatus,Cmd,Args,ExitStatus,JobStartDate,LastRemoteHost,LastJobStatus,Owner"
@@ -64,6 +64,23 @@
param = ConfigParameter(web, "force-html-doctype", bool)
param.default = False
+ # Turn this into a boolean, default is off for introduction.
+ # Hardwire the path to persona.xml unless/until we allow overriding
+ param = ConfigParameter(web, "authorize", bool)
+ param.default = False
+
+ # Turn these off for now...
+
+# param = ConfigParameter(web, "authz-provider", str)
+# param.default = "internal"
+
+# param = ConfigParameter(web, "auth-create-ondemand", bool)
+# param.default = False
+
+# param = ConfigParameter(web, "auth-proxy", bool)
+# param.default = False
+
+
def create_data_section(self, name, strict_section):
data = CuminConfigSection(self, name, strict_section)
data.log_file.default = os.path.join(self.home, "log", name +
".log")
@@ -103,6 +120,9 @@
def get_home(self):
return self.home
+ def get_access_root(self):
+ return os.path.join(self.home, "model/access/")
+
def parse(self):
paths = list()
Modified: trunk/cumin/python/cumin/grid/main.py
===================================================================
--- trunk/cumin/python/cumin/grid/main.py 2012-04-11 18:09:44 UTC (rev 5294)
+++ trunk/cumin/python/cumin/grid/main.py 2012-04-11 19:36:57 UTC (rev 5295)
@@ -24,6 +24,8 @@
super(Module, self).init()
self.frame = PoolFrame(self.app, "grid")
+ self.frame.cumin_module = self.name
+
self.app.main_page.main.grid = self.frame
self.app.main_page.main.add_tab(self.frame)
Modified: trunk/cumin/python/cumin/grid/pool.py
===================================================================
--- trunk/cumin/python/cumin/grid/pool.py 2012-04-11 18:09:44 UTC (rev 5294)
+++ trunk/cumin/python/cumin/grid/pool.py 2012-04-11 19:36:57 UTC (rev 5295)
@@ -78,6 +78,7 @@
self.tag = TagsFrame(app, "tag")
self.add_mode(self.tag)
+ self.tag.cumin_module = "configuration"
dashboard = PoolDashboard(app, "dashboard", self.object)
self.view.add_tab(dashboard)
Modified: trunk/cumin/python/cumin/inventory/main.py
===================================================================
--- trunk/cumin/python/cumin/inventory/main.py 2012-04-11 18:09:44 UTC (rev 5294)
+++ trunk/cumin/python/cumin/inventory/main.py 2012-04-11 19:36:57 UTC (rev 5295)
@@ -16,6 +16,7 @@
self.frame = InventoryFrame(app, "inventory")
self.app = app
+ self.frame.cumin_module = name
def init(self):
self.app.main_page.main.inventory = self.frame
Modified: trunk/cumin/python/cumin/inventory/system.py
===================================================================
--- trunk/cumin/python/cumin/inventory/system.py 2012-04-11 18:09:44 UTC (rev 5294)
+++ trunk/cumin/python/cumin/inventory/system.py 2012-04-11 19:36:57 UTC (rev 5295)
@@ -46,9 +46,11 @@
col = SystemTagsColumn(app, "Tags",
app.model.com_redhat_cumin_grid.Node.Tags, "Tags")
self.add_column(col)
+ col.cumin_module = "configuration"
col = SystemCheckinColumn(app, "Checkin",
app.model.com_redhat_cumin_grid.Node.Checkin, "Checkin")
self.add_column(col)
+ col.cumin_module = "configuration"
self.enable_csv_export()
@@ -73,6 +75,7 @@
configuration = SystemTags(app, "configuration", self.object)
self.view.add_tab(configuration)
+ configuration.cumin_module = "configuration"
def do_process(self, session):
try:
@@ -380,6 +383,7 @@
self.attr = attr
self.title = title
self.format_method = getattr(attr, "unit", None)
+ self.cumin_module = "configuration"
def get_field(self):
field = CheckinField(self.table.adapter, self.name)
Modified: trunk/cumin/python/cumin/main.py
===================================================================
--- trunk/cumin/python/cumin/main.py 2012-04-11 18:09:44 UTC (rev 5294)
+++ trunk/cumin/python/cumin/main.py 2012-04-11 19:36:57 UTC (rev 5295)
@@ -21,6 +21,7 @@
from util import *
from widgets import *
from authenticator import *
+from persona import CuminAuthorizator
from sage.catalog import Catalog
from sage.qmf.qmfoperations import QmfOperations
from sage.wallaby.wallabyoperations import WallabyOperations
@@ -56,6 +57,14 @@
self.modules = list()
self.modules_by_name = dict()
+ # This is an argument to CuminAuthorizator in init()
+ self.access_path = None
+ self.do_authorize = False
+
+ # Currently turned off in config, but maybe used in the future
+ self.auth_proxy = None
+ self.auth_create_ondemand = None
+
self.tasks = list()
self.user = None
@@ -69,14 +78,10 @@
self.form_defaults = self.CuminFormDefaults()
- # Persona methods are used to control what pages and/or
- # modules are present
-
+ # Persona value maps to xml definitions handled
+ # in persona.py
self.persona = persona
- self.personas = dict()
- self.personas["default"] = self._init_default_persona
- self.personas["grid"] = self._init_grid_persona
- self.personas["messaging"] = self._init_messaging_persona
+
self._page_links = list()
# self.model.sql_logging_enabled = True
@@ -113,61 +118,65 @@
self.model.check()
self.database.check()
+
+ def _init_persona(self, modules):
- def _init_grid_persona(self):
- self.main_page = GridMainPage(self, "index.html")
+ # Determine the view based on which modules are enabled
+ if "messaging" in modules:
+ if "grid" in modules:
+ view = MainView(self, "main")
+ else:
+ view = MessagingMainView(self, "main")
+ else:
+ view = GridMainView(self, "main")
+
+ self.main_page = MainPage(self, "index.html", view)
self.add_page(self.main_page, add_to_link_set=True)
self.set_default_page(self.main_page)
-
- self.add_common_pages()
- import account
- import grid
- import inventory
- import usergrid
+ self.form_page = CuminFormPage(self, "form.html")
+ self.add_page(self.form_page)
- account.Module(self, "account")
- grid.Module(self, "grid")
- inventory.Module(self, "inventory")
- usergrid.Module(self, "usergrid")
+ self.add_page(StatFlashPage(self, "chart.json"))
+ self.add_page(FlashFullPage(self, "flashpage.html"))
- def _init_messaging_persona(self):
- self.main_page = MessagingMainPage(self, "index.html")
- self.add_page(self.main_page, add_to_link_set=True)
- self.set_default_page(self.main_page)
+ self.export_page = CuminExportPage(self, "csv")
+ self.add_page(self.export_page)
+
+ self.about_page = AboutPage(self, "about.html")
+ self.add_page(self.about_page)
- self.add_common_pages()
+ self.resource_page.protected = False
- import account
- import messaging
- import inventory
+ # Enable the modules that are associated with the
+ # persona (from persona.py)
+ for name in modules:
+ try:
+ m = __import__(name, globals())
+ m.Module(self, name)
+ except:
+ pass
- account.Module(self, "account")
- messaging.Module(self, "messaging")
- inventory.Module(self, "inventory")
+ def db_init(self, schema_version_check=True):
+ self.model.init()
+ self.database.init(schema_version_check)
- def _init_default_persona(self):
- self.main_page = MainPage(self, "index.html")
- self.add_page(self.main_page, add_to_link_set=True)
- self.set_default_page(self.main_page)
-
- self.add_common_pages()
-
- import account
- import messaging
- import grid
- import inventory
- import usergrid
-
- account.Module(self, "account")
- messaging.Module(self, "messaging")
- grid.Module(self, "grid")
- inventory.Module(self, "inventory")
- usergrid.Module(self, "usergrid")
-
def init(self, schema_version_check=True):
log.info("Initializing %s", self)
+ # Do this initialization as late as possible so that
+ # the application can set a value for self.access_path.
+ # Alternatively, it can be an argument to the constructor.
+ self.authorizator = CuminAuthorizator(self, self.access_path, self.do_authorize)
+ try:
+ self.authorizator.set_persona(self.persona)
+ except:
+ msg = "Persona is not defined '%s'"
+ raise Exception(msg % self.persona)
+
+ self.authorize_cb = self.authorizator.authorize
+ self.mainpage_cb = self.authorizator.find_mainpage
+
# Create RPC interfaces for QMF and aviary.
# These service have overlapping functionality,
# so they are wrapped in a sage.Catalog object
@@ -221,11 +230,9 @@
self.database.init(schema_version_check)
self.server.init()
- # Create the correct persona
- if self.persona in self.personas:
- self.personas[self.persona]()
- else:
- self._init_default_persona()
+ # This will set up the main view and enable the
+ # modules that are associated with the persona
+ self._init_persona(self.authorizator.get_enabled_modules())
for module in self.modules:
module.init()
@@ -235,21 +242,6 @@
super(Cumin, self).init()
- def add_common_pages(self):
- self.form_page = CuminFormPage(self, "form.html")
- self.add_page(self.form_page)
-
- self.add_page(StatFlashPage(self, "chart.json"))
- self.add_page(FlashFullPage(self, "flashpage.html"))
-
- self.export_page = CuminExportPage(self, "csv")
- self.add_page(self.export_page)
-
- self.about_page = AboutPage(self, "about.html")
- self.add_page(self.about_page)
-
- self.resource_page.protected = False
-
def start(self):
log.info("Starting %s", self)
@@ -291,9 +283,8 @@
def __init__(self, app, name):
self.app = app
self.name = name
-
self.tasks = list()
-
+ log.debug("Init cumin module %s", name )
assert not hasattr(app, self.name), self.name
self.app.modules.append(self)
@@ -306,45 +297,10 @@
def init_test(self, test):
pass
-class GridMainPage(CuminPage, ModeSet):
- def __init__(self, app, name):
- super(GridMainPage, self).__init__(app, name)
-
- self.main = GridMainView(app, "main")
- self.add_mode(self.main)
- self.set_default_frame(self.main)
-
- self.page_html_class = "Cumin"
-
- def render_title(self, session):
- return self.get_title(session)
-
- def get_title(self, session):
- return "Administrator"
-
class GridMainView(CuminMainView):
def __init__(self, app, name):
super(GridMainView, self).__init__(app, name)
- #self.overview = GridOverviewFrame(app, "overview")
- #self.add_tab(self.overview)
-
-class MessagingMainPage(CuminPage, ModeSet):
- def __init__(self, app, name):
- super(MessagingMainPage, self).__init__(app, name)
-
- self.main = MessagingMainView(app, "main")
- self.add_mode(self.main)
- self.set_default_frame(self.main)
-
- self.page_html_class = "Cumin"
-
- def render_title(self, session):
- return self.get_title(session)
-
- def get_title(self, session):
- return "Administrator"
-
class MessagingMainView(CuminMainView):
def __init__(self, app, name):
super(MessagingMainView, self).__init__(app, name)
@@ -352,13 +308,21 @@
self.overview = MessagingOverviewFrame(app, "overview")
self.add_tab(self.overview)
-class MainPage(CuminPage, ModeSet):
+class MainView(CuminMainView):
def __init__(self, app, name):
+ super(MainView, self).__init__(app, name)
+
+ self.overview = OverviewFrame(app, "overview")
+ self.add_tab(self.overview)
+
+class MainPage(CuminPage, ModeSet):
+ def __init__(self, app, name, main_view):
super(MainPage, self).__init__(app, name)
- self.main = MainView(app, "main")
+ self.main = main_view
self.add_mode(self.main)
self.set_default_frame(self.main)
+ self.cumin_module = ["messaging", "grid"]
self.page_html_class = "Cumin"
@@ -368,13 +332,6 @@
def get_title(self, session):
return "Administrator"
-class MainView(CuminMainView):
- def __init__(self, app, name):
- super(MainView, self).__init__(app, name)
-
- self.overview = OverviewFrame(app, "overview")
- self.add_tab(self.overview)
-
# XXX Add qmf tab
class OverviewFrame(CuminFrame):
@@ -412,46 +369,12 @@
systems = TopSystemTable(app, "systems")
self.add_child(systems)
+ systems.cumin_module = "inventory"
submissions = TopSubmissionTable(app, "submissions")
self.add_child(submissions)
+ submissions.cumin_module = "grid"
- # XXX
-
- #queues = messaging.queue.TopQueueSet(app, "queues")
- #self.add_child(queues)
-
- #submissions = grid.submission.TopSubmissionSet(app, "submissions")
- #self.add_child(submissions)
-
- #systems = inventory.system.TopSystemSet(app, "systems")
- #self.add_child(systems)
-
-# don't appear to need this....
- # class Heading(CuminHeading):
- # def render_title(self, session):
- # return "Overview"
-
- # def render_icon_href(self, session):
- # return "resource?name=action-36.png"
-
-class GridOverviewFrame(OverviewFrame):
- def __init__(self, app, name):
- super(GridOverviewFrame, self).__init__(app, name)
-
- def get_overview_view(self, app):
- return GridOverviewView(app, "view")
-
-class GridOverviewView(Widget):
- def __init__(self, app, name):
- super(GridOverviewView, self).__init__(app, name)
-
- systems = TopSystemTable(app, "systems")
- self.add_child(systems)
-
- submissions = TopSubmissionTable(app, "submissions")
- self.add_child(submissions)
-
class MessagingOverviewFrame(OverviewFrame):
def __init__(self, app, name):
super(MessagingOverviewFrame, self).__init__(app, name)
Modified: trunk/cumin/python/cumin/messaging/main.py
===================================================================
--- trunk/cumin/python/cumin/messaging/main.py 2012-04-11 18:09:44 UTC (rev 5294)
+++ trunk/cumin/python/cumin/messaging/main.py 2012-04-11 19:36:57 UTC (rev 5295)
@@ -16,6 +16,7 @@
super(Module, self).__init__(app, name)
self.frame = MessagingFrame(self.app, "messaging")
+ self.frame.cumin_module = name
def init(self):
super(Module, self).init()
Copied: trunk/cumin/python/cumin/persona.py (from rev 5294,
branches/roles_II/cumin/python/cumin/persona.py)
===================================================================
--- trunk/cumin/python/cumin/persona.py (rev 0)
+++ trunk/cumin/python/cumin/persona.py 2012-04-11 19:36:57 UTC (rev 5295)
@@ -0,0 +1,170 @@
+#!/usr/bin/env python
+import logging
+import os.path
+import re
+
+try:
+ import xml.etree.ElementTree as etree
+except ImportError:
+ import elementtree.ElementTree as etree
+
+log = logging.getLogger("cumin.authorize")
+
+class CuminGroup(object):
+ def __init__(self, name):
+ self.name = name
+ self.mainpage = "index.html"
+ self.allowed = [] # which modules the group can access
+
+class CuminPersona(object):
+ def __init__(self, name, auth):
+ self.name = name # may be handy to have this here
+
+ self.auth = auth.lower() == "true" # should groups be checked?
+ self.modules = [] # which modules are enabled
+ self.mapping = {} # dictionary of CuminGroup objects
+
+ def find_mainpage(self, group):
+ return self.mapping[group].mainpage
+
+ def lookup(self, group, module):
+ try:
+ if type(module) in (list, tuple):
+ return len(set(module) & \
+ set(self.mapping[group].allowed)) > 0
+ else:
+ return module in self.mapping[group].allowed
+ except KeyError:
+ return False
+
+class CuminPersonaMap(object):
+ def __init__(self):
+ log.info("Initializing %s", self.__class__.__name__)
+ self.personas = {}
+ self.filepath = None
+
+ def __repr__(self):
+ return ("%s %s" % ( self.__class__.__name__, self.filepath ))
+
+ def parse_personafile(self, filepath):
+ #TODO: handle exceptions during parsing
+ acctree = etree.ElementTree().parse(filepath)
+ for prec in acctree.getiterator('Persona'):
+ pkey = prec.attrib["name"]
+ auth = prec.attrib["auth"]
+ persona = CuminPersona(pkey, auth)
+ self.personas[pkey] = persona
+
+ for pchild in prec.getchildren():
+ if pchild.tag == "Module":
+ persona.modules.append(pchild.attrib["name"])
+ elif pchild.tag == "GroupAccess":
+ gkey = pchild.attrib["name"]
+ group = CuminGroup(gkey)
+ persona.mapping[gkey] = group
+ for gchild in pchild.getchildren():
+ if gchild.tag == "MainPage":
+ group.mainpage = gchild.attrib["name"]
+ elif gchild.tag == "ModuleAccess":
+ if gchild.attrib["name"] == "*":
+ group.allowed.extend(persona.modules)
+ else:
+ group.allowed.append(gchild.attrib["name"])
+
+class CuminAuthorizator(object):
+ def __init__(self, app, access_path, do_authorize):
+ self.app = app
+ self.access_path = access_path
+ log.info("Initializing %s", self)
+ self.personas = CuminPersonaMap()
+ self.persona = None
+ self.do_authorize = do_authorize
+
+ log.debug("Access file path is %s", access_path)
+ if os.path.isdir(access_path):
+ files = [os.path.join(access_path, x) \
+ for x in os.listdir(access_path)]
+ elif os.path.isfile(access_path):
+ files = [access_path]
+ else:
+ files = []
+ log.error("Path '%s' for access files does not exist!" %
access_path)
+
+ for accfile in files:
+ if os.path.isfile(accfile) and \
+ os.path.splitext(accfile)[1] == ".xml":
+ try:
+ self.personas.parse_personafile(accfile)
+ except:
+ log.error("Access file '%s' does not parse!" %
accfile)
+
+ def set_persona(self, name):
+ self.persona = self.personas.personas[name]
+
+ def is_enforcing(self):
+ return self.do_authorize and self.persona.auth
+
+ def find_mainpage(self, web_session):
+ # Find a main page for the current login session.
+ # If user is in the admin group, prefer that one.
+ # If we have multiple groups someday beyond user/admin,
+ # we may have to have a priority scheme here (or find
+ # a way to NOT have more than one mainpage).
+ try:
+ group = web_session.client_session.attributes['login_session'].group
+ except KeyError:
+ group = "nogroup"
+ if type(group) == type([]):
+ if "admin" in group:
+ group = "admin"
+ else:
+ group = group[0]
+ return self.persona.find_mainpage(group)
+
+ def get_enabled_modules(self):
+ return self.persona.modules
+
+ def contains_valid_group(self, groups):
+ for g in groups:
+ if g in self.persona.mapping:
+ return True
+ return False
+
+ def authorize(self, web_session, widget):
+ if not hasattr(widget, "cumin_module") or \
+ widget.cumin_module is None:
+ # no module for this widget, we have to allow it
+ return True
+
+ # We can't logically be authorized for a module we haven't even
+ # enabled! This may occur when parts of a display have
+ # associated themselves with a module explicitly.
+ # (like a column in a table that is only valid if a
+ # particular module has been loaded).
+ if type(widget.cumin_module) in (list, tuple):
+ if len(set(widget.cumin_module) & set(self.persona.modules)) == 0:
+ return False
+ elif not widget.cumin_module in self.persona.modules:
+ return False
+
+ if not self.is_enforcing():
+ # Auth checks against group are off.
+ return True
+
+ try:
+ group = web_session.client_session.attributes['login_session'].group
+ except KeyError:
+ group = "nogroup"
+
+ if type(group) == type([]):
+ for g in group:
+ if self.persona.lookup(g, widget.cumin_module):
+ log.debug("Allowing %s authorization for %s ",
+ widget.cumin_module, group)
+ return True
+ else:
+ return self.persona.lookup(group, widget.cumin_module)
+
+ log.debug("Denying %s authorization for %s ", widget.my_name, group)
+ return False
+
Modified: trunk/cumin/python/cumin/usergrid/main.py
===================================================================
--- trunk/cumin/python/cumin/usergrid/main.py 2012-04-11 18:09:44 UTC (rev 5294)
+++ trunk/cumin/python/cumin/usergrid/main.py 2012-04-11 19:36:57 UTC (rev 5295)
@@ -11,3 +11,4 @@
self.app.user_grid_page = MainPage(self.app, "usergrid.html")
self.app.add_page(self.app.user_grid_page, add_to_link_set=True)
+ self.app.user_grid_page.cumin_module = self.name
Modified: trunk/cumin/python/cumin/widgets.py
===================================================================
--- trunk/cumin/python/cumin/widgets.py 2012-04-11 18:09:44 UTC (rev 5294)
+++ trunk/cumin/python/cumin/widgets.py 2012-04-11 19:36:57 UTC (rev 5295)
@@ -81,7 +81,7 @@
return branch.marshal()
else:
return super(CuminMainView, self).render_tab_href(session, tab)
-
+
def render_about_href(self, session):
page = self.app.about_page
lsess = Session(page)
@@ -90,29 +90,24 @@
class CuminPageLinks(ItemSet):
def __init__(self, app, name):
super(CuminPageLinks, self).__init__(app, name)
-
+ self.app = app
self.html_class = CuminPageLinks.__name__
- self.check_admin = self.CheckAdmin(app)
-
def do_get_items(self, session):
return self.app.page_links
def render_item_content(self, session, page):
href = Session(page).marshal()
title = page.render_title(session)
+ if self.app.authorize_cb(session, page):
+ return fmt_link(href, title)
- return fmt_link(href, title)
-
def render_item_class(self, session, page):
if page is session.page:
return "selected"
else:
return "_"
- class CheckAdmin(CuminSqlDataSet):
- pass
-
class CuminFrame(Frame, ModeSet):
def __init__(self, app, name):
super(CuminFrame, self).__init__(app, name)
@@ -156,7 +151,6 @@
def render_href(self, session, *args):
branch = session.branch()
-
if hasattr(self, "view"):
self.view.show(branch)
@@ -1175,12 +1169,11 @@
# XXX this should move somewhere else
class LoginSession(object):
- def __init__(self, app, user):
+ def __init__(self, app, user, group):
self.app = app
self.user = user
-
+ self.group = group
self.created = datetime.now()
-
self.notifications = list()
class NotificationSet(Widget):
@@ -1281,58 +1274,98 @@
self.error_tmpl = WidgetTemplate(self, "error_html")
self.not_found_tmpl = WidgetTemplate(self, "not_found_html")
- def do_process(self, session):
+ def _redirect_to_login(self, session):
session.cursor = self.app.database.get_read_cursor()
-
if not self.authorized(session):
page = self.app.login_page
-
sess = Session(page)
page.origin.set(sess, session.marshal())
-
self.redirect.set(session, sess.marshal())
+ return True
+ return False
- return
+ def redirect_on_not_authorized(self, session):
+ # If the user is not logged in, then it doesn't have a
+ # a group identity yet. Check to see if we should
+ # redirect to the login page before looking for a
+ # valid page to redirect to based on group
+ if not self._redirect_to_login(session):
+ super(CuminPage, self).redirect_on_not_authorized(session)
- super(CuminPage, self).do_process(session)
+ def do_process(self, session):
+ if not self._redirect_to_login(session):
+ super(CuminPage, self).do_process(session)
def authorized(self, session):
if not self.protected:
return True
login = session.client_session.attributes.get("login_session")
-
if login:
when = datetime.now() - timedelta(hours=24)
-
if login.created > when:
return True
- elif self.app.user:
- cls = self.app.model.com_redhat_cumin.User
- users = cls.get_selection(session.cursor, name=self.app.user)
- if not users:
- raise Exception("User '%s' not found" % self.app.user)
+ elif self.app.auth_proxy or self.app.user:
+ username = self.app.user
+ # proxy user overrides app defined user
+ try:
+ username = session.request_environment['HTTP_REMOTE_USER']
+ except KeyError:
+ log.debug("Proxy auth enabled but no remote user set")
+ pass
- login = LoginSession(self.app, users[0])
- session.client_session.attributes["login_session"] = login
+ if username:
+ cls = self.app.model.com_redhat_cumin.User
+ users = cls.get_selection(session.cursor, name=username)
+ if not users:
+ if not self.app.auth_proxy:
+ log.info("User '%s' not found" % username)
+ else:
+ log.info("User %s not found in db, "\
+ "using auth proxy", username )
+ #Hmmm, what is users here? For now just let it return
+ # to the login page
+ return False
- return True
+#TODO prehodit do authenticatora
+# ondemand -> authenticator.create_user
+# user -> authenticator.force_login
+ # Check for valid group
+ if self.app.authorizator.is_enforcing():
+ cursor = self.app.database.get_read_cursor()
+ roles = self.app.admin.get_roles_for_user(cursor, users[0])
+ if not self.app.authorizator.contains_valid_group(roles):
+ log.info("No valid roles for '%s'" % username)
+ return False # go to login page
+ else:
+ roles = []
+
+ login = LoginSession(self.app, users[0], roles)
+ session.client_session.attributes["login_session"] = login
+ return True
return False
def redirect_on_exception(self, session):
# If we have an exception from a missing object, redirect to the
# main page with a notice instead of using the not_found_tmpl below.
- # Test for presence on the index.html page already to avoid any possibility
+ # Test for presence on the main page already to avoid any possibility
# of an infinite redirect loop.
cls, value, traceback = sys.exc_info()
+ if self.app.authorizator.is_enforcing():
+ mainpage = self.app.authorizator.find_mainpage(session)
+ if not mainpage.startswith("/"):
+ mainpage = "/" + mainpage
+ else:
+ mainpage = "/index.html"
+
if cls is RosemaryNotFound and \
- session.request_environment["REQUEST_URI"] !=
"/index.html":
+ session.request_environment["REQUEST_URI"] != mainpage:
+ session.add_notice(Notice(
+ "An object being displayed became unavailable"))
+ return mainpage
- session.add_notice(Notice("An object being displayed became
unavailable"))
- return "/index.html"
-
def render_error(self, session):
cls, value, traceback = sys.exc_info()
Modified: trunk/wooly/python/wooly/__init__.py
===================================================================
--- trunk/wooly/python/wooly/__init__.py 2012-04-11 18:09:44 UTC (rev 5294)
+++ trunk/wooly/python/wooly/__init__.py 2012-04-11 19:36:57 UTC (rev 5295)
@@ -2,7 +2,7 @@
from resources import ResourceFinder
from template import *
from util import *
-
+from traceback import print_exception
log = logging.getLogger("wooly")
strings = StringCatalog(__file__)
@@ -125,6 +125,8 @@
self.__defer_tmpl = WidgetTemplate(self, "deferred_html")
self.nocontent_tmpl = WidgetTemplate(self, "nocontent")
+ self.my_name = self.__class__.__module__ + '.' + self.__class__.__name__
+
def init(self):
log.debug("Initializing %s" % self)
@@ -285,26 +287,36 @@
for child in self.children:
child.save_parameters(session, params)
+ def redirect_on_not_authorized(self, session):
+ # Give an opportunity to redirect to a valid page
+ # when a user is not authorized for this widget.
+ pass
+
def process(self, session, *args):
- if self.update_enabled:
- self.page.enable_update(session, self)
+ if not self.app.authorize_cb(session, self):
+ self.redirect_on_not_authorized(session)
+ else:
+ if self.update_enabled:
+ self.page.enable_update(session, self)
- if self.defer_enabled:
- self.page.enable_defer(session, self)
+ if self.defer_enabled:
+ self.page.enable_defer(session, self)
- if self.app.debug:
- profile = self.page.profile.get(session)
+ if self.app.debug:
+ profile = self.page.profile.get(session)
- call = ProcessCall(profile, self, args)
- call.do(session)
- else:
- self.do_process(session, *args)
+ call = ProcessCall(profile, self, args)
+ call.do(session)
+ else:
+ self.do_process(session, *args)
def do_process(self, session, *args):
for child in self.children:
child.process(session)
def render(self, session, *args):
+ if not self.app.authorize_cb(session, self):
+ return ""
if self.app.debug:
profile = self.page.profile.get(session)
@@ -487,7 +499,12 @@
return None
def service_error(self, session):
- self.error.set(session, PageError(self, session))
+ # Well, we can have other handled exceptions pop up before
+ # we actually get to the render so we want to record here
+ # what the root cause was.
+ cls, value, traceback = sys.exc_info()
+ self.error.set(session, PageError(self, session,
+ cls, value, traceback))
return self.render(session)
@@ -515,15 +532,18 @@
self.url = url
class PageError(object):
- def __init__(self, page, session):
+ def __init__(self, page, session, cls, value, traceback):
self.page = page
self.session = session
+ self.cls = cls
+ self.value = value
+ self.traceback = traceback
def render(self):
writer = Writer()
writer.write("APPLICATION ERROR\n\n")
- print_exc(None, writer)
+ print_exception(self.cls, self.value, self.traceback, None, writer)
writer.write("\n")
Modified: trunk/wooly/python/wooly/pages.py
===================================================================
--- trunk/wooly/python/wooly/pages.py 2012-04-11 18:09:44 UTC (rev 5294)
+++ trunk/wooly/python/wooly/pages.py 2012-04-11 19:36:57 UTC (rev 5295)
@@ -149,6 +149,14 @@
return sess.marshal()
+ def redirect_on_not_authorized(self, session):
+ mainpage = self.app.mainpage_cb(session)
+ if mainpage in self.app.pages_by_name:
+ log.debug("Not authorized, redirecting to %s", mainpage)
+ page = self.app.pages_by_name[mainpage]
+ sess = Session(page)
+ self.redirect.set(session, sess.marshal())
+
def do_process(self, session):
self.update_script.process(session)
self.defer_script.process(session)
Modified: trunk/wooly/python/wooly/table.py
===================================================================
--- trunk/wooly/python/wooly/table.py 2012-04-11 18:09:44 UTC (rev 5294)
+++ trunk/wooly/python/wooly/table.py 2012-04-11 19:36:57 UTC (rev 5295)
@@ -70,6 +70,7 @@
writer = Writer()
for column in self.get_visible_columns(session):
+
writer.write(column.render(session))
return writer.to_string()
@@ -272,7 +273,8 @@
writer = Writer()
for column in self.table.get_visible_columns(session):
- writer.write(column.header.render(session))
+ if self.app.authorize_cb(session, column):
+ writer.write(column.header.render(session))
return writer.to_string()
@@ -285,7 +287,8 @@
writer = Writer()
for column in self.table.get_visible_columns(session):
- writer.write(column.cell.render(session, record))
+ if self.app.authorize_cb(session, column):
+ writer.write(column.cell.render(session, record))
return writer.to_string()
Modified: trunk/wooly/python/wooly/widgets.py
===================================================================
--- trunk/wooly/python/wooly/widgets.py 2012-04-11 18:09:44 UTC (rev 5294)
+++ trunk/wooly/python/wooly/widgets.py 2012-04-11 19:36:57 UTC (rev 5295)
@@ -58,7 +58,8 @@
writer = Writer()
for tab in self.__tabs:
- self.__tab_tmpl.render(writer, session, tab)
+ if self.app.authorize_cb(session, tab):
+ self.__tab_tmpl.render(writer, session, tab)
return writer.to_string()