Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • perun/perun-proxyidp/simplesamlphp-module-campusmultiauth
1 result
Show changes
Commits on Source (4)
Showing
with 2027 additions and 91 deletions
# [1.5.0](https://gitlab.ics.muni.cz/perun-proxy-aai/simplesamlphp/simplesamlphp-module-campusmultiauth/compare/v1.4.2...v1.5.0) (2022-11-07)
### Features
* security images ([07dbcd3](https://gitlab.ics.muni.cz/perun-proxy-aai/simplesamlphp/simplesamlphp-module-campusmultiauth/commit/07dbcd33dfc964f43d7f270fe291ae01fe2e1bee))
## [1.4.2](https://gitlab.ics.muni.cz/perun-proxy-aai/simplesamlphp/simplesamlphp-module-campusmultiauth/compare/v1.4.1...v1.4.2) (2022-10-24)
......
......@@ -31,7 +31,7 @@ To achieve this, you need to define and configure an authentication source in yo
]
],
Let's look at the configuration options:
The following configuration options are available:
`campusmultiauth:campusidp` defines which module and authentication source to use. This is the only mandatory option.
......@@ -43,7 +43,7 @@ Of course, both authsources must be defined in authsources.php file. When the co
## Login page configuration
The second part of the configuration is setting up the login page itself. While doing that, it's highly recommended to follow [our suggestions (Czech only)](https://gitlab.ics.muni.cz/perun-proxy-aai/simplesamlphp/simplesamlphp-module-campusmultiauth/-/wikis/Konfigura%C4%8Dn%C3%AD-doporu%C4%8Den%C3%AD). To configure the login page, you need to create a new configuration file `module_campusmultiauth.php`. In this module, there is an example configuration available at `config-templates/module_campusmultiauth.php`. In configuration file, there are following options available:
The second part of the configuration is setting up the login page itself. While doing that, it is highly recommended to follow [our suggestions (Czech only)](https://gitlab.ics.muni.cz/perun-proxy-aai/simplesamlphp/simplesamlphp-module-campusmultiauth/-/wikis/Konfigura%C4%8Dn%C3%AD-doporu%C4%8Den%C3%AD). To configure the login page, you need to create a new configuration file `module_campusmultiauth.php`. In this module, there is an example configuration available at `config-templates/module_campusmultiauth.php`. In configuration file, there are following options available:
`css_framework` - if set to `muni_jvs`, the login page displays in MUNI framework. Otherwise, Bootstrap 5 is used.
......@@ -69,11 +69,11 @@ Footer defines the bottom of the login page. If it is not set, the footer is emp
### Components
The main part of the login page. The `components` option is designed as a list, where each element represents one component. A component is a map with several possible options. The most important option is `name`. It defines the component's type. There are three possible values for `name`: `local_login`, `searchbox` and `individual_identities`. Let's take a look at each component's type separately.
The main part of the login page. The `components` option is designed as a list, where each element represents one component. A component is a map with several possible options. The most important option is `name`. It defines the component's type. There are three possible values for `name`: `local_login`, `searchbox` and `individual_identities`.
#### local_login
This component represents a form with username and password. It can be used only once. It's possible to show / hide the `remember_me` checkbox by configuring the `session.rememberme.enable` option in the `config.php` file. In the module configuration, there are following options:
This component represents a form with username and password. It can be used only once. For the Remember me functionality, see below. In the module configuration, there are following options:
`username_label` - this is displayed as a label above input for the username. If you want to add localization, you can write the value as a map with language codes as keys and localized strings as values. If current language is not found in keys, the **_first one_** is used instead. If not set at all, it displays a default value.
......@@ -97,7 +97,7 @@ Thanks to the searchbox you can search between all included identity providers.
`placeholder` - text displayed as a placeholder in the searchbox. If you want to add localization, you can write the value as a map with language codes as keys and localized strings as values. If current language is not found in keys, the **_first one_** is used instead. If not set at all, it displays a default value.
`filter` - if you want to display just part of identity providers available in the metadata, you can use this option. If not set, all identity providers from the metadata are included. Otherwise, identity providers to display are chosen based on the [aarc_discovery_hint](https://docs.google.com/document/d/1rHKGzPsjkbqKHxsPnCb0itRLXLtqm-A8CZ5fzzklaxc/edit) logic. However, there are some differences. The content of this option is already decoded (which means it's in the PHP format, not the JSON). Also, you can use the `entityid` claim (instead of `entity_category` / `assurance_certification` / `registration_authority`) to include or exclude specific identity providers. You can find a sample use of the `entityid` claim in [module_campusmultiauth.php](https://gitlab.ics.muni.cz/perun-proxy-aai/simplesamlphp/simplesamlphp-module-campusmultiauth/-/blob/main/config-templates/module_campusmultiauth.php) config template.
`filter` - if you want to display just part of identity providers available in the metadata, you can use this option. If not set, all identity providers from the metadata are included. Otherwise, identity providers to display are chosen based on the [aarc_discovery_hint](https://docs.google.com/document/d/1rHKGzPsjkbqKHxsPnCb0itRLXLtqm-A8CZ5fzzklaxc/edit) logic. However, there are some differences. The content of this option is already decoded (which means it is in the PHP format, not the JSON). Also, you can use the `entityid` claim (instead of `entity_category` / `assurance_certification` / `registration_authority`) to include or exclude specific identity providers. You can find a sample use of the `entityid` claim in [module_campusmultiauth.php](https://gitlab.ics.muni.cz/perun-proxy-aai/simplesamlphp/simplesamlphp-module-campusmultiauth/-/blob/main/config-templates/module_campusmultiauth.php) config template.
`priority` - can be set to `primary`, default value is `secondary`. It should be primary if you want users to use this component if they are able to.
......@@ -131,6 +131,64 @@ Each identity is a map with the following possible options:
`background_color` - background around the logo. Defined as a [CSS color value](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value).
### Remember me and security images
You can add a `remember_me` section to your configuration file to add some convenience and anti-phishing features to your `local_login` component.
#### Remember me
To enable the `remember_me` checkbox and optionally set it as checked by default, configure `session.rememberme.enable` and `session.rememberme.checked` options in the `config.php` file. If you want to make session longer if the checkbox is checked, use the [ExtendIdPSession](https://github.com/simplesamlphp/simplesamlphp/blob/v1.19.6/modules/core/lib/Auth/Process/ExtendIdPSession.php) auth proc filter. It is highly recommended to also set a [session checking function](https://simplesamlphp.org/docs/1.19/simplesamlphp-advancedfeatures.html#session-checking-function).
You can store info about the user in the cookie, including a counter of user's visits of the login page, which is compared to the value stored in the database. This can help to detect some suspicious behaviour. To enable this feature, you have to enable the `remember_me` checkbox mentioned above and add the `RememberMe` auth proc filter. Then set the following options in the `remember_me` section of the configuration file:
`nameAttr` - a name of the attribute with the user's name. Has to be present in the `$request['attributes']` in the Authproc filter. The default value is `displayName`.
`store` - a database configuration, used as an argument for the `SimpleSAML\Database::getInstance()` method.
`tokens_table` - a name of the database table where user tokens with counters are stored. The default value is `cookie_counter`.
`signature_key` - a key used for signing JWTs.
`encryption_key` - a key used for encrypting JWTs.
`signature_algorithm` - a signature algorithm, default `HS512`.
`encryption_algorithm` - an encryption algorithm, default `A256GCM`.
`keywrap_algorithm` - a keywrap algorithm, default `A256GCMKW`.
`uid_attribute` - a user's identifier attribute, default `uid`.
`cipherClass` - an implementation of `SimpleSAML\Module\campusmultiauth\Security\Cipher`, default `SimpleSAML\Module\campusmultiauth\Security\JWTCipher`.
`uidName` - value of this option is displayed before the user's uid attribute value, default value is empty string (which will display nothing).
`cookieName` - a cookie name where the info about user is stored, default `campus_userinfo`.
`dontCookieName` - if user decides not to remember login on current device, this decision will also be stored into a cookie. Value of this option is used as the name of this cookie. The default value is `campus_dont_remember`.
#### Security images
In addition to the remember me function, you can turn on security images. Image specific to each user will be shown on the login page if set, which proves it is not a phishing site. To configure this feature, you need to add `security_images` to the `remember_me` section and set:
`showFreshImage` - if set to true, the security image is fetched everytime user access the login page. Otherwise, it is stored in the cookie. Default `false`.
`pictureDir` - if set, the security image is stored in this directory instead of the cookie. The cookie than contains only a link to the picture. Also, if this option is enabled, `securityImageSalt` and `pictureBaseURL` are mandatory. Default `null`.
`securityImageSalt` - a salt which is used in the filename of the picture if the `pictureDir` is on.
`pictureBaseURL` - base URL to the pictures if the `pictureDir` is on.
`storageClass` - an implementation of `SimpleSAML\Module\campusmultiauth\Data\Storage`, default `SimpleSAML\Module\campusmultiauth\Data\DatabaseStorage`.
`pictures_table` - name of the table with security images, default `security_image`.
`pictureStorage` - if some other storage than `SimpleSAML\Module\campusmultiauth\Data\DatabaseStorage` is used (e.g. `SimpleSAML\Module\campusmultiauth\Data\PerunStorage`), this is the place for the configuration of the storage.
`security.cookie.path` - cookie path.
`security.cookie.samesite` - cookie SameSite.
## Hinting
To help the user choose the right institution to log in, this module supports following standards:
......
......@@ -12,7 +12,16 @@
"simplesamlphp/simplesamlphp": "^1.19",
"league/commonmark": "^2.0",
"ext-intl": "*",
"ext-simplexml": "*"
"ext-curl": "*",
"ext-simplexml": "*",
"ext-json": "*",
"web-token/jwt-core": "^2.2",
"web-token/jwt-signature-algorithm-hmac": "^2.2",
"web-token/jwt-encryption-algorithm-aesgcmkw": "^2.2",
"web-token/jwt-encryption-algorithm-aesgcm": "^2.2",
"web-token/jwt-nested-token": "^2.2",
"web-token/jwt-checker": "^2.2",
"donatj/phpuseragentparser": "^1.0"
},
"config": {
"platform": {
......
This diff is collapsed.
......@@ -121,4 +121,51 @@ $config = [
// 'identifier.attr.name' => 'OIDCClientID',
// 'url.attr.name' => 'rploginurl',
],
'remember_me' => [
'security_images' => [
// 'pictureDir' => '',
// 'showFreshImage' => false,
// 'securityImageSalt' => '',
// 'pictureBaseURL' => '',
// 'pictures_table' => '',
// 'pictureStorage' => [
// 'ldap.hostname' => '',
// 'ldap.port' => 0,
// 'ldap.enable_tls' => false,
// 'ldap.debug' => false,
// 'ldap.referrals' => false,
// 'ldap.timeout' => 0,
// 'ldap.username' => '',
// 'ldap.password' => '',
// 'ldap.basedn' => '',
// 'search.filter' => '',
// 'attribute' => '',
// ],
],
// 'uidName' => '',
// 'cookieName' => '',
// 'nameAttr' => '',
// 'cipherClass' => '',
// 'storageClass' => '',
// 'security.cookie.path' => '',
// 'security.cookie.samesite' => '',
'store' => [
'database.dsn' => 'dsn',
'database.username' => 'username',
'database.password' => 'password',
],
'tokens_table' => 'tokens_table',
'signature_key' => [
'kty' => 'oct',
'k' => 'tCUdnHNp8xH/egDmzwxEkI1BzknCJmAt1khoQsfm9+FNSwIwq9ILN6GYBWjEAoykttrXx5aI/lRdyyGjheRj/g==',
],
'encryption_key' => [
'kty' => 'oct',
'k' => 'BdateloTM7i01lo9L0bfctTJ/2B9E2VCfrTqdhqxilg=',
],
// 'uid_attribute' => '',
// 'signature_algorithm' => '',
// 'encryption_algorithm' => '',
// 'keywrap_algorithm' => '',
],
];
<?php
namespace SimpleSAML\Module\campusmultiauth\Auth\Process;
use SimpleSAML\Auth\ProcessingFilter;
use SimpleSAML\Configuration;
use SimpleSAML\Logger;
use SimpleSAML\Module\campusmultiauth\Constants;
use SimpleSAML\Module\campusmultiauth\Fingerprinting;
use SimpleSAML\Module\campusmultiauth\Utils;
use SimpleSAML\Module\core\Stats\Output\Log;
use SimpleSAML\Utils\HTTP;
/**
* Inspired by the Facebook class.
*
* @see https://github.com/simplesamlphp/simplesamlphp/blob/simplesamlphp-1.14/modules/authfacebook/lib/Facebook.php
*/
class RememberMe extends ProcessingFilter
{
/**
* Name of the GET parameter to clear username.
*/
public const CLEAR_USERNAME_PARAM = 'init';
/**
* Value of the GET parameter to clear username.
*/
public const CLEAR_USERNAME_VALUE = 'true';
/**
* The lifetime of the cookie.
*/
private const COOKIE_LIFETIME = 31536000;
/**
* The secure parameter of the cookie.
*/
private const COOKIE_SECURE = true;
/**
* The http_only parameter of the cookie.
*/
private const COOKIE_HTTPONLY = true;
/**
* Default cookie path.
*/
private const DEFAULT_COOKIE_PATH = '/';
/**
* \SimpleSAML\Module\campusmultiauth\Security\Cipher implementation.
*/
private $cipher;
/**
* Whether to store security image into the cookie or leave it to the login screen to fetch the fresh image.
*/
private $showFreshImage;
/**
* \SimpleSAML\Module\campusmultiauth\Data\Storage implementation.
*/
private $storage;
/**
* Cookie path.
*/
private $cookiePath;
/**
* Cookie SameSite.
*/
private $cookieSameSite;
/**
* Name of the cookie.
*/
private $cookieName;
/**
* Name of the don't remember me cookie.
*/
private $dontCookieName;
/**
* Name of the name attribute.
*/
private $nameAttr;
/**
* The constructor.
*
* @override
*
* @param mixed|null $config
* @param mixed|null $reserved
*/
public function __construct($config = null, $reserved = null)
{
if ($config) {
parent::__construct($config, $reserved);
}
$configuration = Configuration::getOptionalConfig('module_campusmultiauth.php')
->getConfigItem('remember_me', []);
$imagesConfiguration = $configuration->getConfigItem('security_images', []);
$this->showFreshImage = $imagesConfiguration->getBoolean('showFreshImage', false);
$this->cookiePath = $configuration->getString('security.cookie.path', self::DEFAULT_COOKIE_PATH);
$this->cookieSameSite = $configuration->getString('security.cookie.samesite', null);
$this->cookieName = $configuration->getString('cookieName', 'campus_userinfo');
$this->dontCookieName = $configuration->getString('dontCookieName', 'campus_dont_remember');
$this->nameAttr = $configuration->getString('nameAttr', 'displayName');
}
/**
* Get user info from a cookie.
*/
public function getUserInfo(bool $updateCounter = true)
{
// cookie is present
if (!isset($_COOKIE[$this->cookieName])) {
return false;
}
// cookie is valid
try {
$data = json_decode($this->getCipher()->decrypt($_COOKIE[$this->cookieName]), true);
} catch (\Exception $e) {
$this->deleteCookie();
return false;
}
// browser match
if ($data['browser'] !== $this->getBrowserFingerprint()) {
Logger::warning(sprintf('campusmultiauth: Cookie browser mismatch with id %d', $data['id']));
$this->deleteCookie();
return false;
}
// counter match
$storage = $this->getStorage();
if ($storage->getCookieCounter($data['username'], $data['id']) !== $data['counter']) {
// replayed cookie
Logger::warning(
sprintf('campusmultiauth: Replayed cookie with id %d and counter %d', $data['id'], $data['counter'])
);
$this->deleteCookie();
return false;
}
if ($updateCounter) {
// increment counter
$storage->increaseCookieCounter($data['username'], $data['id']);
++$data['counter'];
// update cookie
$this->setCookie($data);
}
return $data;
}
/**
* Save user info in a cookie.
*/
public function setUserInfo(string $username, string $name)
{
$browser = $this->getBrowserFingerprint();
$userInfo = $this->getUserInfo(false);
$id = null;
$counter = 0;
$storage = $this->getStorage();
if ($userInfo !== false && $userInfo['username'] === $username && $userInfo['browser'] === $browser) {
$id = $userInfo['id'];
$counter = $userInfo['counter'];
}
$id = $storage->increaseCookieCounter($username, $id);
if ($id === null) {
Logger::error('Could not insert cookie counter into database.');
$this->deleteCookie();
return;
}
++$counter;
$payload = [
'username' => $username,
'name' => $name,
'browser' => $browser,
'id' => $id,
'counter' => $counter,
];
Logger::debug('Setting user info cookie: ' . print_r($payload, true));
if (!$this->showFreshImage) {
$payload['security_image'] = Utils::getSecurityImageOfUser($username);
}
$this->setCookie($payload);
}
/**
* Delete the cookie.
*/
public function deleteCookie()
{
$this->deleteACookie($this->cookieName);
}
public function getDontCookieName()
{
return $this->dontCookieName;
}
/**
* The constructor.
*
* @override
*
* @param mixed $request
*/
public function process(&$request)
{
$uid_attribute = Configuration::getOptionalConfig('module_campusmultiauth.php')
->getConfigItem('remember_me', [])
->getString('uid_attribute', 'uid');
if (
!empty($request['RememberMe'])
&& !empty($request['Attributes'][$uid_attribute])
&& !empty($request['Attributes'][$this->nameAttr][0])
) {
$uid = $request['Attributes'][$uid_attribute][0];
$name = $request['Attributes'][$this->nameAttr][0];
$this->setUserInfo($uid, $name);
$this->deleteACookie($this->dontCookieName);
}
if (!empty($request['DontRememberMe'])) {
$this->setACookie($this->dontCookieName, 'Yes');
$this->deleteCookie();
}
}
/**
* Get hyperlink for the "this is not my username" button.
*
* @param mixed $authState
*/
public static function getOtherUsernameLink($authState)
{
$link = HTTP::getSelfURL();
$link = HTTP::addURLParameters($link, [
self::CLEAR_USERNAME_PARAM => self::CLEAR_USERNAME_VALUE,
]);
return HTTP::addURLParameters($link, [
'AuthState' => $authState,
]);
}
/**
* Get info about the browser, which should not change too often.
*/
protected function getBrowserFingerprint()
{
return Fingerprinting::getBrowserFingerprint();
}
private function getStorage()
{
if (!$this->storage) {
$this->storage = Utils::getInterfaceInstance(
'SimpleSAML\\Module\\campusmultiauth\\Data\\Storage',
'storageClass',
'SimpleSAML\\Module\\campusmultiauth\\Data\\DatabaseStorage'
);
}
return $this->storage;
}
private function setCookie(array $data)
{
$cookie_value = $this->getCipher()->encrypt(json_encode($data));
$this->setACookie($this->cookieName, $cookie_value);
if ($this->cookiePath !== '/') {
HTTP::setCookie($this->cookieName, null, [
'secure' => self::COOKIE_SECURE,
'httponly' => self::COOKIE_HTTPONLY,
'path' => '/',
'samesite' => $this->cookieSameSite,
], false);
}
if ($this->cookiePath !== '/simplesaml/module.php/core') {
HTTP::setCookie($this->cookieName, null, [
'secure' => self::COOKIE_SECURE,
'httponly' => self::COOKIE_HTTPONLY,
'path' => '/simplesaml/module.php/core',
'samesite' => $this->cookieSameSite,
], false);
}
}
private function setACookie(string $name, string $value)
{
$_COOKIE[$name] = $value;
HTTP::setCookie($name, $value, [
'lifetime' => self::COOKIE_LIFETIME,
'secure' => self::COOKIE_SECURE,
'httponly' => self::COOKIE_HTTPONLY,
'path' => $this->cookiePath,
'samesite' => $this->cookieSameSite,
], false);
}
/**
* Delete a cookie.
*/
private function deleteACookie(string $name)
{
unset($_COOKIE[$name]);
HTTP::setCookie($name, null, [
'secure' => self::COOKIE_SECURE,
'httponly' => self::COOKIE_HTTPONLY,
'path' => $this->cookiePath,
'samesite' => $this->cookieSameSite,
], false);
if ($this->cookiePath !== '/') {
HTTP::setCookie($name, null, [
'secure' => self::COOKIE_SECURE,
'httponly' => self::COOKIE_HTTPONLY,
'path' => '/',
'samesite' => $this->cookieSameSite,
], false);
}
if ($this->cookiePath !== '/simplesaml/module.php/core') {
HTTP::setCookie($name, null, [
'secure' => self::COOKIE_SECURE,
'httponly' => self::COOKIE_HTTPONLY,
'path' => '/simplesaml/module.php/core',
'samesite' => $this->cookieSameSite,
], false);
}
}
private function getCipher()
{
if (empty($this->cipher)) {
$this->cipher = Utils::getInterfaceInstance(
'SimpleSAML\\Module\\campusmultiauth\\Security\\Cipher',
'cipherClass',
'SimpleSAML\\Module\\campusmultiauth\\Security\\JWTCipher'
);
}
return $this->cipher;
}
}
......@@ -38,10 +38,6 @@ class Campusidp extends Source
public const COOKIE_PREFIX = 'campusidp_';
public const COOKIE_USERNAME = 'username';
public const COOKIE_PASSWORD = 'password';
public const IDP_HINT_BUTTONS_LIMIT = 5;
// idp hinting
......
<?php
declare(strict_types=1);
namespace SimpleSAML\Module\campusmultiauth\Data;
use SimpleSAML\Configuration;
use SimpleSAML\Database;
/**
* Implementation of Storage using Database.
*/
class DatabaseStorage implements Storage
{
/**
* Name of the column with uid.
*/
private const UID_COL = 'userid';
/**
* DB table name for pictures.
*/
private $pictures_table;
/**
* DB table name for tokens.
*/
private $tokens_table;
/**
* Configuration.
*/
private $config;
/**
* Database instance.
*/
private $db;
/**
* @override
*/
public function __construct()
{
$this->config = Configuration::getOptionalConfig('module_campusmultiauth.php')
->getConfigItem('remember_me', []);
$imagesConfiguration = $this->config->getConfigItem('security_images', []);
$this->db = Database::getInstance($this->config->getConfigItem('store', []));
$this->pictures_table = $this->db->applyPrefix(
$imagesConfiguration->getString('pictures_table', 'security_image')
);
$this->tokens_table = $this->db->applyPrefix(
$this->config->getString('tokens_table', 'cookie_counter')
);
}
/**
* @override
*/
public function getSecurityImageOfUser(string $uid): ?string
{
$query = 'SELECT picture FROM ' . $this->pictures_table
. ' WHERE ' . self::UID_COL . '=:userid';
$statement = $this->db->read($query, [
'userid' => $uid,
]);
$picture = $statement->fetchColumn();
if ($picture === false) {
return null;
}
return $picture;
}
/**
* @override
*/
public function getCookieCounter(string $uid, int $id): ?int
{
$query = 'SELECT counter FROM ' . $this->tokens_table
. ' WHERE ' . self::UID_COL . ' = :userid AND id = :id LIMIT 1';
$params = [
'userid' => $uid,
'id' => $id,
];
$statement = $this->db->read($query, $params);
$counter = $statement->fetchColumn();
if ($counter === false) {
return null;
}
return (int) $counter;
}
/**
* @override
*/
public function increaseCookieCounter(string $uid, ?int $id = null): ?int
{
$success = true;
if ($id === null) {
$id = $this->insert($uid);
} else {
$success = $this->update($uid, $id);
}
if ($id === null || !$success) {
return null;
}
return $id;
}
private function insert(string $uid): ?int
{
$query = 'INSERT INTO ' . $this->tokens_table . ' (' . self::UID_COL . ', id) VALUES (:userid, :id)';
$i = 0;
$params = [
'userid' => $uid,
];
do {
$new_id = random_int(1, PHP_INT_MAX);
$params['id'] = $new_id;
$success = $this->db->write($query, $params);
} while (!$success && $i++ < 3);
return $success ? $new_id : null;
}
private function update(string $uid, int $id): bool
{
$params = [
'userid' => $uid,
'id' => $id,
];
$query = 'UPDATE ' . $this->tokens_table . ' SET counter=counter+1'
. ' WHERE ' . self::UID_COL . '=:userid AND id=:id';
return (bool) $this->db->write($query, $params);
}
}
<?php
declare(strict_types=1);
namespace SimpleSAML\Module\campusmultiauth\Data;
use SimpleSAML\Configuration;
use SimpleSAML\Logger;
use SimpleSAML\Module\ldap\Auth\Ldap;
use SimpleSAML\Module\campusmultiauth\Constants;
/**
* Implementation of Storage using Perun LDAP and Database.
*/
class PerunStorage extends DatabaseStorage
{
/**
* Configuration.
*/
private $config;
/**
* LDAP instance.
*/
private $ldap;
/**
* @override
*/
public function __construct()
{
parent::__construct();
$this->config = Configuration::getOptionalConfig('module_campusmultiauth.php')
->getConfigItem('remember_me', [])
->getConfigItem('security_images', [])
->getConfigItem('pictureStorage', []);
$hostname = $this->config->getString('ldap.hostname');
$port = $this->config->getInteger('ldap.port', 389);
$enable_tls = $this->config->getBoolean('ldap.enable_tls', false);
$debug = $this->config->getBoolean('ldap.debug', false);
$referrals = $this->config->getBoolean('ldap.referrals', true);
$timeout = $this->config->getInteger('ldap.timeout', 0);
$username = $this->config->getString('ldap.username', null);
$password = $this->config->getString('ldap.password', null);
try {
$this->ldap = new Ldap($hostname, $enable_tls, $debug, $timeout, $port, $referrals);
} catch (\Exception $e) {
// Added this warning in case $this->getLdap() fails
Logger::warning('PerunStorage: LDAP exception ' . $e);
return;
}
$this->ldap->bind($username, $password);
}
/**
* @override
*/
public function getSecurityImageOfUser(string $uid): ?string
{
$base = $this->config->getString('ldap.basedn');
$filter = $this->config->getString('search.filter');
$filter = str_replace('%uid%', $uid, $filter);
$attribute = $this->config->getString('attribute');
try {
$entries = $this->ldap->searchformultiple([$base], $filter, [$attribute], [], true, false);
} catch (\Exception $e) {
$entries = [];
}
if (count($entries) < 1 || empty($entries[0][$attribute])) {
return null;
}
return $entries[0][$attribute][0];
}
}
<?php
declare(strict_types=1);
namespace SimpleSAML\Module\campusmultiauth\Data;
interface Storage
{
public function __construct();
/**
* Null if user has none, URL otherwise.
*/
public function getSecurityImageOfUser(string $uid): ?string;
/**
* False if not found (should not happen), counter otherwise.
*/
public function getCookieCounter(string $uid, int $id): ?int;
/**
* Increment a counter for a user. Returns the cookie id.
*/
public function increaseCookieCounter(string $uid, ?int $id): ?int;
}
<?php
declare(strict_types=1);
namespace SimpleSAML\Module\campusmultiauth;
abstract class Fingerprint
{
abstract public function getValue();
}
<?php
declare(strict_types=1);
namespace SimpleSAML\Module\campusmultiauth\Fingerprint;
use donatj\UserAgent\UserAgentParser;
abstract class Browser extends \SimpleSAML\Module\campusmultiauth\Fingerprint
{
public function getValue()
{
$ua = self::getBrowserInfo();
return $this->getProperty($ua);
}
abstract protected function getProperty($ua);
private static function getBrowserInfo()
{
$parser = new UserAgentParser();
return $parser->parse();
}
}
<?php
declare(strict_types=1);
namespace SimpleSAML\Module\campusmultiauth\Fingerprint\Browser;
class Name extends \SimpleSAML\Module\campusmultiauth\Fingerprint\Browser
{
protected function getProperty($ua)
{
return $ua->browser();
}
}
<?php
declare(strict_types=1);
namespace SimpleSAML\Module\campusmultiauth\Fingerprint\Browser;
class Platform extends \SimpleSAML\Module\campusmultiauth\Fingerprint\Browser
{
protected function getProperty($ua)
{
return $ua->platform();
}
}
<?php
declare(strict_types=1);
namespace SimpleSAML\Module\campusmultiauth\Fingerprint;
abstract class Header extends \SimpleSAML\Module\campusmultiauth\Fingerprint
{
public function getValue()
{
return isset($_SERVER[$this->getHeaderName()]) ? $_SERVER[$this->getHeaderName()] : false;
}
/**
* @returns string
*/
abstract protected function getHeaderName();
}
<?php
declare(strict_types=1);
namespace SimpleSAML\Module\campusmultiauth\Fingerprint\Header;
class Accept extends \SimpleSAML\Module\campusmultiauth\Fingerprint\Header
{
protected function getHeaderName()
{
return 'HTTP_ACCEPT';
}
}
<?php
declare(strict_types=1);
namespace SimpleSAML\Module\campusmultiauth\Fingerprint\Header;
class AcceptCharset extends \SimpleSAML\Module\campusmultiauth\Fingerprint\Header
{
protected function getHeaderName()
{
return 'HTTP_ACCEPT_CHARSET';
}
}
<?php
declare(strict_types=1);
namespace SimpleSAML\Module\campusmultiauth\Fingerprint\Header;
class AcceptEncoding extends \SimpleSAML\Module\campusmultiauth\Fingerprint\Header
{
protected function getHeaderName()
{
return 'HTTP_ACCEPT_ENCODING';
}
}
<?php
declare(strict_types=1);
namespace SimpleSAML\Module\campusmultiauth\Fingerprint\Header;
class AcceptLanguage extends \SimpleSAML\Module\campusmultiauth\Fingerprint\Header
{
protected function getHeaderName()
{
return 'HTTP_ACCEPT_LANGUAGE';
}
}
<?php
declare(strict_types=1);
namespace SimpleSAML\Module\campusmultiauth\Fingerprint\Header;
class Connection extends \SimpleSAML\Module\campusmultiauth\Fingerprint\Header
{
protected function getHeaderName()
{
return 'HTTP_CONNECTION';
}
}