* Introduce a new configuration variable `ALLOWOP' with a policy flag
for each request type;
* have `BaseRequest.check' ensure that the corresponding policy flag
is set;
* export this policy switch to the template language; and
* only show widgets for the permitted operations in the web interface.
The commands still appear in the userv/SSH interface, which is a bit
gnarly.
package = PACKAGE,
version = VERSION,
script = CFG.SCRIPT_NAME,
package = PACKAGE,
version = VERSION,
script = CFG.SCRIPT_NAME,
+ static = CFG.STATIC,
+ allowop = CFG.ALLOWOP)
class TemplateFinder (object):
"""
class TemplateFinder (object):
"""
FORMS.acct = {
elts: ['list'],
check: function () { return null; }
FORMS.acct = {
elts: ['list'],
check: function () { return null; }
+ }~={allowop.set}:[
+
+ // Password setting is forbidden, so here's a stub function.
+ function check_partial_passwd() { return null; }~;~]
--></script>
</div>
<div class=expand-reference>
--></script>
</div>
<div class=expand-reference>
-<h2>Set a new password</h2>
+~={allowop.set}:[~;<h2>Set a new password</h2>
<table>
<tr>
<td class=label>
<table>
<tr>
<td class=label>
-<h2>Generate a new password</h2>
+~={allowop.reset}:[~;<h2>Generate a new password</h2>
<button type=submit id=reset-submit accesskey=g
name=%act value=reset><u>G</u>enerate</button>
<span class=whinge id=reset-whinge>OK</span>
<button type=submit id=reset-submit accesskey=g
name=%act value=reset><u>G</u>enerate</button>
<span class=whinge id=reset-whinge>OK</span>
return check_accounts() || check_partial_passwd();
}
}
return check_accounts() || check_partial_passwd();
}
}
-<h2>Clear the existing passwords</h2>
+~={allowop.clear}:[~;<h2>Clear the existing passwords</h2>
<button type=submit id=clear-submit accesskey=c
name=%act value=clear><u>C</u>lear</button>
<span class=whinge id=clear-whinge>OK</span>
<button type=submit id=clear-submit accesskey=c
name=%act value=clear><u>C</u>lear</button>
<span class=whinge id=clear-whinge>OK</span>
return check_accounts() || check_partial_passwd();
}
}
return check_accounts() || check_partial_passwd();
}
}
###--------------------------------------------------------------------------
### Requests.
###--------------------------------------------------------------------------
### Requests.
+CONF.DEFAULTS.update(
+
+ ## A boolean switch for each operation to tell us whether it's allowed. By
+ ## default, they all are.
+ ALLOWOP = polswitch(**dict((i, True) for i in OPS)))
+
## A request object represents a single user-level operation targetted at
## multiple services. The user might be known under a different alias by
## each service, so requests operate on service/user pairs, bundled in an
## A request object represents a single user-level operation targetted at
## multiple services. The user might be known under a different alias by
## each service, so requests operate on service/user pairs, bundled in an
class BaseRequest (object):
"""
class BaseRequest (object):
"""
- Base class for requests, provides basic protocol. In particular, it
- provides an empty `INFO' map, a trivial `check' method, and the obvious
- `perform' method which assumes that the `ops' list has already been
- constructed.
+ Base class for requests, provides basic protocol.
+
+ It provides an empty `INFO' map; a simple `check' method which checks the
+ operation name (in the class attribute `OP') against the configured policy
+ `CFG'ALLOWOP'; and the obvious `perform' method which assumes that the
+ `ops' list has already been constructed.
+ ## A dictionary describing the additional information returned by the
+ ## request: it maps attribute names to human-readable descriptions.
+
def check(me):
"""
Check the request to make sure we actually want to proceed.
"""
def check(me):
"""
Check the request to make sure we actually want to proceed.
"""
+ if not getattr(CFG.ALLOWOP, me.OP):
+ raise U.ExpectedError, \
+ (401, "Operation `%s' forbidden by policy" % me.OP)
+
def makeop(me, optype, svc, user, **kw):
"""
Hook for making operations. A policy class can substitute a
`FailOperation' to partially disallow a request.
"""
return optype(svc, user, **kw)
def makeop(me, optype, svc, user, **kw):
"""
Hook for making operations. A policy class can substitute a
`FailOperation' to partially disallow a request.
"""
return optype(svc, user, **kw)
def perform(me):
"""
Perform the queued-up operations.
"""
for op in me.ops: op.perform()
return me.ops
def perform(me):
"""
Perform the queued-up operations.
"""
for op in me.ops: op.perform()
return me.ops
CONF.export('BaseRequest', ExpectedError = U.ExpectedError)
class SetRequest (BaseRequest):
CONF.export('BaseRequest', ExpectedError = U.ExpectedError)
class SetRequest (BaseRequest):
inspection. The `check' method ensures that the password is not empty, but
imposes no other policy restrictions.
"""
inspection. The `check' method ensures that the password is not empty, but
imposes no other policy restrictions.
"""
def __init__(me, accts, new):
me.new = new
me.ops = [me.makeop(SetOperation, acct.svc, acct.user, passwd = new)
for acct in accts]
def __init__(me, accts, new):
me.new = new
me.ops = [me.makeop(SetOperation, acct.svc, acct.user, passwd = new)
for acct in accts]
def check(me):
if me.new == '':
raise U.ExpectedError, (400, "Empty password not permitted")
super(SetRequest, me).check()
def check(me):
if me.new == '':
raise U.ExpectedError, (400, "Empty password not permitted")
super(SetRequest, me).check()
CONF.export('SetRequest')
class ResetRequest (BaseRequest):
CONF.export('SetRequest')
class ResetRequest (BaseRequest):
Alternatively, subclasses can override the `pwgen' method.
"""
Alternatively, subclasses can override the `pwgen' method.
"""
## Password generation parameters.
PWBYTES = 16
ENCODING = 'base32'
## Password generation parameters.
PWBYTES = 16
ENCODING = 'base32'
def pwgen(me):
return U.ENCODINGS[me.ENCODING].encode(OS.urandom(me.PWBYTES)) \
.rstrip('=')
def pwgen(me):
return U.ENCODINGS[me.ENCODING].encode(OS.urandom(me.PWBYTES)) \
.rstrip('=')
CONF.export('ResetRequest')
class ClearRequest (BaseRequest):
"""
Request to clear the password for the given ACCTS.
"""
CONF.export('ResetRequest')
class ClearRequest (BaseRequest):
"""
Request to clear the password for the given ACCTS.
"""
def __init__(me, accts):
me.ops = [me.makeop(ClearOperation, acct.svc, acct.user)
for acct in accts]
def __init__(me, accts):
me.ops = [me.makeop(ClearOperation, acct.svc, acct.user)
for acct in accts]
CONF.export('ClearRequest')
###--------------------------------------------------------------------------
CONF.export('ClearRequest')
###--------------------------------------------------------------------------