Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion appinfo/app.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@
$config,
$urlGenerator,
\OC::$server->getSession(),
\OC::$server->getDatabaseConnection()
\OC::$server->getDatabaseConnection(),
\OC::$server->getUserManager()
);
$userBackend->registerBackends(\OC::$server->getUserManager()->getBackends());
OC_User::useBackend($userBackend);
Expand Down
26 changes: 26 additions & 0 deletions js/admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ $(function() {
$('#user-saml-settings .button').addClass('hidden');
}

if($('#user-saml-general-require_provisioned_account').val() === '0') {
$('#user-saml-attribute-mapping').toggleClass('hidden');
}

$('#user-saml-choose-saml').click(function(e) {
e.preventDefault();
OCA.User_SAML.Admin.chooseSaml();
Expand Down Expand Up @@ -110,6 +114,9 @@ $(function() {
} else {
$(this).val("0");
}
if(key === 'require_provisioned_account') {
$('#user-saml-attribute-mapping').toggleClass('hidden');
}
OCA.User_SAML.Admin.setSamlConfigValue('general', key, $(this).val());
});
});
Expand All @@ -127,6 +134,18 @@ $(function() {
});
});

$('#user-saml-attribute-mapping input[type="text"], #user-saml-attribute-mapping textarea').change(function(e) {
var el = $(this);
$.when(el.focusout()).then(function() {
var key = $(this).attr('name');
OCA.User_SAML.Admin.setSamlConfigValue('saml-attribute-mapping', key, $(this).val());
});
if (e.keyCode === 13) {
var key = $(this).attr('name');
OCA.User_SAML.Admin.setSamlConfigValue('saml-attribute-mapping', key, $(this).val());
}
});

