diff options
Diffstat (limited to 'plugins')
-rw-r--r-- | plugins/Authentication/AuthenticationPlugin.php | 2 | ||||
-rw-r--r-- | plugins/Authorization/AuthorizationPlugin.php | 108 | ||||
-rw-r--r-- | plugins/LdapAuthentication/LdapAuthenticationPlugin.php | 13 | ||||
-rw-r--r-- | plugins/LdapAuthorization/LdapAuthorizationPlugin.php | 211 | ||||
-rw-r--r-- | plugins/LdapAuthorization/README | 91 |
5 files changed, 419 insertions, 6 deletions
diff --git a/plugins/Authentication/AuthenticationPlugin.php b/plugins/Authentication/AuthenticationPlugin.php index 1b9084187..cd1de1149 100644 --- a/plugins/Authentication/AuthenticationPlugin.php +++ b/plugins/Authentication/AuthenticationPlugin.php @@ -2,7 +2,7 @@ /** * StatusNet, the distributed open-source microblogging tool * - * Superclass for plugins that do authentication and/or authorization + * Superclass for plugins that do authentication * * PHP version 5 * diff --git a/plugins/Authorization/AuthorizationPlugin.php b/plugins/Authorization/AuthorizationPlugin.php new file mode 100644 index 000000000..e4e046d08 --- /dev/null +++ b/plugins/Authorization/AuthorizationPlugin.php @@ -0,0 +1,108 @@ +<?php +/** + * StatusNet, the distributed open-source microblogging tool + * + * Superclass for plugins that do authorization + * + * PHP version 5 + * + * LICENCE: This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @category Plugin + * @package StatusNet + * @author Craig Andrews <candrews@integralblue.com> + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +/** + * Superclass for plugins that do authorization + * + * @category Plugin + * @package StatusNet + * @author Craig Andrews <candrews@integralblue.com> + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +abstract class AuthorizationPlugin extends Plugin +{ + //is this plugin authoritative for authorization? + public $authoritative = false; + + //------------Auth plugin should implement some (or all) of these methods------------\\ + + /** + * Is a user allowed to log in? + * @param user + * @return boolean true if the user is allowed to login, false if explicitly not allowed to login, null if we don't explicitly allow or deny login + */ + function loginAllowed($user) { + return null; + } + + /** + * Does a profile grant the user a named role? + * @param profile + * @return boolean true if the profile has the role, false if not + */ + function hasRole($profile, $name) { + return false; + } + + //------------Below are the methods that connect StatusNet to the implementing Auth plugin------------\\ + function onInitializePlugin(){ + + } + + function onStartSetUser(&$user) { + $loginAllowed = $this->loginAllowed($user); + if($loginAllowed === true){ + return; + }else if($loginAllowed === false){ + $user = null; + return false; + }else{ + if($this->authoritative) { + $user = null; + return false; + }else{ + return; + } + } + } + + function onStartSetApiUser(&$user) { + return $this->onStartSetUser(&$user); + } + + function onStartHasRole($profile, $name, &$has_role) { + if($this->hasRole($profile, $name)){ + $has_role = true; + return false; + }else{ + if($this->authoritative) { + $has_role = false; + return false; + }else{ + return; + } + } + } +} + diff --git a/plugins/LdapAuthentication/LdapAuthenticationPlugin.php b/plugins/LdapAuthentication/LdapAuthenticationPlugin.php index ad5dd3a02..8caacff46 100644 --- a/plugins/LdapAuthentication/LdapAuthenticationPlugin.php +++ b/plugins/LdapAuthentication/LdapAuthenticationPlugin.php @@ -2,7 +2,7 @@ /** * StatusNet, the distributed open-source microblogging tool * - * Plugin to enable LDAP Authentication and Authorization + * Plugin to enable LDAP Authentication * * PHP version 5 * @@ -63,6 +63,9 @@ class LdapAuthenticationPlugin extends AuthenticationPlugin if(!isset($this->attributes['username'])){ throw new Exception("must specify a username attribute"); } + if($this->password_changeable && (! isset($this->attributes['password']) || !isset($this->password_encoding))){ + throw new Exception("if password_changeable is set, the password attribute and password_encoding must also be specified"); + } } //---interface implementation---// @@ -156,20 +159,21 @@ class LdapAuthenticationPlugin extends AuthenticationPlugin } function ldap_get_connection($config = null){ - if($config == null){ - $config = $this->ldap_get_config(); + if($config == null && isset($this->default_ldap)){ + return $this->default_ldap; } //cannot use Net_LDAP2::connect() as StatusNet uses //PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, 'handleError'); //PEAR handling can be overridden on instance objects, so we do that. - $ldap = new Net_LDAP2($config); + $ldap = new Net_LDAP2(isset($config)?$config:$this->ldap_get_config()); $ldap->setErrorHandling(PEAR_ERROR_RETURN); $err=$ldap->bind(); if (Net_LDAP2::isError($err)) { common_log(LOG_WARNING, 'Could not connect to LDAP server: '.$err->getMessage()); return false; } + if($config == null) $this->default_ldap=$ldap; return $ldap; } @@ -186,7 +190,6 @@ class LdapAuthenticationPlugin extends AuthenticationPlugin } $filter = Net_LDAP2_Filter::create($this->attributes['username'], 'equals', $username); $options = array( - 'scope' => 'sub', 'attributes' => $attributes ); $search = $ldap->search(null,$filter,$options); diff --git a/plugins/LdapAuthorization/LdapAuthorizationPlugin.php b/plugins/LdapAuthorization/LdapAuthorizationPlugin.php new file mode 100644 index 000000000..5e759c379 --- /dev/null +++ b/plugins/LdapAuthorization/LdapAuthorizationPlugin.php @@ -0,0 +1,211 @@ +<?php +/** + * StatusNet, the distributed open-source microblogging tool + * + * Plugin to enable LDAP Authorization + * + * PHP version 5 + * + * LICENCE: This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @category Plugin + * @package StatusNet + * @author Craig Andrews <candrews@integralblue.com> + * @copyright 2009 Craig Andrews http://candrews.integralblue.com + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +require_once INSTALLDIR.'/plugins/Authorization/AuthorizationPlugin.php'; +require_once 'Net/LDAP2.php'; + +class LdapAuthorizationPlugin extends AuthorizationPlugin +{ + public $host=null; + public $port=null; + public $version=null; + public $starttls=null; + public $binddn=null; + public $bindpw=null; + public $basedn=null; + public $options=null; + public $filter=null; + public $scope=null; + public $provider_name = null; + public $uniqueMember_attribute = null; + public $roles_to_groups = array(); + public $login_group = null; + public $attributes = array(); + + function onInitializePlugin(){ + parent::onInitializePlugin(); + if(!isset($this->host)){ + throw new Exception("must specify a host"); + } + if(!isset($this->basedn)){ + throw new Exception("must specify a basedn"); + } + if(!isset($this->provider_name)){ + throw new Exception("provider_name must be set. Use the provider_name from the LDAP Authentication plugin."); + } + if(!isset($this->uniqueMember_attribute)){ + throw new Exception("uniqueMember_attribute must be set."); + } + if(!isset($this->attributes['username'])){ + throw new Exception("username attribute must be set."); + } + } + + //---interface implementation---// + function loginAllowed($user) { + $user_username = new User_username(); + $user_username->user_id=$user->id; + $user_username->provider_name=$this->provider_name; + if($user_username->find() && $user_username->fetch()){ + $entry = $this->ldap_get_user($user_username->username); + if($entry){ + if(isset($this->login_group)){ + if(is_array($this->login_group)){ + foreach($this->login_group as $group){ + if($this->ldap_is_dn_member_of_group($entry->dn(),$group)){ + return true; + } + } + }else{ + if($this->ldap_is_dn_member_of_group($entry->dn(),$this->login_group)){ + return true; + } + } + return null; + }else{ + //if a user exists, we can assume he's allowed to login + return true; + } + }else{ + return null; + } + }else{ + return null; + } + } + + function hasRole($profile, $name) { + $user_username = new User_username(); + $user_username->user_id=$profile->id; + $user_username->provider_name=$this->provider_name; + if($user_username->find() && $user_username->fetch()){ + $entry = $this->ldap_get_user($user_username->username); + if($entry){ + if(isset($this->roles_to_groups[$name])){ + if(is_array($this->roles_to_groups[$name])){ + foreach($this->roles_to_groups[$name] as $group){ + if($this->ldap_is_dn_member_of_group($entry->dn(),$group)){ + return true; + } + } + }else{ + if($this->ldap_is_dn_member_of_group($entry->dn(),$this->roles_to_groups[$name])){ + return true; + } + } + } + } + } + return false; + } + + function ldap_is_dn_member_of_group($userDn, $groupDn) + { + $ldap = $this->ldap_get_connection(); + $link = $ldap->getLink(); + $r = ldap_compare($link, $groupDn, $this->uniqueMember_attribute, $userDn); + if ($r === true){ + return true; + }else if($r === false){ + return false; + }else{ + common_log(LOG_ERR, ldap_error($r)); + return false; + } + } + + function ldap_get_config(){ + $config = array(); + $keys = array('host','port','version','starttls','binddn','bindpw','basedn','options','filter','scope'); + foreach($keys as $key){ + $value = $this->$key; + if($value!==null){ + $config[$key]=$value; + } + } + return $config; + } + + //-----the below function were copied from LDAPAuthenticationPlugin. They will be moved to a utility class soon.----\\ + function ldap_get_connection($config = null){ + if($config == null && isset($this->default_ldap)){ + return $this->default_ldap; + } + + //cannot use Net_LDAP2::connect() as StatusNet uses + //PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, 'handleError'); + //PEAR handling can be overridden on instance objects, so we do that. + $ldap = new Net_LDAP2(isset($config)?$config:$this->ldap_get_config()); + $ldap->setErrorHandling(PEAR_ERROR_RETURN); + $err=$ldap->bind(); + if (Net_LDAP2::isError($err)) { + common_log(LOG_WARNING, 'Could not connect to LDAP server: '.$err->getMessage()); + return false; + } + if($config == null) $this->default_ldap=$ldap; + return $ldap; + } + + /** + * get an LDAP entry for a user with a given username + * + * @param string $username + * $param array $attributes LDAP attributes to retrieve + * @return string DN + */ + function ldap_get_user($username,$attributes=array(),$ldap=null){ + if($ldap==null) { + $ldap = $this->ldap_get_connection(); + } + $filter = Net_LDAP2_Filter::create($this->attributes['username'], 'equals', $username); + $options = array( + 'attributes' => $attributes + ); + $search = $ldap->search(null,$filter,$options); + + if (PEAR::isError($search)) { + common_log(LOG_WARNING, 'Error while getting DN for user: '.$search->getMessage()); + return false; + } + + if($search->count()==0){ + return false; + }else if($search->count()==1){ + $entry = $search->shiftEntry(); + return $entry; + }else{ + common_log(LOG_WARNING, 'Found ' . $search->count() . ' ldap user with the username: ' . $username); + return false; + } + } +} diff --git a/plugins/LdapAuthorization/README b/plugins/LdapAuthorization/README new file mode 100644 index 000000000..44239d8e0 --- /dev/null +++ b/plugins/LdapAuthorization/README @@ -0,0 +1,91 @@ +The LDAP Authorization plugin allows for StatusNet to handle authorization +through LDAP. + +Installation +============ +add "addPlugin('ldapAuthorization', + array('setting'=>'value', 'setting2'=>'value2', ...);" +to the bottom of your config.php + +You *cannot* use this plugin without the LDAP Authentication plugin + +Settings +======== +provider_name*: name of the LDAP authentication provider that this plugin works with. +authoritative (false): should this plugin be authoritative for + authorization? +uniqueMember_attribute ('uniqueMember')*: the attribute of a group + that lists the DNs of its members +roles_to_groups: array that maps StatusNet roles to LDAP groups + some StatusNet roles are: moderator, administrator, sandboxed, silenced +login_group: if this is set to a group DN, only members of that group will be + allowed to login + +The below settings must be exact copies of the settings used for the + corresponding LDAP Authentication plugin. + +host*: LDAP server name to connect to. You can provide several hosts in an + array in which case the hosts are tried from left to right. + See http://pear.php.net/manual/en/package.networking.net-ldap2.connecting.php +port: Port on the server. + See http://pear.php.net/manual/en/package.networking.net-ldap2.connecting.php +version: LDAP version. + See http://pear.php.net/manual/en/package.networking.net-ldap2.connecting.php +starttls: TLS is started after connecting. + See http://pear.php.net/manual/en/package.networking.net-ldap2.connecting.php +binddn: The distinguished name to bind as (username). + See http://pear.php.net/manual/en/package.networking.net-ldap2.connecting.php +bindpw: Password for the binddn. + See http://pear.php.net/manual/en/package.networking.net-ldap2.connecting.php +basedn*: LDAP base name (root directory). + See http://pear.php.net/manual/en/package.networking.net-ldap2.connecting.php +options: See http://pear.php.net/manual/en/package.networking.net-ldap2.connecting.php +filter: Default search filter. + See http://pear.php.net/manual/en/package.networking.net-ldap2.connecting.php +scope: Default search scope. + See http://pear.php.net/manual/en/package.networking.net-ldap2.connecting.php + +attributes: an array that relates StatusNet user attributes to LDAP ones + username*: LDAP attribute value entered when authenticating to StatusNet + +* required +default values are in (parenthesis) + +Example +======= +Here's an example of an LDAP plugin configuration that connects to + Microsoft Active Directory. + +addPlugin('ldapAuthentication', array( + 'provider_name'=>'Example', + 'authoritative'=>true, + 'autoregistration'=>true, + 'binddn'=>'username', + 'bindpw'=>'password', + 'basedn'=>'OU=Users,OU=StatusNet,OU=US,DC=americas,DC=global,DC=loc', + 'host'=>array('server1', 'server2'), + 'password_encoding'=>'ad', + 'attributes'=>array( + 'username'=>'sAMAccountName', + 'nickname'=>'sAMAccountName', + 'email'=>'mail', + 'fullname'=>'displayName', + 'password'=>'unicodePwd') +)); +addPlugin('ldapAuthorization', array( + 'provider_name'=>'Example', + 'authoritative'=>false, + 'uniqueMember_attribute'=>'member', + 'roles_to_groups'=> array( + 'moderator'=>'CN=SN-Moderators,OU=Users,OU=StatusNet,OU=US,DC=americas,DC=global,DC=loc', + 'administrator'=> array('CN=System-Adminstrators,OU=Users,OU=StatusNet,OU=US,DC=americas,DC=global,DC=loc', + 'CN=SN-Administrators,OU=Users,OU=StatusNet,OU=US,DC=americas,DC=global,DC=loc') + ), + 'binddn'=>'username', + 'bindpw'=>'password', + 'basedn'=>'OU=Users,OU=StatusNet,OU=US,DC=americas,DC=global,DC=loc', + 'host'=>array('server1', 'server2'), + 'attributes'=>array( + 'username'=>'sAMAccountName') +)); + |