diff options
Diffstat (limited to 'ipa-server/ipa-gui/ipagui')
-rw-r--r-- | ipa-server/ipa-gui/ipagui/controllers.py | 19 | ||||
-rw-r--r-- | ipa-server/ipa-gui/ipagui/forms/user.py | 49 | ||||
-rw-r--r-- | ipa-server/ipa-gui/ipagui/helpers/__init__.py | 1 | ||||
-rw-r--r-- | ipa-server/ipa-gui/ipagui/helpers/userhelper.py | 29 | ||||
-rw-r--r-- | ipa-server/ipa-gui/ipagui/static/css/style.css | 36 | ||||
-rw-r--r-- | ipa-server/ipa-gui/ipagui/templates/master.kid | 47 | ||||
-rw-r--r-- | ipa-server/ipa-gui/ipagui/templates/useredit.kid | 18 | ||||
-rw-r--r-- | ipa-server/ipa-gui/ipagui/templates/usereditform.kid | 23 | ||||
-rw-r--r-- | ipa-server/ipa-gui/ipagui/templates/userlist.kid | 14 | ||||
-rw-r--r-- | ipa-server/ipa-gui/ipagui/templates/usernewform.kid | 32 | ||||
-rw-r--r-- | ipa-server/ipa-gui/ipagui/templates/usershow.kid | 20 |
11 files changed, 244 insertions, 44 deletions
diff --git a/ipa-server/ipa-gui/ipagui/controllers.py b/ipa-server/ipa-gui/ipagui/controllers.py index 5fb4be06..538e66b4 100644 --- a/ipa-server/ipa-gui/ipagui/controllers.py +++ b/ipa-server/ipa-gui/ipagui/controllers.py @@ -17,6 +17,7 @@ import ipa.ipaclient import ipa.user import xmlrpclib import forms.user +from helpers import userhelper from ipa import ipaerror ipa.config.init_config() @@ -47,6 +48,14 @@ class Root(controllers.RootController): def index(self): return dict() + @expose() + def topsearch(self, **kw): + if kw.get('searchtype') == "Users": + return self.userlist(uid=kw.get('searchvalue')) + else: + return self.index() + + ######## # User # @@ -107,7 +116,7 @@ class Root(controllers.RootController): def userupdate(self, **kw): """Updates an existing user""" restrict_post() - if kw.get('submit') == 'Cancel': + if kw.get('submit') == 'Cancel Edit': turbogears.flash("Edit user cancelled") raise turbogears.redirect('/usershow', uid=kw.get('uid')) @@ -188,7 +197,7 @@ class Root(controllers.RootController): def userindex(self): raise turbogears.redirect("/userlist") - @expose() + # @expose() def generate_password(self): password = "" generator = random.SystemRandom() @@ -203,6 +212,9 @@ class Root(controllers.RootController): if (len(givenname) == 0) or (len(sn) == 0): return "" + givenname = givenname.lower() + sn = sn.lower() + uid = givenname[0] + sn[:7] try: client.get_user_by_uid(uid) @@ -244,6 +256,9 @@ class Root(controllers.RootController): if (len(givenname) == 0) or (len(sn) == 0): return "" + givenname = givenname.lower() + sn = sn.lower() + # TODO - get from config domain = "freeipa.org" diff --git a/ipa-server/ipa-gui/ipagui/forms/user.py b/ipa-server/ipa-gui/ipagui/forms/user.py index b9b6f33d..078e06dd 100644 --- a/ipa-server/ipa-gui/ipagui/forms/user.py +++ b/ipa-server/ipa-gui/ipagui/forms/user.py @@ -3,29 +3,41 @@ from turbogears import validators, widgets class UserFields(): uid = widgets.TextField(name="uid", label="Login") - userpassword = widgets.TextField(name="userpassword", label="Password") + userpassword = widgets.PasswordField(name="userpassword", label="Password") + userpassword_confirm = widgets.PasswordField(name="userpassword_confirm", + label="Confirm Password") uidnumber = widgets.TextField(name="uidnumber", label="UID") gidnumber = widgets.TextField(name="gidnumber", label="GID") givenname = widgets.TextField(name="givenname", label="First name") sn = widgets.TextField(name="sn", label="Last name") mail = widgets.TextField(name="mail", label="E-mail address") telephonenumber = widgets.TextField(name="telephonenumber", label="Phone") - nsAccountLock = widgets.CheckBox(name="nsAccountLock", label="Account Deactivated") - - uid.validator = validators.PlainText(not_empty=True) - userpassword.validator = validators.String(not_empty=True) - givenname.validator = validators.String(not_empty=True) - sn.validator = validators.String(not_empty=True) - mail.validator = validators.Email(not_empty=True) - # validators.PhoneNumber may be a bit too picky, requiring an area code - telephonenumber.validator = validators.PlainText(not_empty=True) + # nsAccountLock = widgets.CheckBox(name="nsAccountLock", label="Account Deactivated") + nsAccountLock = widgets.SingleSelectField(name="nsAccountLock", + label="Account Status", + options = [("", "active"), ("true", "inactive")]) uid_hidden = widgets.HiddenField(name="uid") uidnumber_hidden = widgets.HiddenField(name="uidnumber") gidnumber_hidden = widgets.HiddenField(name="gidnumber") + krbPasswordExpiration_hidden = widgets.HiddenField(name="krbPasswordExpiration") user_orig = widgets.HiddenField(name="user_orig") +class UserNewValidator(validators.Schema): + uid = validators.PlainText(not_empty=True) + userpassword = validators.String(not_empty=False) + userpassword_confirm = validators.String(not_empty=False) + givenname = validators.String(not_empty=True) + sn = validators.String(not_empty=True) + mail = validators.Email(not_empty=True) + # validators.PhoneNumber may be a bit too picky, requiring an area code + # telephonenumber = validators.PlainText(not_empty=False) + + chained_validators = [ + validators.FieldsMatch('userpassword', 'userpassword_confirm') + ] + class UserNewForm(widgets.Form): params = ['user'] @@ -34,6 +46,8 @@ class UserNewForm(widgets.Form): UserFields.uidnumber, UserFields.gidnumber, UserFields.sn, UserFields.mail] + validator = UserNewValidator() + def __init__(self, *args, **kw): super(UserNewForm,self).__init__(*args, **kw) (self.template_c, self.template) = widgets.meta.load_kid_template("ipagui.templates.usernewform") @@ -46,6 +60,18 @@ class UserNewForm(widgets.Form): def has_foo(self): return False +class UserEditValidator(validators.Schema): + userpassword = validators.String(not_empty=False) + userpassword_confirm = validators.String(not_empty=False) + givenname = validators.String(not_empty=True) + sn = validators.String(not_empty=True) + mail = validators.Email(not_empty=True) + # validators.PhoneNumber may be a bit too picky, requiring an area code + # telephonenumber = validators.PlainText(not_empty=False) + + chained_validators = [ + validators.FieldsMatch('userpassword', 'userpassword_confirm') + ] class UserEditForm(widgets.Form): params = ['user'] @@ -53,8 +79,11 @@ class UserEditForm(widgets.Form): fields = [UserFields.givenname, UserFields.sn, UserFields.mail, UserFields.uid_hidden, UserFields.user_orig, UserFields.uidnumber_hidden, UserFields.gidnumber_hidden, + UserFields.krbPasswordExpiration_hidden, ] + validator = UserEditValidator() + def __init__(self, *args, **kw): super(UserEditForm,self).__init__(*args, **kw) (self.template_c, self.template) = widgets.meta.load_kid_template("ipagui.templates.usereditform") diff --git a/ipa-server/ipa-gui/ipagui/helpers/__init__.py b/ipa-server/ipa-gui/ipagui/helpers/__init__.py new file mode 100644 index 00000000..143f486c --- /dev/null +++ b/ipa-server/ipa-gui/ipagui/helpers/__init__.py @@ -0,0 +1 @@ +# __init__.py diff --git a/ipa-server/ipa-gui/ipagui/helpers/userhelper.py b/ipa-server/ipa-gui/ipagui/helpers/userhelper.py new file mode 100644 index 00000000..e1ade3a2 --- /dev/null +++ b/ipa-server/ipa-gui/ipagui/helpers/userhelper.py @@ -0,0 +1,29 @@ +import sys +import datetime + +from ipa import ipautil + +def password_expires_in(datestr): + """Returns the number of days that password expires in. Returns a negative number + if the password is already expired.""" + if (datestr == None) or (datestr == ""): + return sys.maxint + + expdate = ipautil.parse_generalized_time(datestr) + if not expdate: + return sys.maxint + + delta = expdate - datetime.datetime.now() + return delta.days + +def password_is_expired(days): + return days < 0 + +def password_expires_soon(days): + return (not password_is_expired(days)) and (days < 7) + +def account_status_display(status): + if status == "true": + return "inactive" + else: + return "active" diff --git a/ipa-server/ipa-gui/ipagui/static/css/style.css b/ipa-server/ipa-gui/ipagui/static/css/style.css index 9ea86ae0..9654ebf1 100644 --- a/ipa-server/ipa-gui/ipagui/static/css/style.css +++ b/ipa-server/ipa-gui/ipagui/static/css/style.css @@ -17,6 +17,7 @@ body { background:#ccc; /* should be same as #sidebar */ margin:0 auto; width:100%; + clear:both; } @@ -24,9 +25,26 @@ body { background:#fff; } -#header h1 { - padding:5px; - margin:0; +#header #logo { + float:left; +} + +#header #headerinfo { + text-align:right; + padding-right:10px; +} + +#header #headerinfo #login { +} + +#header #headerinfo #topsearch { + padding-top: 15px; +} + +.searchtext { + background-color:#E5F1F4; + border:1px solid #8E8E8E; + color:#444444; } @@ -75,7 +93,11 @@ body { float:left; width:10%; padding: 5px; - font-size: small; + font-size: medium; +} + +#sidebar p { + line-height: 150%; } #sidebar h2 { @@ -140,6 +162,12 @@ body { font-weight: bolder; } +.warning_message { + font-size: 120%; + color: #ee0000; + font-weight: bolder; +} + .fielderror { color: red; font-weight: bold; diff --git a/ipa-server/ipa-gui/ipagui/templates/master.kid b/ipa-server/ipa-gui/ipagui/templates/master.kid index 2f39afc4..3be1f4c0 100644 --- a/ipa-server/ipa-gui/ipagui/templates/master.kid +++ b/ipa-server/ipa-gui/ipagui/templates/master.kid @@ -24,35 +24,66 @@ </span> </div> - <div id="page"> - <div id="header"> + <div id="header"> + <div id="logo"> <a href="${tg.url('/')}"><img - src="${tg.url('/static/images/logo.png')}" - border="0" - /></a> + src="${tg.url('/static/images/logo.png')}" + border="0" + /></a> + </div> + <div id="headerinfo"> + <div id="login"> + Logged in as: ace + </div> + <div id="topsearch"> + <form action="${tg.url('/topsearch')}" method="post"> + <select name="searchtype"> + <option>Users</option> + <option>Groups</option> + </select> + <input class="searchtext" id="topsearchbox" type="text" + name="searchvalue" + value="Type search terms here." + onfocus="clearsearch()" /> + <input type="submit" value="Search"/> + </form> + <script type="text/javascript"> + function clearsearch() { + topsearchbox = document.getElementById('topsearchbox'); + topsearchbox.onfocus = null; + topsearchbox.value = ""; + } + </script> + </div> </div> + </div> + <div id="page"> <div id="nav"><!-- This used to have links. Keeping around in case we move them back... --></div> <div id="sidebar"> <h2>Tasks</h2> + <p> <a href="${tg.url('/usernew')}">Add Person</a><br/> <a href="${tg.url('/userlist')}">Find People</a><br/> - <br /> + </p> + <p> <a href="${tg.url('/groupindex')}">Add Group</a><br/> <a href="${tg.url('/groupindex')}">Find Groups</a><br/> - <br /> + </p> + <p> <a href="${tg.url('/')}">Manage Policy</a><br/> <a href="${tg.url('/')}">Self Service</a><br/> + </p> </div> <div py:replace="[item.text]+item[:]"></div> <div id="footer"> - <a href="http://www.freeipa.com/">Powered by FreeIPA</a> + <a href="http://www.freeipa.com/" target="_blank">Powered by FreeIPA</a> </div> </div> diff --git a/ipa-server/ipa-gui/ipagui/templates/useredit.kid b/ipa-server/ipa-gui/ipagui/templates/useredit.kid index db47ab29..1f31139d 100644 --- a/ipa-server/ipa-gui/ipagui/templates/useredit.kid +++ b/ipa-server/ipa-gui/ipagui/templates/useredit.kid @@ -8,6 +8,24 @@ <body> <h2>Edit Person</h2> +<?python +from ipagui.helpers import userhelper +pw_expires_days = userhelper.password_expires_in(user.get("krbPasswordExpiration")) +pw_expires_soon = userhelper.password_expires_soon(pw_expires_days) +pw_is_expired = userhelper.password_is_expired(pw_expires_days) +if pw_expires_days != 1: + days_suffix = "s" +else: + days_suffix = "" +?> + + <div py:if='pw_expires_soon' class="warning_message"> + Password will expire in ${pw_expires_days} day${days_suffix} + </div> + <div py:if='pw_is_expired' class="warning_message"> + Password has expired + </div> + ${form.display(action="userupdate", value=user)} </body> </html> diff --git a/ipa-server/ipa-gui/ipagui/templates/usereditform.kid b/ipa-server/ipa-gui/ipagui/templates/usereditform.kid index dc61f38c..6227c444 100644 --- a/ipa-server/ipa-gui/ipagui/templates/usereditform.kid +++ b/ipa-server/ipa-gui/ipagui/templates/usereditform.kid @@ -56,8 +56,9 @@ <span py:replace="user.userpassword.display(value_for(user.userpassword))" /> <span py:if="tg.errors.get('userpassword')" class="fielderror" py:content="tg.errors.get('userpassword')" /> - <span id="password_text">********</span> + <!-- + <span id="password_text">********</span> <input id="genpassword_button" type="button" value="Generate Password" disabled="true" onclick="new Ajax.Request('${tg.url('/generate_password')}', @@ -89,6 +90,20 @@ } } </script> + --> + </td> + </tr> + + <tr> + <th valign="top"> + <label class="fieldlabel" for="${user.userpassword_confirm.field_id}" + py:content="user.userpassword_confirm.label" />: + </th> + <td valign="top"> + <span py:replace="user.userpassword_confirm.display( + value_for(user.userpassword_confirm))" /> + <span py:if="tg.errors.get('userpassword_confirm')" class="fielderror" + py:content="tg.errors.get('userpassword_confirm')" /> </td> </tr> @@ -158,11 +173,13 @@ <tr> <th> <br /> - <input type="submit" class="submitbutton" name="submit" value="Submit"/> + <input type="submit" class="submitbutton" name="submit" + value="Update Person"/> </th> <td> <br /> - <input type="submit" class="submitbutton" name="submit" value="Cancel" /> + <input type="submit" class="submitbutton" name="submit" + value="Cancel Edit" /> </td> <td></td> </tr> diff --git a/ipa-server/ipa-gui/ipagui/templates/userlist.kid b/ipa-server/ipa-gui/ipagui/templates/userlist.kid index de4c4eb2..1f3e72b0 100644 --- a/ipa-server/ipa-gui/ipagui/templates/userlist.kid +++ b/ipa-server/ipa-gui/ipagui/templates/userlist.kid @@ -6,20 +6,18 @@ <title>Find People</title> </head> <body> - <h2>Find People</h2> <div id="search"> <form action="${tg.url('/userlist')}" method="post"> - Search: <input id="uid" type="text" name="uid" value="${uid}" /> - <input type="submit" /> + <input type="submit" value="Find People"/> </form> <script type="text/javascript"> document.getElementById("uid").focus(); </script> </div> - <div py:if='users != None'> + <div py:if='(users != None) and (len(users) > 0)'> <h2>${len(users)} results returned:</h2> - <table id="resultstable" py:if='len(users) > 0'> + <table id="resultstable"> <tr> <th> <label class="fieldlabel" py:content="fields.uid.label" /> @@ -61,9 +59,9 @@ </td> </tr> </table> - <div py:if='len(users) == 0'> - No results found. - </div> + </div> + <div py:if='(users != None) and (len(users) == 0)'> + <h2>No results found for "${uid}"</h2> </div> </body> </html> diff --git a/ipa-server/ipa-gui/ipagui/templates/usernewform.kid b/ipa-server/ipa-gui/ipagui/templates/usernewform.kid index d4863a3d..daf13143 100644 --- a/ipa-server/ipa-gui/ipagui/templates/usernewform.kid +++ b/ipa-server/ipa-gui/ipagui/templates/usernewform.kid @@ -27,6 +27,9 @@ <span py:if="tg.errors.get('sn')" class="fielderror" py:content="tg.errors.get('sn')" /> <script type="text/javascript"> + var uid_suggest = "" + var mail_suggest = "" + function autofill(self) { givenname = document.getElementById('form_givenname'); sn = document.getElementById('form_sn'); @@ -35,22 +38,24 @@ } uid = document.getElementById('form_uid'); mail = document.getElementById('form_mail'); - if (uid.value == "") { + if ((uid.value == "") || (uid.value == uid_suggest)) { new Ajax.Request('${tg.url('/suggest_uid')}', { method: 'get', parameters: {'givenname': givenname.value, 'sn': sn.value}, onSuccess: function(transport) { uid.value = transport.responseText; + uid_suggest = uid.value; new Effect.Highlight(uid); } }); } - if (mail.value == "") { + if ((mail.value == "") || (mail.value == mail_suggest)) { new Ajax.Request('${tg.url('/suggest_email')}', { method: 'get', parameters: {'givenname': givenname.value, 'sn': sn.value}, onSuccess: function(transport) { mail.value = transport.responseText; + mail_suggest = mail.value; new Effect.Highlight(mail); } }); @@ -87,6 +92,7 @@ <span py:if="tg.errors.get('userpassword')" class="fielderror" py:content="tg.errors.get('userpassword')" /> + <!-- <input type="button" value="Generate Password" onclick="new Ajax.Request('${tg.url('/generate_password')}', { @@ -96,6 +102,20 @@ transport.responseText; } });" /> + --> + </td> + </tr> + + <tr> + <th> + <label class="fieldlabel" for="${user.userpassword_confirm.field_id}" + py:content="user.userpassword_confirm.label" />: + </th> + <td> + <span py:replace="user.userpassword_confirm.display( + value_for(user.userpassword_confirm))" /> + <span py:if="tg.errors.get('userpassword_confirm')" class="fielderror" + py:content="tg.errors.get('userpassword_confirm')" /> </td> </tr> @@ -167,15 +187,11 @@ <table class="formtable" cellpadding="2" cellspacing="0" border="0"> <tr> - <th> - <br /> - <input type="submit" class="submitbutton" name="submit" value="Submit"/> - </th> + <th></th> <td> <br /> - <input type="submit" class="submitbutton" name="submit" value="Cancel" /> + <input type="submit" class="submitbutton" name="submit" value="Add Person"/> </td> - <td></td> </tr> </table> diff --git a/ipa-server/ipa-gui/ipagui/templates/usershow.kid b/ipa-server/ipa-gui/ipagui/templates/usershow.kid index 4e73eba3..aff400c5 100644 --- a/ipa-server/ipa-gui/ipagui/templates/usershow.kid +++ b/ipa-server/ipa-gui/ipagui/templates/usershow.kid @@ -8,6 +8,24 @@ <body> <h2>View Person</h2> +<?python +from ipagui.helpers import userhelper +pw_expires_days = userhelper.password_expires_in(user.get("krbPasswordExpiration")) +pw_expires_soon = userhelper.password_expires_soon(pw_expires_days) +pw_is_expired = userhelper.password_is_expired(pw_expires_days) +if pw_expires_days != 1: + days_suffix = "s" +else: + days_suffix = "" +?> + + <div py:if='pw_expires_soon' class="warning_message"> + Password will expire in ${pw_expires_days} day${days_suffix} + </div> + <div py:if='pw_is_expired' class="warning_message"> + Password has expired + </div> + <div class="formsection">Identity Details</div> <table class="formtable" cellpadding="2" cellspacing="0" border="0"> <tr> @@ -68,7 +86,7 @@ <th> <label class="fieldlabel" py:content="fields.nsAccountLock.label" />: </th> - <td>${user.get("nsAccountLock")}</td> + <td>${userhelper.account_status_display(user.get("nsAccountLock"))}</td> </tr> </table> |