$('#user-saml').change(function() {
if(type === 'saml') {
// Checks on each request whether the settings make sense or not
Expand Down Expand Up @@ -172,6 +191,13 @@ $(function() {
text = 'Show Service Provider settings ...';
}
break;
case 'user-saml-attribute-mapping':
if (nextSibling.hasClass('hidden')) {
text = 'Hide attribute mapping settings ...';
} else {
text = 'Show attribute mapping settings ...';
}
break;
}
el.html(t('user_saml', text));

Expand Down
6 changes: 5 additions & 1 deletion lib/Controller/SAMLController.php
Original file line number Diff line number Diff line change
Expand Up @@ -99,15 +99,19 @@ private function autoprovisionIfPossible(array $auth) {
}

$userExists = $this->userManager->userExists($uid);
$autoProvisioningAllowed = $this->userBackend->autoprovisionAllowed();
if($userExists === true) {
if($autoProvisioningAllowed) {
$this->userBackend->updateAttributes($uid, $auth);
}
return;
}

$autoProvisioningAllowed = $this->userBackend->autoprovisionAllowed();
if(!$userExists && !$autoProvisioningAllowed) {
throw new NoUserFoundException();
} elseif(!$userExists && $autoProvisioningAllowed) {
$this->userBackend->createUserIfNotExists($uid);
$this->userBackend->updateAttributes($uid, $auth);
return;
}
}
Expand Down
13 changes: 13 additions & 0 deletions lib/Settings/Admin.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,18 @@ public function getForm() {
'type' => 'checkbox',
],
];
$attributeMappingSettings = [
'displayName_mapping' => [
'text' => $this->l10n->t('Attribute to map the displayname to.'),
'type' => 'line',
'required' => true,
],
'email_mapping' => [
'text' => $this->l10n->t('Attribute to map the email address to.'),
'type' => 'line',
'required' => true,
],
];

$type = $this->config->getAppValue('user_saml', 'type');
if($type === 'saml') {
Expand All @@ -102,6 +114,7 @@ public function getForm() {
'security-required' => $securityRequiredFields,
'security-general' => $securityGeneral,
'general' => $generalSettings,
'attributeMappings' => $attributeMappingSettings,
'type' => $type,
];

Expand Down
112 changes: 108 additions & 4 deletions lib/userbackend.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
use OCP\Authentication\IApacheBackend;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
use OCP\IUserManager;
use OCP\UserInterface;
use OCP\IUserBackend;
use OCP\IConfig;
Expand All @@ -39,6 +40,8 @@ class UserBackend implements IApacheBackend, UserInterface, IUserBackend {
private $session;
/** @var IDBConnection */
private $db;
/** @var IUserManager */
private $userManager;
/** @var \OCP\UserInterface[] */
private $backends;

Expand All @@ -47,15 +50,18 @@ class UserBackend implements IApacheBackend, UserInterface, IUserBackend {
* @param IURLGenerator $urlGenerator
* @param ISession $session
* @param IDBConnection $db
* @param IUserManager $userManager
*/
public function __construct(IConfig $config,
IURLGenerator $urlGenerator,
ISession $session,
IDBConnection $db) {
IDBConnection $db,
IUserManager $userManager) {
$this->config = $config;
$this->urlGenerator = $urlGenerator;
$this->session = $session;
$this->db = $db;
$this->userManager = $userManager;
}

/**
Expand Down Expand Up @@ -109,8 +115,14 @@ public function createUserIfNotExists($uid) {
* @since 4.5.0
*/
public function implementsActions($actions) {
return (bool)((\OC_User_Backend::CHECK_PASSWORD)
& $actions);
$availableActions = \OC_User_Backend::CHECK_PASSWORD;
if($this->autoprovisionAllowed()
&& $this->config->getAppValue('user_saml', 'saml-attribute-mapping-displayName_mapping', '') !== '') {

$availableActions |= \OC_User_Backend::GET_DISPLAYNAME;
}

return (bool)($availableActions & $actions);
}

/**
Expand Down Expand Up @@ -207,13 +219,48 @@ public function userExists($uid) {
}
}

public function setDisplayName($uid, $displayName) {
if($backend = $this->getActualUserBackend($uid)) {
return $backend->setDisplayName($uid, $displayName);
}

if ($this->userExistsInDatabase($uid)) {
$qb = $this->db->getQueryBuilder();
$qb->update('user_saml_users')
->set('displayname', $qb->createNamedParameter($displayName))
->where($qb->expr()->eq('uid', $qb->createNamedParameter($uid)))
->execute();
return true;
}

return false;
}

/**
* get display name of the user
* Get display name of the user
*
* @param string $uid user ID of the user
* @return string display name
* @since 4.5.0
*/
public function getDisplayName($uid) {
if($backend = $this->getActualUserBackend($uid)) {
return $backend->getDisplayName($uid);
} else {
if($this->userExistsInDatabase($uid)) {
$qb = $this->db->getQueryBuilder();
$qb->select('displayname')
->from('user_saml_users')
->where($qb->expr()->eq('uid', $qb->createNamedParameter($uid)))
->setMaxResults(1);
$result = $qb->execute();
$users = $result->fetchAll();
if (isset($users[0]['displayname'])) {
return $users[0]['displayname'];
}
}
}

return false;
}

Expand All @@ -233,6 +280,9 @@ public function getDisplayNames($search = '', $limit = null, $offset = null) {
->where(
$qb->expr()->iLike('uid', $qb->createNamedParameter('%' . $this->db->escapeLikeParameter($search) . '%'))
)
->orWhere(
$qb->expr()->iLike('displayname', $qb->createNamedParameter('%' . $this->db->escapeLikeParameter($search) . '%'))
)
->setMaxResults($limit);
if($offset !== null) {
$qb->setFirstResult($offset);
Expand Down Expand Up @@ -360,4 +410,58 @@ public function registerBackends(array $backends) {
$this->backends = $backends;
}

private function getAttributeValue($name, array $attributes) {
$keys = explode(' ', $this->config->getAppValue('user_saml', $name, ''));

if(count($keys) === 1 && $keys[1] === '') {
throw new \InvalidArgumentException('Attribute is not configured');
}

$value = '';
foreach($keys as $key) {
if (isset($attributes[$key])) {
if (is_array($attributes[$key])) {
if($value !== '') {
$value .= ' ';
}
$value .= $attributes[$key][0];
} else {
if($value !== '') {
$value .= ' ';
}
$value .= $attributes[$key];
}
}
}

return $value;
}

public function updateAttributes($uid,
array $attributes) {
$user = $this->userManager->get($uid);
try {
$newEmail = $this->getAttributeValue('saml-attribute-mapping-email_mapping', $attributes);
} catch (\InvalidArgumentException $e) {
$newEmail = null;
}
try {
$newDisplayname = $this->getAttributeValue('saml-attribute-mapping-displayName_mapping', $attributes);
} catch (\InvalidArgumentException $e) {
$newDisplayname = null;
}

if ($user !== null) {
$currentEmail = (string)$user->getEMailAddress();
if ($newEmail !== null
&& $currentEmail !== $newEmail) {
$user->setEMailAddress($newEmail);
}
$currentDisplayname = (string)$this->getDisplayName($uid);
if($newDisplayname !== null
&& $currentDisplayname !== $newDisplayname) {
$this->setDisplayName($uid, $newDisplayname);
}
}
}
}
20 changes: 20 additions & 0 deletions templates/admin.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,26 @@
<p><textarea name="x509cert" placeholder="<?php p($l->t('Public X.509 certificate of the IdP')) ?>"><?php p(\OC::$server->getConfig()->getAppValue('user_saml', 'idp-x509cert', '')) ?></textarea></p>
</div>
</div>

<div id="user-saml-attribute-mapping" class="hidden">
<h3><?php p($l->t('Attribute mapping')) ?></h3>
<p>
<?php print_unescaped($l->t('If you want to optionally map SAML attributes to the user you can configure these here.')) ?>
<span class="toggle"><?php p($l->t('Show attribute mapping settings ...')) ?></span>
</p>

<div class="hidden">
<?php foreach($_['attributeMappings'] as $key => $attribute): ?>
<?php
if($attribute['type'] === 'line'): ?>
<p>
<input name="<?php p($key) ?>" value="<?php p(\OC::$server->getConfig()->getAppValue('user_saml', 'saml-attribute-mapping-'.$key, '')) ?>" type="text" <?php if(isset($attribute['required']) && $attribute['required'] === true): ?>class="required"<?php endif;?> placeholder="<?php p($attribute['text']) ?>"/>
</p>
<?php endif; ?>
<?php endforeach; ?>
</div>
</div>

<div id="user-saml-security">
<h3><?php p($l->t('Security settings')) ?></h3>
<p>
Expand Down
4 changes: 2 additions & 2 deletions tests/integration/features/EnvironmentVariable.feature
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Feature: EnvironmentVariable
And The environment variable "REMOTE_USER" is set to "not-provisioned-user"
When I send a GET request to "http://localhost/index.php/login"
Then I should be redirected to "http://localhost/index.php/apps/files/"
Then I should be logged-in to Nextcloud as user "not-provisioned-user"
Then The user value "id" should be "not-provisioned-user"

Scenario: Authenticating using environment variable with SSO and successful check if user exists on backend
Given A local user with uid "provisioned-user" exists
Expand All @@ -16,7 +16,7 @@ Feature: EnvironmentVariable
And The environment variable "REMOTE_USER" is set to "provisioned-user"
When I send a GET request to "http://localhost/index.php/login"
Then I should be redirected to "http://localhost/index.php/apps/files/"
Then I should be logged-in to Nextcloud as user "provisioned-user"
Then The user value "id" should be "provisioned-user"

Scenario: Authenticating using environment variable with SSO and unsuccessful check if user exists on backend
Given The setting "type" is set to "environment-variable"
Expand Down
Loading