summaryrefslogtreecommitdiffstats
path: root/ipa-server/ipa-gui
diff options
context:
space:
mode:
Diffstat (limited to 'ipa-server/ipa-gui')
-rw-r--r--ipa-server/ipa-gui/ipagui/controllers.py19
-rw-r--r--ipa-server/ipa-gui/ipagui/forms/user.py49
-rw-r--r--ipa-server/ipa-gui/ipagui/helpers/__init__.py1
-rw-r--r--ipa-server/ipa-gui/ipagui/helpers/userhelper.py29
-rw-r--r--ipa-server/ipa-gui/ipagui/static/css/style.css36
-rw-r--r--ipa-server/ipa-gui/ipagui/templates/master.kid47
-rw-r--r--ipa-server/ipa-gui/ipagui/templates/useredit.kid18
-rw-r--r--ipa-server/ipa-gui/ipagui/templates/usereditform.kid23
-rw-r--r--ipa-server/ipa-gui/ipagui/templates/userlist.kid14
-rw-r--r--ipa-server/ipa-gui/ipagui/templates/usernewform.kid32
-rw-r--r--ipa-server/ipa-gui/ipagui/templates/usershow.kid20
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>