[Jifty-commit] r4109 - in apps/CASPlus: . trunk/etc
trunk/lib/CASPlus trunk/lib/CASPlus/Action
trunk/lib/CASPlus/Model trunk/lib/CASPlus/View
trunk/lib/CASPlus/View/Admin/User trunk/share/web/static/css
trunk/share/web/templates/_elements
trunk/share/web/templates/user trunk/t
jifty-commit at lists.jifty.org
jifty-commit at lists.jifty.org
Thu Sep 13 15:54:47 EDT 2007
Author: sterling
Date: Thu Sep 13 15:54:46 2007
New Revision: 4109
Added:
apps/CASPlus/trunk/lib/CASPlus/Action/UpdateUserPassword.pm
apps/CASPlus/trunk/lib/CASPlus/View/Admin/User/Limits.pm
apps/CASPlus/trunk/lib/CASPlus/View/Admin/User/Permissions.pm
apps/CASPlus/trunk/lib/CASPlus/View/Admin/User/Properties.pm
apps/CASPlus/trunk/lib/CASPlus/View/User.pm
apps/CASPlus/trunk/t/20-action-UpdateUserPassword.t
Removed:
apps/CASPlus/trunk/share/web/templates/_elements/
apps/CASPlus/trunk/share/web/templates/user/
Modified:
apps/CASPlus/ (props changed)
apps/CASPlus/trunk/ (props changed)
apps/CASPlus/trunk/etc/config.yml
apps/CASPlus/trunk/lib/CASPlus/Action/ProxyValidate.pm
apps/CASPlus/trunk/lib/CASPlus/Action/Validate.pm
apps/CASPlus/trunk/lib/CASPlus/Dispatcher.pm
apps/CASPlus/trunk/lib/CASPlus/Model/SSOSession.pm
apps/CASPlus/trunk/lib/CASPlus/Model/ServiceSession.pm
apps/CASPlus/trunk/lib/CASPlus/Model/User.pm
apps/CASPlus/trunk/lib/CASPlus/ProfileRelationshipBase.pm
apps/CASPlus/trunk/lib/CASPlus/ProfileRelationshipMixin.pm
apps/CASPlus/trunk/lib/CASPlus/View.pm
apps/CASPlus/trunk/share/web/static/css/app.css
apps/CASPlus/trunk/t/50-me.t
Log:
r12094 at riddle: andrew | 2007-09-13 14:53:07 -0500
Merge down from Boomer SSO vendor branch.
----------------------------------------------------------------------
r11982: andrew | 2007-09-11 17:44:23 -0500
r11970 at dynpc145 (orig r1988): andrew | 2007-09-11 17:54:44 -0500
r11969 at dynpc145: andrew | 2007-09-11 17:41:13 -0500
Fixing a typo in the record_class variable.
----------------------------------------------------------------------
r12074: andrew | 2007-09-13 14:46:18 -0500
r12010 at riddle (orig r1989): andrew | 2007-09-12 10:06:47 -0500
r12009 at riddle: andrew | 2007-09-12 09:51:22 -0500
Adding relationships to the administrative menu.
----------------------------------------------------------------------
r12075: andrew | 2007-09-13 14:46:18 -0500
r12012 at riddle (orig r1990): andrew | 2007-09-12 10:08:11 -0500
r12011 at riddle: andrew | 2007-09-12 09:54:34 -0500
Fix the name of the relationships menu.
----------------------------------------------------------------------
r12076: andrew | 2007-09-13 14:46:19 -0500
r12014 at riddle (orig r1991): andrew | 2007-09-12 10:33:47 -0500
r12013 at riddle: andrew | 2007-09-12 10:20:10 -0500
Adding rule to auto-generate CRUD views for relationships.
----------------------------------------------------------------------
r12077: andrew | 2007-09-13 14:46:19 -0500
r12016 at riddle (orig r1992): andrew | 2007-09-12 10:36:00 -0500
r12015 at riddle: andrew | 2007-09-12 10:22:20 -0500
Fixing a missing parameter to _register_crud().
----------------------------------------------------------------------
r12078: andrew | 2007-09-13 14:46:20 -0500
r12018 at riddle (orig r1993): andrew | 2007-09-12 10:38:32 -0500
r12017 at riddle: andrew | 2007-09-12 10:24:58 -0500
Making relationship CRUD use relationship tables rather than ModelClass.
----------------------------------------------------------------------
r12079: andrew | 2007-09-13 14:46:20 -0500
r12020 at riddle (orig r1994): andrew | 2007-09-12 10:42:43 -0500
r12019 at riddle: andrew | 2007-09-12 10:29:08 -0500
Making the relationship admin screens show the link table rather than ModelClass.
----------------------------------------------------------------------
r12080: andrew | 2007-09-13 14:46:21 -0500
r12024 at riddle (orig r1995): andrew | 2007-09-12 11:29:00 -0500
r12023 at riddle: andrew | 2007-09-12 11:15:19 -0500
Adding admin screens for permissions. Adding better allow/deny handling of actions.
----------------------------------------------------------------------
r12081: andrew | 2007-09-13 14:46:21 -0500
r12025 at riddle (orig r1996): andrew | 2007-09-12 11:32:04 -0500
r6117 at viper: root | 2007-09-12 11:18:04 -0500
Fixed an accidentally inserted colon.
----------------------------------------------------------------------
r12082: andrew | 2007-09-13 14:46:22 -0500
r12026 at riddle (orig r1997): andrew | 2007-09-12 11:43:01 -0500
r6119 at viper: root | 2007-09-12 11:29:41 -0500
Fixed a mispelt call to may_manage_roles().
----------------------------------------------------------------------
r12083: andrew | 2007-09-13 14:46:22 -0500
r12027 at riddle (orig r1998): andrew | 2007-09-12 12:19:53 -0500
r6121 at viper: andrew | 2007-09-12 12:05:48 -0500
Make sure the relationship trigger always passes objects to the role calculation utilities.
----------------------------------------------------------------------
r12084: andrew | 2007-09-13 14:46:22 -0500
r12028 at riddle (orig r1999): andrew | 2007-09-12 12:20:03 -0500
r6122 at viper: root | 2007-09-12 12:06:29 -0500
Added missing current_user_can() method for relationships.
----------------------------------------------------------------------
r12085: andrew | 2007-09-13 14:46:23 -0500
r12029 at riddle (orig r2000): andrew | 2007-09-12 13:17:59 -0500
r6125 at viper: andrew | 2007-09-12 13:00:38 -0500
Added a check to the password test to confirm that the user actually has a password.
----------------------------------------------------------------------
r12086: andrew | 2007-09-13 14:46:23 -0500
r12030 at riddle (orig r2001): andrew | 2007-09-12 13:18:09 -0500
r6126 at viper: andrew | 2007-09-12 13:04:36 -0500
Correct the log message on a missing password error.
----------------------------------------------------------------------
r12087: andrew | 2007-09-13 14:46:24 -0500
r12033 at riddle (orig r2002): andrew | 2007-09-12 13:51:22 -0500
r12032 at riddle: andrew | 2007-09-12 13:37:22 -0500
Removing the remaining Mason templates and stripped half-baked RDF stuff.
----------------------------------------------------------------------
r12088: andrew | 2007-09-13 14:46:24 -0500
r12039 at riddle (orig r2003): andrew | 2007-09-12 14:07:38 -0500
r12038 at riddle: andrew | 2007-09-12 13:53:37 -0500
Adding the user's profile information to their profile view page.
----------------------------------------------------------------------
r12089: andrew | 2007-09-13 14:46:25 -0500
r12044 at riddle (orig r2004): andrew | 2007-09-12 16:42:43 -0500
r12042 at riddle: andrew | 2007-09-12 16:25:17 -0500
Added password updates to the my profile page.
----------------------------------------------------------------------
r12090: andrew | 2007-09-13 14:46:25 -0500
r12045 at riddle (orig r2005): andrew | 2007-09-12 16:42:53 -0500
r12043 at riddle: andrew | 2007-09-12 16:28:58 -0500
Fixed navigation setup so that it happens after actions are run. Added a logout link to the nav.
----------------------------------------------------------------------
r12091: andrew | 2007-09-13 14:46:26 -0500
r12062 at riddle (orig r2006): andrew | 2007-09-13 10:10:54 -0500
r6129 at viper: andrew | 2007-09-13 09:57:21 -0500
Fixing various permission issues regarding service/proxy validation.
----------------------------------------------------------------------
r12092: andrew | 2007-09-13 14:46:26 -0500
r12063 at riddle (orig r2007): andrew | 2007-09-13 10:54:59 -0500
r12061 at dynpc145: andrew | 2007-09-13 10:40:57 -0500
Making the password update form smarter to allow administrators to change passwords nicely.
----------------------------------------------------------------------
r12093: andrew | 2007-09-13 14:46:26 -0500
r12065 at riddle (orig r2008): andrew | 2007-09-13 10:57:48 -0500
r12064 at dynpc145: andrew | 2007-09-13 10:44:10 -0500
Fixing the help message for user administrators on the change password screen.
----------------------------------------------------------------------
Modified: apps/CASPlus/trunk/etc/config.yml
==============================================================================
--- apps/CASPlus/trunk/etc/config.yml (original)
+++ apps/CASPlus/trunk/etc/config.yml Thu Sep 13 15:54:46 2007
@@ -8,12 +8,12 @@
Database:
CheckSchema: 1
Database: casplus
- Driver: SQLite
+ Driver: mysql
Host: localhost
Password: ''
RecordBaseClass: Jifty::DBI::Record::Cachable
- RecordUUIDs: active
User: ''
+ RecordUUIDs: active
Version: 0.0.1
DevelMode: 1
L10N:
Modified: apps/CASPlus/trunk/lib/CASPlus/Action/ProxyValidate.pm
==============================================================================
--- apps/CASPlus/trunk/lib/CASPlus/Action/ProxyValidate.pm (original)
+++ apps/CASPlus/trunk/lib/CASPlus/Action/ProxyValidate.pm Thu Sep 13 15:54:46 2007
@@ -95,7 +95,10 @@
# Proxy ticket renewal doesn't make sense, renew is ignored.
# Load the proxy ticket
- my $proxy_ticket = CASPlus::Model::ProxySession->new;
+ my $superuser = CASPlus::CurrentUser->superuser;
+ my $proxy_ticket = CASPlus::Model::ProxySession->new(
+ current_user => $superuser,
+ );
$proxy_ticket->load_by_cols(
service_identifier => $service,
proxy_ticket => $ticket,
Added: apps/CASPlus/trunk/lib/CASPlus/Action/UpdateUserPassword.pm
==============================================================================
--- (empty file)
+++ apps/CASPlus/trunk/lib/CASPlus/Action/UpdateUserPassword.pm Thu Sep 13 15:54:46 2007
@@ -0,0 +1,150 @@
+use strict;
+use warnings;
+
+=head1 NAME
+
+CASPlus::Action::UpdateUserPassword
+
+=cut
+
+package CASPlus::Action::UpdateUserPassword;
+use base qw/CASPlus::Action Jifty::Action/;
+
+use Jifty::Param::Schema;
+use Jifty::Action schema {
+ param id =>
+ is mandatory,
+ render as 'Hidden',
+ ;
+
+ param old_password =>
+ label is 'Old password',
+ render as 'Password',
+ ajax validates,
+ is focus,
+ ;
+
+ param new_password =>
+ label is 'New password',
+ render as 'Password',
+ ajax validates,
+ ;
+
+ param confirm_password =>
+ label is 'Re-type password',
+ render as 'Password',
+ hints is 'Please enter the same password here as in the previous box to confirm your entry.',
+ ajax validates,
+ ;
+};
+
+=head2 take_action
+
+=cut
+
+sub take_action {
+ my $self = shift;
+
+ my $new_password = $self->argument_value('new_password');
+ $self->user->set_password($new_password);
+
+ $self->report_success if not $self->result->failure;
+
+ return 1;
+}
+
+=head2 report_success
+
+=cut
+
+sub report_success {
+ my $self = shift;
+
+ if ($self->current_user->id == $self->user->id) {
+ $self->result->message('Your password has been updated.');
+ }
+ else {
+ $self->result->message(
+ _('The password for <em>%1</em> has been updated.', $self->user->username)
+ );
+ }
+}
+
+__PACKAGE__->mk_accessors(qw/ _user_record /);
+
+sub user {
+ my $self = shift;
+ unless ($self->_user_record) {
+ my $user = CASPlus::Model::User->new;
+ $user->load($self->argument_value('id'));
+ $self->_user_record($user);
+ }
+ return $self->_user_record;
+}
+
+sub validate_old_password {
+ my $self = shift;
+ my $value = shift;
+
+ # Administrators don't need to enter the old_password
+ if ($self->current_user->id
+ && $self->current_user->may_manage_users
+ && $self->current_user->id != $self->argument_value('id')) {
+
+ return $self->validation_ok('old_password');
+ }
+
+ unless (length $value) {
+ return $self->validation_error(
+ old_password => 'Sorry, you must enter your old password to confirm your identity.');
+ }
+
+ unless ($self->user->password_is($value)) {
+ return $self->validation_error(
+ old_password => 'This password is incorrect.');
+ }
+
+ return $self->validation_ok('old_password');
+}
+
+sub validate_new_password {
+ my $self = shift;
+ my $value = shift;
+
+ unless (length $value) {
+ return $self->validation_error(
+ new_password => 'You must specify a new password for your account.');
+ }
+
+ if ($self->user->password_is($value)) {
+ return $self->validation_error(
+ new_password => 'This password is the same as the old password.');
+ }
+
+ if (length $value < 6) {
+ return $self->validation_error(
+ new_password => 'Your new password must be at least 6 letters long.');
+ }
+
+ return $self->validation_ok('new_password');
+}
+
+sub validate_confirm_password {
+ my $self = shift;
+ my $value = shift;
+
+ unless (length $value) {
+ return $self->validation_error(
+ confirm_password => 'You must re-type your new password to continue.');
+ }
+
+ unless ($value eq $self->argument_value('new_password')) {
+ return $self->validation_error(
+ confirm_password => 'This password does not match the previous entry.');
+ }
+
+ return $self->validation_ok('confirm_password');
+}
+
+1;
+
Modified: apps/CASPlus/trunk/lib/CASPlus/Action/Validate.pm
==============================================================================
--- apps/CASPlus/trunk/lib/CASPlus/Action/Validate.pm (original)
+++ apps/CASPlus/trunk/lib/CASPlus/Action/Validate.pm Thu Sep 13 15:54:46 2007
@@ -221,7 +221,10 @@
my $pgt_url = $self->argument_value('pgtUrl');
# Load the service ticket
- my $service_ticket = CASPlus::Model::ServiceSession->new;
+ my $superuser = CASPlus::CurrentUser->superuser;
+ my $service_ticket = CASPlus::Model::ServiceSession->new(
+ current_user => $superuser
+ );
$service_ticket->load_by_cols(
service_url => $service,
service_ticket => $ticket,
@@ -288,7 +291,7 @@
$self->log->info('SERVICE TICKET FAILED '
."Service mismatch for ticket $ticket and "
- ."service $service");
+ ."service '$service' (not the same as '@{[$service_ticket->service_url]}')");
}
Modified: apps/CASPlus/trunk/lib/CASPlus/Dispatcher.pm
==============================================================================
--- apps/CASPlus/trunk/lib/CASPlus/Dispatcher.pm (original)
+++ apps/CASPlus/trunk/lib/CASPlus/Dispatcher.pm Thu Sep 13 15:54:46 2007
@@ -9,6 +9,8 @@
use CASPlus::Action::Logout;
use CASPlus::Action::Validate;
+use Scalar::Util qw/ looks_like_number /;
+
=head1 NAME
CASPlus::Dispatcher - Dispatcher for CAS+
@@ -44,6 +46,44 @@
}
}
+ # Make sure the admin actions are available to administrators
+ {
+ my $current_user = Jifty->web->current_user;
+ my $cud = qr/^CASPlus::Action::(?:Create|Update|Delete)/;
+
+ if ($current_user->may_manage_profiles) {
+ Jifty->api->allow('Jifty::Action::UpdateModelClass');
+ Jifty->api->allow('Jifty::Action::DeleteModelClass');
+ Jifty->api->allow('Jifty::Action::UpdateModelClassColumn');
+ Jifty->api->allow('Jifty::Action::DeleteModelClassColumn');
+ }
+
+ # If not a profile manager, block them
+ else {
+ Jifty->api->deny(qr/${cud}Profile$/);
+ Jifty->api->deny(qr/${cud}ProfileProperty$/);
+ Jifty->api->deny(qr/${cud}ProfileRelationship$/);
+ }
+
+ # If not a user manager, block them
+ if (!$current_user->may_manage_users) {
+ Jifty->api->deny(qr/${cud}User$/);
+ }
+
+ # If not a role manager, block them
+ if (!$current_user->may_manage_roles) {
+ Jifty->api->deny(qr/${cud}Role$/);
+ Jifty->api->deny(qr/${cud}ProfilePermission$/);
+ Jifty->api->deny(qr/${cud}ProfilePermissionLimit$/);
+ Jifty->api->deny(qr/${cud}ProfilePermissionProperty$/);
+ }
+
+ # TODO Come up with a schema for blocking the profile object and
+ # relationship actions. This isn't easy because we have to take the
+ # various permissions into account.
+ }
+
+ # TODO Move part of this to the before /admin rule
# Setup the administration menus
{
my $menu = CASPlus->admin_menu;
@@ -104,11 +144,26 @@
url => '/admin/user/roles',
description => _('Manage the roles that may be granted to user accounts to grant special privileges within the CAS server and in client services.'),
);
+ $users->child( ProfilePermission =>
+ label => _('Profile Permissions'),
+ url => '/admin/user/permissions',
+ description => _('Manage the permissions assigned to roles.'),
+ );
+ $users->child( ProfilePermissionLimit =>
+ label => _('Permission Limits'),
+ url => '/admin/user/limits',
+ description => _('Manage the limits on permissions.'),
+ );
+ $users->child( ProfilePermissionProperty =>
+ lable => _('Permission Properties'),
+ url => '/admin/user/properties',
+ description => _('Determine which properties are affected by a permission.'),
+ );
}
# Sessions menu
my $sessions =
- $menu->child( Sessions => label => 'Sessions', side => 'right' );
+ $menu->child( Sessions => label => 'Sessions', side => 'left' );
$sessions->child( AccessLog =>
label => _('Access Log'),
@@ -142,6 +197,44 @@
}
}
+ if ($current_user->may_manage_profile_objects) {
+ my $relationships_menu =
+ $menu->child( Relationships => label => 'Relationships', side => 'right' );
+
+ my $relationships = CASPlus::Model::ProfileRelationshipCollection->new;
+ $relationships->limit(
+ column => 'many_parents',
+ value => 1,
+ );
+ $relationships->limit(
+ column => 'many_children',
+ value => 1,
+ );
+
+ while (my $relationship = $relationships->next) {
+ $relationships_menu->child( $relationship->link_table->name,
+ label => $relationship->name,
+ url => '/admin/relationship/' . $relationship->link_table->name,
+ description => _('Relationship between %1 and %2.',
+ $relationship->relation_parent->name,
+ $relationship->relation_child->name),
+ );
+ }
+ }
+ }
+};
+
+=head2 /**
+
+Sets up all the main menu items.
+
+=cut
+
+on '**' => run {
+
+ # Setup the main menu for administrator
+ {
+
# Add the Administer item to main navigation
my $nav = Jifty->web->navigation;
$nav->child( Administer =>
@@ -150,6 +243,24 @@
sort_order => 50,
);
}
+
+ # Add the profile link for logged users
+ {
+ if (Jifty->web->current_user->id) {
+ my $nav = Jifty->web->navigation;
+ $nav->child( MyProfile =>
+ label => _('My Profile'),
+ url => '/user/me',
+ sort_order => 20,
+ );
+
+ $nav->child( Logout =>
+ label => _('Logout'),
+ url => '/logout',
+ sort_order => 999,
+ );
+ }
+ }
};
=head2 ROOT
@@ -442,19 +553,45 @@
=cut
+sub _load_user {
+ my $user_or_id = shift;
+
+ my $user;
+ if (ref $user_or_id) {
+ $user = $user_or_id;
+ }
+ else {
+ $user = CASPlus::Model::User->new;
+
+ if (looks_like_number $user_or_id) {
+ $user->load($user_or_id);
+ }
+ else {
+ $user->load_by_cols( username => $user_or_id );
+ }
+ }
+
+ return '' unless $user->id;
+
+ set id => $user->id;
+ set user => $user;
+ set action => $user->as_update_action;
+
+ my $profile = $user->profile;
+ if (defined $profile && $profile->id) {
+ set profile => $profile;
+ set profile_action => $profile->as_update_action;
+ }
+
+ return 1;
+}
+
under 'user' => [
on 'me' => run {
- if (Jifty->web->current_user->id) {
- my $user = Jifty->web->current_user->user_object;
- my $action = Jifty->web->new_action(
- class => 'UpdateUser',
- record => $user,
- );
+ if (Jifty->web->current_user->id
+ && _load_user( Jifty->web->current_user->user_object )) {
- set rdf => $user->as_rdf;
- set user => $user;
- set action => $action;
show '/user/view';
}
@@ -463,6 +600,47 @@
}
},
+ on 'display' => run {
+ my $id = get 'id';
+ _load_user($id);
+ show '/user/display';
+ },
+
+ on 'password' => run {
+ my $id = get 'id';
+ my $user = CASPlus::Model::User->new;
+ $user->load($id);
+
+ my $action = Jifty->web->new_action(
+ class => 'UpdateUserPassword',
+ arguments => { id => $user->id },
+ );
+
+ set user => $user;
+ set action => $action;
+ show '/user/password';
+ },
+
+ under '=' => [
+ on '#' => run {
+ if (_load_user($1)) {
+ show '/user/view';
+ }
+
+ else {
+ Jifty->log->error("No user with ID ($1) found.");
+ }
+ },
+
+ on '*' => run {
+ if (_load_user($1)) {
+ show '/user/view';
+ }
+ else {
+ Jifty->log->error("No user with name '$1' found.");
+ }
+ },
+ ],
];
=head2 /admin/setup
@@ -507,18 +685,54 @@
# Load the associated profile
my $profile = CASPlus::Model::Profile->new;
- $profile->load_by_cols( name => $1 );
+ $profile->load_by_cols( name => $name );
# Stop now unless the profile exists
next_rule unless $profile->id;
+ my $path = '/admin/object/'.$name;
+
+ # Only create the CRUD view if it doesn't exist yet
+ unless (Template::Declare->resolve_template($path)) {
+ CASPlus::View::_register_crud($path, $name, $profile->record_class);
+ }
+
+ # Show that CRUD view!
+ show $path;
+};
+
+=head2 /admin/relationship/*
+
+Automatically creates CRUD views for relationships.
+
+=cut
+
+on 'admin/relationship/*' => run {
+ my $name = $1;
+
+ # Load the link table
+ my $link_table = Jifty::Model::ModelClass->new;
+ $link_table->load_by_cols( name => $name );
+
+ next_rule unless $link_table->id;
+
+ # Load the associated profile
+ my $relationship = CASPlus::Model::ProfileRelationship->new;
+ $relationship->load_by_cols( link_table => $link_table );
+
+ # Stop now unless the profile exists
+ next_rule unless $relationship->id;
+
+ my $path = '/admin/relationship/'.$name;
+
# Only create the CRUD view if it doesn't exist yet
- unless (Template::Declare->resolve_template('/admin/object/'.$name)) {
- CASPlus::View::_register_profile_object($name, $profile->record_class);
+ unless (Template::Declare->resolve_template($path)) {
+ CASPlus::View::_register_crud($path, $name,
+ $relationship->record_class);
}
# Show that CRUD view!
- show '/admin/object/'.$name;
+ show $path;
};
=head2 /error, /error/**
Modified: apps/CASPlus/trunk/lib/CASPlus/Model/SSOSession.pm
==============================================================================
--- apps/CASPlus/trunk/lib/CASPlus/Model/SSOSession.pm (original)
+++ apps/CASPlus/trunk/lib/CASPlus/Model/SSOSession.pm Thu Sep 13 15:54:46 2007
@@ -138,10 +138,11 @@
my $self = shift;
my $now = DateTime->now;
+ my $exp = $self->expiration_time;
return $self->id
- && $self->current_valid_ticket
- && (!defined $self->expiration_time || $now < $self->expiration_time);
+ && $self->current_valid_ticket
+ && (!defined $exp || $now < $exp);
}
=head2 fetch_cookie
@@ -211,7 +212,7 @@
sub current_user_can {
my ($self, $right, %args) = @_;
-
+
if ($self->current_user->id) {
if ($right eq 'create') {
Modified: apps/CASPlus/trunk/lib/CASPlus/Model/ServiceSession.pm
==============================================================================
--- apps/CASPlus/trunk/lib/CASPlus/Model/ServiceSession.pm (original)
+++ apps/CASPlus/trunk/lib/CASPlus/Model/ServiceSession.pm Thu Sep 13 15:54:46 2007
@@ -127,11 +127,12 @@
my $self = shift;
my $now = DateTime->now;
+ my $exp = $self->expiration_time;
- return $self->id
+ return $self->id
&& $self->sso_session->is_valid
- && $self->current_valid_ticket
- && (!defined $self->expiration_time || $now < $self->expiration_time);
+ && $self->current_valid_ticket
+ && (!defined $exp || $now < $exp);
}
=head2 authenticated_user
Modified: apps/CASPlus/trunk/lib/CASPlus/Model/User.pm
==============================================================================
--- apps/CASPlus/trunk/lib/CASPlus/Model/User.pm (original)
+++ apps/CASPlus/trunk/lib/CASPlus/Model/User.pm Thu Sep 13 15:54:46 2007
@@ -88,6 +88,12 @@
my ($self, $value) = @_;
my $password = $self->__value('password');
+
+ unless (ref $password eq 'ARRAY') {
+ Jifty->log->warn("NO PASSWORD for user @{[$self->username]}");
+ return '';
+ }
+
my ($hash, $salt) = @$password;
return $hash eq md5_hex($value . $salt);
Modified: apps/CASPlus/trunk/lib/CASPlus/ProfileRelationshipBase.pm
==============================================================================
--- apps/CASPlus/trunk/lib/CASPlus/ProfileRelationshipBase.pm (original)
+++ apps/CASPlus/trunk/lib/CASPlus/ProfileRelationshipBase.pm Thu Sep 13 15:54:46 2007
@@ -18,12 +18,42 @@
Any user with superuser user or the manage profile objects permission assigned to at least one role may access this object.
-Otherwise, a user will only be able to create, read, write, or delete this object if it is granted by a profile permission associated with the user's roles. This association must be with both ends of a link. If the user is able to create
+Otherwise, a user will only be able to create, read, write, or delete this object if it is granted by a profile permission associated with the user's roles. This association must be with both ends of a link.
=cut
-# XXX FIXME TODO Add triggers for the parent and child columns to handle
-# add/delete of relationships in many-to-many.
+sub current_user_can {
+ my $self = shift;
+ my ($right, %args) = @_;
+
+ # Has access if an administrator
+ return 1 if $self->current_user->may_manage_profile_objects;
+
+ # These will store the left and right permissions
+ my ($a, $b);
+
+ # handle create
+ if ($right eq 'create') {
+ $a = $self->current_user->can_access_profile(
+ $args{parent}, $right, %args);
+ $b = $self>current_user->can_access_profile(
+ $args{child}, $right, %args);
+ }
+
+ # handle read, write, and delete
+ else {
+ $a = $self->current_user->can_access_profile(
+ $self->parent, $right, %args);
+ $b = $self->current_user->can_access_profile(
+ $self->child, $right, %args);
+ }
+
+ # does the use have access to both ends?
+ return 1 if $a and $b;
+
+ # fallback on the built-ins otherwise
+ return $self->SUPER::current_user_can($right, %args);
+}
=head2 profile_relationship_definition
Modified: apps/CASPlus/trunk/lib/CASPlus/ProfileRelationshipMixin.pm
==============================================================================
--- apps/CASPlus/trunk/lib/CASPlus/ProfileRelationshipMixin.pm (original)
+++ apps/CASPlus/trunk/lib/CASPlus/ProfileRelationshipMixin.pm Thu Sep 13 15:54:46 2007
@@ -89,6 +89,20 @@
my $args = delete $self->{__create_relationship};
$args->{relationship} = $self->profile_relationship_definition;
+ # Convert parent into object
+ unless (ref $args->{parent}) {
+ my $id = $args->{parent};
+ $args->{parent} = $args->{relationship}->relation_parent->record_class->new;
+ $args->{parent}->load($id);
+ }
+
+ # Convert child into object
+ unless (ref $args->{child}) {
+ my $id = $args->{child};
+ $args->{child} = $args->{relationship}->relation_child->record_class->new;
+ $args->{child}->load($id);
+ }
+
CASPlus::Util::Relationship->add_relationship(%$args) if $result;
return 1;
Modified: apps/CASPlus/trunk/lib/CASPlus/View.pm
==============================================================================
--- apps/CASPlus/trunk/lib/CASPlus/View.pm (original)
+++ apps/CASPlus/trunk/lib/CASPlus/View.pm Thu Sep 13 15:54:46 2007
@@ -14,6 +14,9 @@
'User::Users' => 'User',
'User::Roles' => 'Role',
+ 'User::Permissions' => 'ProfilePermission',
+ 'User::Limits' => 'ProfilePermissionLimit',
+ 'User::Properties' => 'ProfilePermissionProperty',
);
Readonly our @ADMIN_OTHER_VIEWS => qw/
@@ -256,6 +259,9 @@
};
};
+use CASPlus::View::User;
+alias CASPlus::View::User under '/user';
+
use CASPlus::View::Error;
alias CASPlus::View::Error under '/error';
@@ -281,12 +287,13 @@
alias $view_class under $path;
}
-sub _register_profile_object {
+sub _register_crud {
+ my $path = shift;
my $name = shift;
my $record_class = shift;
# Install a CRUD view for the profile object
- alias Jifty::View::Declare::CRUD under '/admin/object/'.$name, {
+ alias Jifty::View::Declare::CRUD under $path, {
object_type => $name,
record_class => $record_class,
};
Added: apps/CASPlus/trunk/lib/CASPlus/View/Admin/User/Limits.pm
==============================================================================
--- (empty file)
+++ apps/CASPlus/trunk/lib/CASPlus/View/Admin/User/Limits.pm Thu Sep 13 15:54:46 2007
@@ -0,0 +1,8 @@
+use strict;
+use warnings;
+
+package CASPlus::View::Admin::User::Limits;
+use Jifty::View::Declare -base;
+use base qw/ Jifty::View::Declare::CRUD /;
+
+1;
Added: apps/CASPlus/trunk/lib/CASPlus/View/Admin/User/Permissions.pm
==============================================================================
--- (empty file)
+++ apps/CASPlus/trunk/lib/CASPlus/View/Admin/User/Permissions.pm Thu Sep 13 15:54:46 2007
@@ -0,0 +1,8 @@
+use strict;
+use warnings;
+
+package CASPlus::View::Admin::User::Permissions;
+use Jifty::View::Declare -base;
+use base qw/ Jifty::View::Declare::CRUD /;
+
+1;
Added: apps/CASPlus/trunk/lib/CASPlus/View/Admin/User/Properties.pm
==============================================================================
--- (empty file)
+++ apps/CASPlus/trunk/lib/CASPlus/View/Admin/User/Properties.pm Thu Sep 13 15:54:46 2007
@@ -0,0 +1,8 @@
+use strict;
+use warnings;
+
+package CASPlus::View::Admin::User::Properties;
+use Jifty::View::Declare -base;
+use base qw/ Jifty::View::Declare::CRUD /;
+
+1;
Added: apps/CASPlus/trunk/lib/CASPlus/View/User.pm
==============================================================================
--- (empty file)
+++ apps/CASPlus/trunk/lib/CASPlus/View/User.pm Thu Sep 13 15:54:46 2007
@@ -0,0 +1,70 @@
+use strict;
+use warnings;
+
+package CASPlus::View::User;
+use Jifty::View::Declare -base;
+
+use Jifty::Plugin::TabView::View;
+use Scalar::Defer qw/ defer /;
+
+template 'view' => page {
+ my $self = shift;
+ my $user = get 'user';
+
+ { title is _(q{%1's Profile}, $user->username) }
+
+ form {
+ $self->render_tabs('profile', [qw(id)],
+ { label => 'View', path => '/user/display', defer => 1 },
+ { label => 'Password', path => '/user/password', defer => 1 },
+ );
+ };
+};
+
+template 'display' => sub {
+ my $action = get 'action';
+ my $profile = get 'profile';
+ my $paction = get 'profile_action';
+
+ die unless $action;
+
+ render_action $action, [ qw/ username / ], { render_mode => 'read' };
+
+ if ($profile) {
+ render_action $paction,
+ [ grep { $_ ne 'user_object' }
+ map { $_->name } $profile->columns ],
+ { render_mode => 'read' };
+ }
+};
+
+template 'password' => sub {
+ my $user = get 'user';
+ my $action = get 'action';
+
+ my $current_user = Jifty->web->current_user;
+ if ($current_user->may_manage_users && $current_user->id != $user->id) {
+ p { outs_raw _(q{To change <em>%1's</em> password, please fill out the following form and click <strong>Change Password</strong>.}, $user->username); };
+
+ # XXX TODO FIXME The defer thing is a cheap trick to focus new_password
+ my $count = 0;
+ render_action $action, [ qw( id new_password confirm_password ) ], {
+ focus => defer { $count++ == 1 ? 1 : 0 },
+ };
+ }
+ else {
+ p { outs_raw _('If you would like to update your password, please fill in the following form and click <strong>Change Password</strong>.'); };
+
+ render_action $action;
+ }
+
+ form_submit
+ label => 'Change Password',
+ onclick => {
+ submit => $action,
+ refresh_self => 1,
+ },
+ ;
+};
+
+1;
Modified: apps/CASPlus/trunk/share/web/static/css/app.css
==============================================================================
--- apps/CASPlus/trunk/share/web/static/css/app.css (original)
+++ apps/CASPlus/trunk/share/web/static/css/app.css Thu Sep 13 15:54:46 2007
@@ -36,3 +36,139 @@
font-size: 1.2em;
margin-bottom: 0.2em;
}
+
+/* default space between tabs */
+.yui-navset .yui-nav li {
+ margin-right:0.5em; /* horizontal tabs */
+}
+.yui-navset-left .yui-nav li, .yui-navset-right .yui-nav li {
+ margin:0 0 0.5em; /* vertical tabs */
+}
+
+/* default width for side tabs */
+.yui-navset-left .yui-nav, .yui-navset-right .yui-nav { width:6em; }
+.yui-navset-left { padding-left:6em; } /* map to nav width */
+.yui-navset-right { padding-right:6em; } /* ditto */
+
+/* core */
+
+.yui-nav, .yui-nav li {
+ margin:0;
+ padding:0;
+ list-style:none;
+}
+.yui-navset li em { font-style:normal; }
+
+.yui-navset {
+ position:relative; /* contain absolute positioned tabs (left/right) */
+ zoom:1;
+}
+
+.yui-navset .yui-content { zoom:1; }
+
+.yui-navset .yui-nav li {
+ display:inline-block;
+ display:-moz-inline-stack;
+ *display:inline; /* IE */
+ vertical-align:bottom; /* safari: for overlap */
+ cursor:pointer; /* gecko: due to -moz-inline-stack on anchor */
+ zoom:1; /* IE: kill space between horizontal tabs */
+}
+
+.yui-navset-left .yui-nav li, .yui-navset-right .yui-nav li {
+ display:block;
+}
+
+.yui-navset .yui-nav a {
+ outline:0; /* gecko: keep from shifting */
+}
+
+.yui-navset .yui-nav a { position:relative; } /* IE: to allow overlap */
+
+.yui-navset .yui-nav li a {
+ display:block;
+ display:inline-block;
+ vertical-align:bottom; /* safari: for overlap */
+ zoom:1;
+}
+
+.yui-navset-left .yui-nav li a, .yui-navset-right .yui-nav li a {
+ display:block;
+}
+
+.yui-navset-bottom .yui-nav li a {
+ vertical-align:text-top; /* for inline overlap (reverse for Op border bug) */
+}
+
+.yui-navset .yui-nav li a em { display:block; }
+
+/* position left and right oriented tabs */
+.yui-navset-left .yui-nav, .yui-navset-right .yui-nav { position:absolute; z-index:1; }
+.yui-navset-left .yui-nav { left:0; }
+.yui-navset-right .yui-nav { right:0; }
+
+.yui-navset .yui-nav li a, .yui-navset .yui-content {
+ border:1px solid #000; /* label and content borders */
+}
+
+.yui-navset .yui-nav .selected a, .yui-navset .yui-nav a:hover, .yui-navset .yui-content {
+ background-color:#f6f7ee; /* active tab, tab hover, and content bgcolor */
+}
+
+.yui-navset .yui-nav li em { padding:.5em; } /* tab padding */
+
+/* defaults to orientation "top" */
+.yui-navset .yui-nav .selected a {
+ border-bottom-width:0; /* no bottom border for active tab */
+ padding-bottom:1px; /* to match height of other tabs */
+}
+
+.yui-navset .yui-content {
+ margin-top:-1px; /* for active tab overlap */
+}
+
+/* overrides for other orientations */
+
+.yui-navset-bottom .yui-nav .selected a {
+ border-width:0 1px 1px; /* no top border for active tab */
+ padding:1px 0 0; /* to match height of other tabs */
+}
+
+.yui-navset-bottom .yui-content {
+ margin:0 0 -1px; /* for active tab overlap */
+}
+
+.yui-navset-left .yui-nav li.selected a {
+ border-width:1px 0 1px 1px; /* no right border for active tab */
+ padding:0 1px 0 0; /* to match width of other tabs */
+}
+
+.yui-navset-left .yui-content {
+ margin:0 0 0 -1px; /* for active tab overlap */
+}
+
+.yui-navset-right .yui-nav li.selected a {
+ border-width:1px 1px 1px 0; /* no left border for active tab */
+ padding:0 0 0 1px; /* to match width of other tabs */
+}
+
+.yui-navset-right .yui-content {
+ margin:0 -1px 0 0; /* for active tab overlap */
+}
+
+ul.yui-nav {
+ padding: 0 1em;
+}
+
+.yui-navset .yui-nav .selected a {
+ padding: 0.3em;
+}
+
+ul.yui-nav li a {
+ padding: 0.3em;
+ vertical-align: inherit;
+}
+
+div.yui-content {
+ padding: 1em;
+}
Added: apps/CASPlus/trunk/t/20-action-UpdateUserPassword.t
==============================================================================
--- (empty file)
+++ apps/CASPlus/trunk/t/20-action-UpdateUserPassword.t Thu Sep 13 15:54:46 2007
@@ -0,0 +1,15 @@
+#!/usr/bin/env perl
+use warnings;
+use strict;
+
+=head1 DESCRIPTION
+
+A (very) basic test harness for the UpdateUserPassword action.
+
+=cut
+
+use Jifty::Test tests => 1;
+
+# Make sure we can load the action
+use_ok('CASPlus::Action::UpdateUserPassword');
+
Modified: apps/CASPlus/trunk/t/50-me.t
==============================================================================
--- apps/CASPlus/trunk/t/50-me.t (original)
+++ apps/CASPlus/trunk/t/50-me.t Thu Sep 13 15:54:46 2007
@@ -2,7 +2,7 @@
use strict;
use warnings;
-use Jifty::Test tests => 27;
+use Jifty::Test tests => 17;
use Jifty::Test::WWW::Mechanize;
my $system_user = CASPlus::CurrentUser->superuser;
@@ -51,46 +51,6 @@
$mech->title_is('Login Status', 'attempted login');
$mech->content_contains('test-me', 'login success');
-# XXX Use instead of follow_link_ok because Jifty::Test::WWW::Mechanize applies
-# HTML::Lint, which pukes on the RDF.
-my $response = $mech->follow_link(text => 'test-me');
-ok($response->is_success, 'go to me');
+$mech->follow_link_ok(text => 'test-me', 'go to me');
$mech->title_is(q{test-me's Profile}, 'test-me profile page');
-
-SKIP: {
- eval "use XML::LibXML";
- skip "XML::LibXML is installed and these tests will fail when using that library.", 10 unless $@;
- eval "use Test::XML::XPath";
- skip "Test::XML::XPath is not available.", 10 if $@;
-
- my $xml = $mech->content;
- my $xp = XML::XPath->new($xml);
-
- like_xpath($xml, '//rdf:RDF', 'has RDF element');
- like_xpath($xml, '//rdf:RDF/casplus:user', 'has user element');
-
- my $rdf_ns = $xp->findnodes('//rdf:RDF')->shift
- ->getNamespace('rdf')->getExpanded;
- my $casplus_ns = $xp->findnodes('//rdf:RDF/casplus:user')->shift
- ->getNamespace('casplus')->getExpanded;
-
- is($rdf_ns, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', 'rdf namespace');
- is($casplus_ns, 'http://sterling.hanenkamp.com/rdf/CASPlus#',
- 'casplus namespace');
-
- my $user_id = $user->id;
- like_xpath($xml, "//rdf:RDF/casplus:user[\@rdf:about='$url/user/$user_id']",
- 'rdf:about is set correctly');
- like_xpath($xml, '//rdf:RDF/casplus:user/casplus:username',
- 'username namespace');
- is_xpath($xml, '//rdf:RDF/casplus:user/casplus:username', 'test-me',
- 'username is set correctly');
-
- like_xpath($xml, '//rdf:RDF/casplus:user/casplus:profile',
- 'user has a profile');
- like_xpath($xml, '//rdf:RDF/casplus:user/casplus:profile/casplus:full_name',
- 'user has a full name');
- is_xpath($xml, '//rdf:RDF/casplus:user/casplus:profile/casplus:full_name',
- 'Unit Test', 'full name is set correctly');
-};
More information about the Jifty-commit
mailing list