Commit 4301aced authored by Cecilia Vela Gurovic's avatar Cecilia Vela Gurovic Committed by Gerrit Code Review

Merge changes from topic 'WR325528'

* changes:
  Bug 1859115: Add SAML specific error message when user not found
  Bug 1855383: Adding in autogroupadmin user role
parents f9c8dbf8 a7b09164
......@@ -115,6 +115,8 @@ $groupadminsform = pieform(array(
'elements' => array(
'admins' => array(
'type' => 'userlist',
'group' => $group->id,
'allowuserrules' => true,
'hiddenlabel' => true,
'title' => get_string('groupadmins', 'group'),
'defaultvalue' => $admins,
......@@ -142,7 +144,7 @@ function groupadminsform_submit(Pieform $form, $values) {
UPDATE {group_member}
SET role = 'member'
WHERE role = 'admin' AND \"group\" = ?
AND member IN ($demoted)",
AND \"member\" IN ($demoted)",
array($group->id)
);
}
......
......@@ -110,7 +110,7 @@ if ($user->roles && isset($user->roles['_site'])) {
foreach ($user->roles['_site'] as $rk => $role) {
$roleelements['userroles_' . $role->id] = array(
'type' => 'switchbox',
'title' => get_string($rk),
'title' => get_string($rk, 'artefact.internal'),
'defaultvalue' => $role->active,
);
}
......
......@@ -76,8 +76,10 @@ $string['pinterest.input'] = 'Pinterest username';
$string['pinterest'] = 'Pinterest';
$string['occupation'] = 'Occupation';
$string['industry'] = 'Industry';
// Custom user roles
$string['userroles'] = 'User roles';
$string['nospecialroles'] = '<span class="text-midtone">No special roles</span>';
$string['autogroupadmin'] = 'Auto group admin';
// Field names for view user and search user display
$string['name'] = 'Name';
......
......@@ -159,7 +159,7 @@ $instance = auth_saml_find_authinstance($saml_attributes);
// if we don't have an auth instance then this is a serious failure
if (!$instance) {
throw new UserNotFoundException(get_string('errorbadinstitution', 'auth.saml'));
throw new SamlUserNotFoundException(get_string('errorbadinstitution', 'auth.saml'));
}
if ($SESSION->get('migratecheck')) {
......@@ -182,7 +182,7 @@ try {
$can_login = $auth->request_user_authorise($saml_attributes);
}
catch (AccessDeniedException $e) {
throw new UserNotFoundException(get_string('errnosamluser', 'auth.saml'));
throw new SamlUserNotFoundException(get_string('errnosamluser', 'auth.saml'));
}
catch (XmlrpcClientException $e) {
throw new AccessDeniedException($e->getMessage());
......@@ -217,7 +217,7 @@ if ($can_login) {
// are we configured to allow testing of local login and linking?
$loginlink = get_field('auth_instance_config', 'value', 'field', 'loginlink', 'instance', $instance->id);
if (empty($loginlink)) {
throw new UserNotFoundException(get_string('errnosamluser', 'auth.saml'));
throw new SamlUserNotFoundException(get_string('errnosamluser', 'auth.saml'));
}
// used in the submit callback for auth_saml_loginlink_screen()
......
......@@ -96,6 +96,9 @@ $string['samlfieldforrolesitestaff'] = 'Role mapping for site staff';
$string['samlfieldforroleinstadmin'] = 'Role mapping for institution administrator';
$string['samlfieldforroleinststaff'] = 'Role mapping for institution staff';
$string['samlfieldfororganisationname'] = 'SSO field for Organisation';
$string['samlfieldforautogroups'] = 'Roles have auto group administration';
$string['samlfieldforautogroupsall'] = 'Auto group administration to all groups';
$string['samlfieldforautogroupsalldescription'] = 'If enabled then the user will be added as a group admin to all groups otherwise they are only added as a group admin to groups within their institution.';
$string['samlfieldauthloginmsg'] = 'Wrong login message';
$string['spentityid'] = "Service Provider entityId";
$string['title'] = 'SAML';
......
......@@ -100,6 +100,8 @@ class AuthSaml extends Auth {
$this->config['roleinstadmin'] = '';
$this->config['roleinststaff'] = '';
$this->config['organisationname'] = '';
$this->config['roleautogroups'] = '';
$this->config['roleautogroupsall'] = false;
$this->instanceid = $id;
if (!empty($id)) {
......@@ -164,6 +166,11 @@ class AuthSaml extends Auth {
$rolesitestaff = isset($this->config['rolesitestaff']) ? array_map('trim', explode(',', $this->config['rolesitestaff'])) : array();
$roleinstadmin = isset($this->config['roleinstadmin']) ? array_map('trim', explode(',', $this->config['roleinstadmin'])) : array();
$roleinststaff = isset($this->config['roleinststaff']) ? array_map('trim', explode(',', $this->config['roleinststaff'])) : array();
$roleautogroups = isset($this->config['roleautogroups']) ? array_map('trim', explode(',', $this->config['roleautogroups'])) : array();
$roleautogroupsall = isset($this->config['roleautogroupsall']) ? $this->config['roleautogroupsall'] : false;
if (is_isolated()) {
$roleautogroupsall = false;
}
$institutionname = $this->institution;
$create = false;
......@@ -259,6 +266,7 @@ class AuthSaml extends Auth {
/*******************************************/
$institutionrole = 'member'; // default role
$userroles = array();
$usr_is_siteadmin = 0;
$usr_is_sitestaff = 0;
if ($roles && is_array($roles)) {
......@@ -277,6 +285,12 @@ class AuthSaml extends Auth {
if (in_array($rv, $roleinststaff)) {
$institutionrole = 'staff';
}
if (in_array($rv, $roleautogroups)) {
$userroles[] = array('role' => 'autogroupadmin',
'institution' => ($roleautogroupsall ? '_site' : $institutionname),
'active' => 1,
'provisioner' => 'saml');
}
}
}
......@@ -404,6 +418,52 @@ class AuthSaml extends Auth {
$user->lastlastlogin = $user->lastlogin;
$user->lastlogin = time();
}
if (!empty($userroles)) {
if ($create) {
$user->set_roles($userroles);
}
else {
$user->get_roles();
// Turn off all the roles that are not associated with the SAML user roles anymore
foreach ($user->roles as $inst => $roles) {
if (in_array($inst, array_column($userroles, 'institution')) === false) {
// Not in institution anymore so remove institution specific roles
foreach ($roles as $role) {
$user->update_role($role->id, 0);
}
continue;
}
foreach ($roles as $k => $role) {
if (in_array($role->role, array_column($userroles, 'role')) === false) {
// User does not have role any more in IdP so remove role
$user->update_role($role->id, 0);
}
}
}
// Now check which roles need adding / updating
foreach ($userroles as $index => $userrole) {
if (isset($user->roles[$userrole['institution']]) &&
isset($user->roles[$userrole['institution']][$userrole['role']])) {
if ($user->roles[$userrole['institution']][$userrole['role']]->active == 0) {
// Need to activate role
$user->update_role($user->roles[$userrole['institution']][$userrole['role']]->id, 1);
}
}
else {
// Need to add role
$user->set_roles(array($userrole));
}
}
}
}
else if (empty($userroles) && !$create) {
// User exists but doesn't have any user roles so we need to turn of all existing ones
$existingroleids = get_column('usr_roles', 'id', 'usr', $user->get('id'), 'active', 1);
foreach ($existingroleids as $roleid) {
$user->update_role($roleid, 0);
}
}
$user->commit();
/**
......@@ -465,6 +525,8 @@ class PluginAuthSaml extends PluginAuth {
'roleinstadmin' => '',
'roleinststaff' => '',
'organisationname' => '',
'roleautogroups' => '',
'roleautogroupsall' => 0,
'emailfield' => '',
'studentidfield' => '',
'updateuserinfoonlogin' => 1,
......@@ -1427,8 +1489,20 @@ EOF;
'type' => 'text',
'title' => get_string('samlfieldforroleinststaff', 'auth.saml'),
'defaultvalue' => self::$default_config['roleinststaff'],
),
'roleautogroups' => array(
'type' => 'text',
'title' => get_string('samlfieldforautogroups', 'auth.saml'),
'defaultvalue' => self::$default_config['roleautogroups'],
'help' => false,
),
'roleautogroupsall' => array(
'type' => 'switchbox',
'title' => get_string('samlfieldforautogroupsall', 'auth.saml'),
'defaultvalue' => is_isolated() ? false : self::$default_config['roleautogroupsall'],
'description' => get_string('samlfieldforautogroupsalldescription', 'auth.saml'),
'disabled' => is_isolated(),
),
'authloginmsg' => array(
'type' => 'wysiwyg',
'rows' => 10,
......@@ -1605,6 +1679,8 @@ EOF;
'roleinstadmin' => $values['roleinstadmin'],
'roleinststaff' => $values['roleinststaff'],
'organisationname' => $values['organisationname'],
'roleautogroups' => $values['roleautogroups'],
'roleautogroupsall' => $values['roleautogroupsall'],
'updateuserinfoonlogin' => $values['updateuserinfoonlogin'],
'institutionattribute' => $values['institutionattribute'],
'institutionvalue' => $values['institutionvalue'],
......
......@@ -1050,7 +1050,11 @@ class User {
}
public function update_role($roleid, $state) {
set_field('usr_roles', 'active', $state, 'id', $roleid);
$role = ucfirst(get_field('usr_roles', 'role', 'id', $roleid));
$classname = 'UserRole' . $role;
$r = new $classname((object)array('id' => $roleid));
$r->set('active', $state);
$r->commit();
$this->reset_roles();
}
......@@ -2322,6 +2326,113 @@ abstract class UserRole {
}
}
class UserRoleAutogroupadmin extends UserRole {
public function __construct($data=null) {
parent::__construct('autogroupadmin', $data);
}
public function commit() {
parent::commit();
require_once(get_config('docroot') . 'lib/group.php');
$institution = $this->institution == '_site' || $this->institution === null ? 'all' : $this->institution;
if ($this->active) {
// Add the user to all the groups as a group admin
group_add_user_to_existing_groups($this->usr, 'admin', $institution);
}
else {
// Remove the user from all the groups where they are group admin
group_remove_user_from_existing_groups($this->usr, $institution);
}
}
private function _prepare_sql($data) {
$wheresql = 'role = ? ';
$where = array($this->role);
if (empty($data['institution']) || $data['institution'] == '_site') {
$wheresql .= 'AND institution IS NULL ';
}
else {
$wheresql .= 'AND (institution = ? OR institution IS NULL) ';
$where[] = $data['institution'];
}
if (isset($data['active'])) {
$wheresql .= 'AND active = ? ';
$where[] = (bool)$data['active'];
}
if (!empty($data['userid'])) {
$wheresql .= 'AND usr = ? ';
$where[] = $data['userid'];
}
if (!empty($data['provisioner'])) {
$wheresql .= 'AND provisioner = ? ';
$where[] = $data['provisioner'];
}
return array($wheresql, $where);
}
public function group_join($data) {
if (!empty($data['groupid'])) {
list($wheresql, $where) = self::_prepare_sql($data);
if ($results = get_column_sql("SELECT usr FROM {usr_roles} WHERE " . $wheresql . " AND active = 1", $where)) {
foreach ($results as $userid) {
insert_record('group_member', (object) array(
'group' => $data['groupid'],
'member' => $userid,
'role' => 'admin',
'ctime' => !empty($data['ctime']) ? $data['ctime'] : db_format_timestamp(time()),
));
}
return array('userids' => $results);
}
}
return false;
}
public function group_leave($data) {
if (!empty($data['groupid']) && !empty($data['userid'])) {
list($wheresql, $where) = self::_prepare_sql($data);
if ($results = get_column_sql("SELECT usr FROM {usr_roles} WHERE " . $wheresql, $where)) {
return array('can_leave' => false);
}
}
return array('can_leave' => true);
}
public function interaction_subscribe($data) {
if (!empty($data['userid'])) {
// subscribe one user to the forum
subscribe_user_to_forum($data['userid'], $data['id']);
}
else {
// Need to make sure all autogroupadmins are subscribed
list($wheresql, $where) = self::_prepare_sql($data);
if ($results = get_column_sql("SELECT usr FROM {usr_roles} WHERE " . $wheresql . " AND active = 1", $where)) {
foreach ($results as $userid) {
if (!get_record('interaction_forum_subscription_forum', 'user', $userid, 'forum', $data['id'])) {
subscribe_user_to_forum($data['userid'], $data['id']);
}
}
}
}
return array('can_subscribe' => true);
}
public function interaction_unsubscribe($data) {
if (!empty($data['userid'])) {
// Check to see if this user has the authgroupadmin role
list($wheresql, $where) = self::_prepare_sql($data);
if ($results = get_column_sql("SELECT usr FROM {usr_roles} WHERE " . $wheresql, $where)) {
// Not allowed to remove subscription
return array('can_unsubscribe' => false);
}
}
return array('can_unsubscribe' => true);
}
}
/**
* Indicates whether the site is closed for a user
* @param boolean $isuseradmin Whether the user we're checking for is an admin
......
......@@ -57,17 +57,18 @@ if ($forums) {
foreach ($forums as $forum) {
$forum->feedlink = get_config('wwwroot') . 'interaction/forum/atom.php?type=f&id=' . $forum->id;
$allowunsubscribe = get_config_plugin_instance('interaction_forum', $forum->id, 'allowunsubscribe');
if ($allowunsubscribe) {
if ($allowunsubscribe === null || $allowunsubscribe > 0) {
// Check if any UserRoles are in play
$checks = $USER->apply_userrole_method('interaction_unsubscribe', array('forum' => $forum->id, 'userid' => $USER->get('id')));
$checks = $USER->apply_userrole_method('interaction_unsubscribe', array('forum' => $forum->id, 'userid' => $USER->get('id'), 'institution' => $group->institution));
foreach ($checks as $check) {
if ($check['can_unsubscribe'] === false) {
// A UserRole is stopping us from unsubscribing
$allowunsubscribe = false;
$allowunsubscribe = 0;
break;
}
}
}
if ($membership) {
$forum->subscribe = pieform(array(
'name' => 'subscribe_forum' . ($i == 0 ? '' : $i),
......
......@@ -1703,7 +1703,7 @@ function subscribe_forum_submit(Pieform $form, $values) {
else {
$can_unsubscribe = true;
// Check if any UserRoles are in play
$checks = $USER->apply_userrole_method('interaction_unsubscribe', array('forum' => $values['forum'], 'userid' => $USER->get('id')));
$checks = $USER->apply_userrole_method('interaction_unsubscribe', array('forum' => $values['forum'], 'userid' => $USER->get('id'), 'institution' => get_field('group', 'institution', 'id', $values['group'])));
foreach ($checks as $check) {
if ($check['can_unsubscribe'] === false) {
// A UserRole is stopping us from unsubscribing
......
......@@ -83,3 +83,4 @@ $string['warninstitutionregistrationinstitutions'] = array(
);
$string['warnmultiinstitutionsoff'] = '$cfg->usersuniquebyusername is turned on but the site option \'Users allowed multiple institutions\' is off. This makes no sense, as users will then change institution every time they log in from somewhere else. Please turn this setting on in Administration -> Configure site -> Institution settings.';
$string['alternativelogins'] = 'Administration login';
$string['unabletosigninviasso'] = 'Unable to sign in via external authentication (SSO)';
......@@ -968,6 +968,17 @@ class ViewLimitExceededException extends UserException {}
*/
class UserNotFoundException extends NotFoundException {}
/**
* Exception - user not found while doing SAML authentication
*/
class SamlUserNotFoundException extends UserNotFoundException {
public function strings() {
return array_merge(parent::strings(),
array('message' => get_string('invaliduser', 'error')),
array('title' => get_string('unabletosigninviasso', 'auth')));
}
}
/**
* Exception - user not found while doing XMLRPC authentication
*/
......
......@@ -36,9 +36,18 @@ function pieform_element_userlist(Pieform $form, $element) {
$members = get_records_select_assoc('usr','id IN (' . join(',',array_map('intval', $value)) . ')', null, $orderby, 'id,username,firstname,lastname,preferredname,staff');
foreach($members as &$member) {
$member = display_name($member);
$member->displayname = display_name($member);
if (!empty($element['allowuserrules']) && !empty($element['group'])) {
global $USER;
$institution = get_field('group', 'institution', 'id', $element['group']);
$checks = $USER->apply_userrole_method('group_leave', array('groupid' => $element['group'], 'userid' => $member->id, 'institution' => $institution));
foreach ($checks as $check) {
if ($check['can_leave'] === false) {
$member->disabled = true;
}
}
}
}
$smarty->assign('options',$members);
$smarty->assign('value', join(',',$value));
}
......
......@@ -494,7 +494,7 @@ function group_create($data) {
);
}
// Check if any UserRoles are in play
$USER->apply_userrole_method('group_join', array('groupid' => $id, 'ctime' => $data['ctime']));
$USER->apply_userrole_method('group_join', array('groupid' => $id, 'ctime' => $data['ctime'], 'institution' => $data['institution']));
// Copy views for the new group
$artefactcopies = array();
......@@ -1077,7 +1077,7 @@ function group_user_can_leave($group, $userid=null) {
}
// Check if any UserRoles are in play
$checks = $USER->apply_userrole_method('group_leave', array('groupid' => $group->id, 'userid' => $userid));
$checks = $USER->apply_userrole_method('group_leave', array('groupid' => $group->id, 'userid' => $userid, 'institution' => $group->institution));
foreach ($checks as $check) {
if ($check['can_leave'] === false) {
return ($result[$group->id][$userid] = false);
......@@ -1898,9 +1898,10 @@ function group_get_membersearch_data($results, $group, $query, $membershiptype,
$role = group_user_access($group);
$userid = $USER->get('id');
$institution = get_field('group', 'institution', 'id', $group);
foreach ($results['data'] as &$r) {
// Check if any UserRoles are in play
$checks = $USER->apply_userrole_method('group_leave', array('groupid' => $group, 'userid' => $r['id']));
$checks = $USER->apply_userrole_method('group_leave', array('groupid' => $group, 'userid' => $r['id'], 'institution' => $institution));
foreach ($checks as $check) {
if ($check['can_leave'] === false) {
continue 2;
......@@ -3268,7 +3269,7 @@ function get_group_access_roles() {
return $data;
}
function group_add_user_to_existing_groups($userid = null, $role = 'member') {
function group_add_user_to_existing_groups($userid = null, $role = 'member', $institution = 'all') {
global $USER;
if (empty($userid)) {
......@@ -3276,11 +3277,21 @@ function group_add_user_to_existing_groups($userid = null, $role = 'member') {
}
// Find all the non-deleted groups where the user is not present
// or is present but with a different group role
$where = array($userid, $role);
$wheresql = '';
if (is_array($institution)) {
$wheresql .= ' AND g.institution IN (' . join(',', array_map('db_quote', $institution)) . ')';
}
else if ($institution != 'all') {
$wheresql .= ' AND g.institution IN (?)';
$where[] = $institution;
}
if ($groups = get_records_sql_assoc("SELECT g.id, gm.* FROM {group} g
LEFT JOIN {group_member} gm ON (gm.group = g.id AND gm.member = ?)
WHERE (gm.role IS NULL OR gm.role != ?)
AND g.deleted = 0
ORDER BY g.id", array($userid, $role))) {
" . $wheresql . "
ORDER BY g.id", $where)) {
foreach ($groups as $k => $group) {
if ($group->role) {
try {
......@@ -3306,17 +3317,27 @@ function group_add_user_to_existing_groups($userid = null, $role = 'member') {
$user->reset_grouproles();
}
function group_remove_user_from_existing_groups($userid = null) {
function group_remove_user_from_existing_groups($userid = null, $institution = 'all') {
global $USER;
if (empty($userid)) {
$userid = $USER->get('id');
}
// Find all the non-deleted groups where the user is present
$where = array($userid);
$wheresql = '';
if (is_array($institution)) {
$wheresql .= ' AND g.institution IN (' . join(',', array_map('db_quote', $institution)) . ')';
}
else if ($institution != 'all') {
$wheresql .= ' AND g.institution IN (?)';
$where[] = $institution;
}
if ($groups = get_records_sql_assoc("SELECT g.id, gm.* FROM {group} g
JOIN {group_member} gm ON (gm.group = g.id AND gm.member = ?)
WHERE g.deleted = 0
ORDER BY g.id", array($userid))) {
" . $wheresql . "
ORDER BY g.id", $where)) {
foreach ($groups as $k => $group) {
try {
group_remove_user($k, $userid, true);
......
......@@ -166,7 +166,7 @@
{{if $righttitle}}<label class="h3" for="{{$name}}_members">{{$righttitle}}</label>{{/if}}
<select class="form-control" size="10" multiple="true" id="{{$name}}_members" style="width: 100%;"><option></option>
{{foreach from=$options key=id item=user}}
<option value="{{$id}}">{{$user}}</option>
<option {{if $user->disabled}}disabled="disabled"{{/if}} value="{{$id}}">{{$user->displayname}}</option>
{{/foreach}}
</select>
</td>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment