FreeIPA Auth Adaptor for LibreTime

Allow delegating user authentication to FreeIPA rather than having it be checked against the database.
This commit is contained in:
Lucas Bickel 2017-03-18 19:15:20 +01:00
parent a01c7c23ec
commit aa5bc06d74
8 changed files with 371 additions and 2 deletions

View file

@ -0,0 +1,117 @@
<?php
/**
* Auth adaptor for FreeIPA
*/
class LibreTime_Auth_Adaptor_FreeIpa implements Zend_Auth_Adapter_Interface {
/**
* @var string
*/
private $username;
/**
* @var string
*/
private $password;
/**
* @var Application_Model_User
*/
private $user;
/**
* username from form
*
* @return self
*/
function setIdentity($username) {
$this->username = $username;
return $this;
}
/**
* password from form
*
* This is ignored by FreeIPA but needs to get passed for completeness
*
* @return self
*/
function setCredential($password) {
$this->password = $password;
return $this;
}
/**
* Check if apache logged the user and get data from ldap
*
* @return Zend_Auth_Result
*/
function authenticate()
{
if (array_key_exists('EXTERNAL_AUTH_ERROR', $_SERVER)) {
return new Zend_Auth_Result(Zend_Auth_Result::FAILURE, null, array($_SERVER['EXTERNAL_AUTH_ERROR']));
}
if (!array_key_exists('REMOTE_USER', $_SERVER)) {
return new Zend_Auth_Result(Zend_Auth_Result::FAILURE, null);
}
// success, the user is good since the service populated the REMOTE_USER
$remoteUser = $_SERVER['REMOTE_USER'];
$subj = CcSubjsQuery::create()->findOneByDbLogin($remoteUser);
$subjId =null;
if ($subj) {
$subjId = $subj->getDBId();
}
if ($subjId) {
$user = new Application_Model_User($subjId);
} else {
// upsert the user on login for first time users
$user = new Application_Model_User('');
}
// Always zap any local info with new info from ipa
$user->setLogin($remoteUser);
// Use a random password for IPA users, reset on each login... I may change this to get set to the IPA pass but hate that it is being stored as md5 behind the scenes
// gets rescrambled on each succeful login for security purposes
$ipaDummyPass = bin2hex(openssl_random_pseudo_bytes(10));
$user->setPassword($ipaDummyPass);
// grab user info from LDAP
$userParts = explode('@', $remoteUser);
$userInfo = LibreTime_Model_FreeIpa::GetUserInfo($userParts[0]);
$user->setType($userInfo['type']);
$user->setFirstName($userInfo['first_name']);
$user->setLastName($userInfo['last_name']);
$user->setEmail($userInfo['email']);
$user->setCellPhone($userInfo['cell_phone']);
$user->setSkype($userInfo['skype']);
$user->setJabber($userInfo['jabber']);
$user->save();
$this->user = $user;
try {
return new Zend_Auth_Result(Zend_Auth_Result::SUCCESS, $user);
} catch (Exception $e) {
// exception occured
return new Zend_Auth_Result(Zend_Auth_Result::FAILURE, null);
}
}
/**
* return dummy object for internal auth handling
*
* we need to build a dummpy object since the auth layer knows nothing about the db
*
* @return stdClass
*/
public function getResultRowObject() {
$o = new \stdClass;
$o->id = $this->user->getId();
$o->username = $this->user->getLogin();
$o->password = $this->user->getPassword();
$o->real_name = implode(' ', array($this->user->getFirstName(), $this->user->getLastName()));
$o->type = $this->user->getType();
$o->login = $this->user->getLogin();
return $o;
}
}

View file

@ -76,6 +76,10 @@ class Application_Common_GoogleAnalytics
/** Return true if the user used to be on a trial plan and was just converted to a paid plan. */
public static function didPaidConversionOccur($request)
{
if (LIBRETIME_ENABLE_GOOGLE_ANALYTICS !== true) {
return false;
}
$userInfo = Zend_Auth::getInstance()->getStorage()->read();
if ($userInfo) {
$user = new Application_Model_User($userInfo->id);

View file

@ -37,6 +37,11 @@ class Config {
$CC_CONFIG['dev_env'] = 'production';
}
$CC_CONFIG['auth'] = 'local';
if (isset($values['general']['auth'])) {
$CC_CONFIG['auth'] = $values['general']['auth'];
}
//Backported static_base_dir default value into saas for now.
if (array_key_exists('static_base_dir', $values['general'])) {
$CC_CONFIG['staticBaseDir'] = $values['general']['static_base_dir'];
@ -64,7 +69,7 @@ class Config {
$CC_CONFIG['cache_ahead_hours'] = $values['general']['cache_ahead_hours'];
// Database config
// Database config
$CC_CONFIG['dsn']['username'] = $values['database']['dbuser'];
$CC_CONFIG['dsn']['password'] = $values['database']['dbpass'];
$CC_CONFIG['dsn']['hostspec'] = $values['database']['host'];
@ -92,6 +97,19 @@ class Config {
$CC_CONFIG['facebook-app-api-key'] = $globalAirtimeConfigValues['facebook']['facebook_app_api_key'];
}
// ldap config
$CC_CONFIG['ldap_hostname'] = $values['ldap']['hostname'];
$CC_CONFIG['ldap_binddn'] = $values['ldap']['binddn'];
$CC_CONFIG['ldap_password'] = $values['ldap']['password'];
$CC_CONFIG['ldap_account_domain'] = $values['ldap']['account_domain'];
$CC_CONFIG['ldap_basedn'] = $values['ldap']['basedn'];
$CC_CONFIG['ldap_groupmap_guest'] = $values['ldap']['groupmap_guest'];
$CC_CONFIG['ldap_groupmap_host'] = $values['ldap']['groupmap_host'];
$CC_CONFIG['ldap_groupmap_program_manager'] = $values['ldap']['groupmap_program_manager'];
$CC_CONFIG['ldap_groupmap_admin'] = $values['ldap']['groupmap_admin'];
$CC_CONFIG['ldap_groupmap_superadmin'] = $values['ldap']['groupmap_superadmin'];
$CC_CONFIG['ldap_filter_field'] = $values['ldap']['filter_field'];
if(isset($values['demo']['demo'])){
$CC_CONFIG['demo'] = $values['demo']['demo'];
}

View file

@ -76,7 +76,10 @@ class Application_Model_Auth
public static function getAuthAdapter()
{
$CC_CONFIG = Config::getConfig();
if ($CC_CONFIG['auth'] !== 'local') {
return self::getCustomAuthAdapter($CC_CONFIG['auth']);
}
// Database config
$db = Zend_Db::factory('PDO_' . $CC_CONFIG['dsn']['phptype'], array(
'host' => $CC_CONFIG['dsn']['hostspec'],
@ -95,6 +98,15 @@ class Application_Model_Auth
return $authAdapter;
}
/**
* Gets an alternative Adapter that does not need to auth agains a databse table
*
* @return object
*/
public static function getCustomAuthAdapter($adaptor) {
return new $adaptor();
}
/**
* Get random string
*

View file

@ -0,0 +1,74 @@
<?php
class LibreTime_Model_FreeIpa {
/**
* get userinfo in the format needed by the Auth Adaptor
*
* @return array
*/
public static function GetUserInfo($username)
{
$config = Config::getConfig();
$conn = self::_getLdapConnection();
$ldapResults = $conn->search(sprintf('%s=%s', $config['ldap_filter_field'], $username, $config['ldap_basedn']));
if ($ldapResults->count() !== 1) {
throw new Exception('Could not find logged user in LDAP');
}
$ldapUser = $ldapResults->getFirst();
$groupMap = array(
UTYPE_GUEST => $config['ldap_groupmap_guest'],
UTYPE_HOST => $config['ldap_groupmap_host'],
UTYPE_PROGRAM_MANAGER => $config['ldap_groupmap_program_manager'],
UTYPE_ADMIN => $config['ldap_groupmap_admin'],
UTYPE_SUPERADMIN => $config['ldap_groupmap_superadmin'],
);
$type = UTYPE_GUEST;
foreach ($groupMap as $groupType => $group) {
if (in_array($group, $ldapUser['memberof'])) {
$type = $groupType;
}
}
// grab first value for multivalue field
$firstName = $ldapUser['givenname'][0];
$lastName = $ldapUser['sn'][0];
$mail = $ldapUser['mail'][0];
// return full user info for auth adapter
return array(
'type' => $type,
'first_name' => $firstName,
'last_name' => $lastName,
'email' => $mail,
'cell_phone' => '', # empty since I did not find it in ldap
'skype' => '', # empty until we decide on a field
'jabber' => '' # empty until we decide on a field
);
}
/**
* Bind to ldap so we can fetch additional user info
*
* @return Zend_Ldap
*/
private static function _getLdapConnection()
{
$config = Config::getConfig();
$options = array(
'host' => $config['ldap_hostname'],
'username' => $config['ldap_binddn'],
'password' => $config['ldap_password'],
'bindRequiresDn' => true,
'accountDomainName' => $config['ldap_account_domain'],
'baseDn' => $config['ldap_basedn']
);
$conn = new Zend_Ldap($options);
$conn->connect();
return $conn;
}
}