I'm trying to setup single sign on for MediaWiki with ExtAuthDB extension. The purpose is to authenticate user from external user system automatically when user logins in the main website: www.mysite.com. Mediawiki is located on subdomain: www.wiki.mysite.com.
I have installed the extension as it said in the guide. All priviliges are correct. But it doesn't work.
ExtAuthDB.php is:
<?php
/**
* Authentication plugin interface. Instantiate a subclass of AuthPlugin
* and set $wgAuth to it to authenticate against some external tool.
*
* The default behavior is not to do anything, and use the local user
* database for all authentication. A subclass can require that all
* accounts authenticate externally, or use it only as a fallback; also
* you can transparently create internal wiki accounts the first time
* someone logs in who can be authenticated externally.
*
* This interface is a derivation of AuthJoomla and might change a bit before 1.4.0 final is done...
*
*/
$wgExtensionCredits['parserhook'][] = array (
'name' => 'ExtAuthDB',
'author' => 'Alessandra Bilardi',
'description' => 'Authenticate users about external MySQL database',
'url' => 'https://www.mediawiki.org/wiki/Extension:ExtAuthDB',
'version' => '0.1',
);
require_once ( "$IP/includes/AuthPlugin.php" );
class ExtAuthDB extends AuthPlugin
{
/**
* Add into LocalSettings.php the following code:
*
* MySQL Host Name.
* $wgExtAuthDB_MySQL_Host = '';
* MySQL Username.
* $wgExtAuthDB_MySQL_Username = '';
* MySQL Password.
* $wgExtAuthDB_MySQL_Password = '';
* MySQL Database Name.
* $wgExtAuthDB_MySQL_Database = '';
* MySQL Database Table of users data.
* $wgExtAuthDB_MySQL_Table = '';
* MySQL Database username column label.
* $wgExtAuthDB_MySQL_Login = '';
* MySQL Database login password column label
* $wgExtAuthDB_MySQL_Pswrd = '';
* MySQL Database email column label
* $wgExtAuthDB_MySQL_Email = '';
* MySQL Database user real name column label
* $wgExtAuthDB_MySQL_RealN = '';
* require_once("$IP/extensions/ExtAuthDB/ExtAuthDB.php");
* $wgAuth = new ExtAuthDB();
*
* #return Object Database
*/
private function connectToDB()
{
$db = & Database :: newFromParams(
$GLOBALS['wgExtAuthDB_MySQL_Host'],
$GLOBALS['wgExtAuthDB_MySQL_Username'],
$GLOBALS['wgExtAuthDB_MySQL_Password'],
$GLOBALS['wgExtAuthDB_MySQL_Database']);
$this->userTable = $GLOBALS['wgExtAuthDB_MySQL_Table'];
$this->userLogin = $GLOBALS['wgExtAuthDB_MySQL_Login'];
$this->userPswrd = $GLOBALS['wgExtAuthDB_MySQL_Pswrd'];//.$GLOBALS['$wgExtAuthDB_MySQL_Salt'];
$this->userEmail = $GLOBALS['wgExtAuthDB_MySQL_Email'];
$this->userRealN = $GLOBALS['wgExtAuthDB_MySQL_RealN'];
wfDebug("ExtAuthDB::connectToDB() : DB failed to open\n");
return $db;
}
/**
* Check whether there exists a user account with the given name.
* The name will be normalized to MediaWiki's requirements, so
* you might need to munge it (for instance, for lowercase initial
* letters).
*
* #param $username String: username.
* #return bool
* #public
*/
function userExists( $username ) {
# Override this!
return true;
}
/**
* Check if a username+password pair is a valid login.
* The name will be normalized to MediaWiki's requirements, so
* you might need to munge it (for instance, for lowercase initial
* letters).
*
* #param $username String: username.
* #param $password String: user password.
* #return bool
* #public
*/
function authenticate( $username, $password )
{
$db = $this->connectToDB();
$hash_password = $db->selectRow($this->userTable,array ($this->userPswrd), array ($this->userLogin => $username ), __METHOD__ );
if ($password == $hash_password->{$this->userPswrd}) {
return true;
}
return false;
}
/**
* Set the domain this plugin is supposed to use when authenticating.
*
* #param $domain String: authentication domain.
* #public
*/
function setDomain( $domain ) {
$this->domain = $domain;
}
/**
* Check to see if the specific domain is a valid domain.
*
* #param $domain String: authentication domain.
* #return bool
* #public
*/
function validDomain( $domain ) {
# Override this!
return true;
}
/**
* When a user logs in, optionally fill in preferences and such.
* For instance, you might pull the email address or real name from the
* external user database.
*
* The User object is passed by reference so it can be modified; don't
* forget the & on your function declaration.
*
* #param User $user
* #public
*/
function updateUser( &$user )
{
$db = $this->connectToDB();
$euser = $db->selectRow($this->userTable,array ( '*' ), array ($this->userLogin => $user->mName ), __METHOD__ );
$user->setRealName($euser->{$this->userRealN});
$user->setEmail($euser->{$this->userEmail});
$user->mEmailAuthenticated = wfTimestampNow();
$user->saveSettings();
//exit;
# Override this and do something
return true;
}
function disallowPrefsEditByUser() {
return array (
'wpRealName' => true,
'wpUserEmail' => true,
'wpNick' => true
);
}
/**
* Return true if the wiki should create a new local account automatically
* when asked to login a user who doesn't exist locally but does in the
* external auth database.
*
* If you don't automatically create accounts, you must still create
* accounts in some way. It's not possible to authenticate without
* a local account.
*
* This is just a question, and shouldn't perform any actions.
*
* #return bool
* #public
*/
function autoCreate() {
return true;
}
/**
* Can users change their passwords?
*
* #return bool
*/
function allowPasswordChange() {
return false;
}
/**
* Set the given password in the authentication database.
* As a special case, the password may be set to null to request
* locking the password to an unusable value, with the expectation
* that it will be set later through a mail reset or other method.
*
* Return true if successful.
*
* #param $user User object.
* #param $password String: password.
* #return bool
* #public
*/
function setPassword( $user, $password ) {
return true;
}
/**
* Update user information in the external authentication database.
* Return true if successful.
*
* #param $user User object.
* #return bool
* #public
*/
function updateExternalDB( $user ) {
$db = $this->connectToDB();
$euser = $db->selectRow($this->userTable,array ( '*' ), array ($this->userLogin => $user->mName ), __METHOD__ );
$user->setRealName($euser->{$this->userRealN});
$user->setEmail($euser->{$this->userEmail});
$user->mEmailAuthenticated = wfTimestampNow();
$user->saveSettings();
return true;
}
/**
* Check to see if external accounts can be created.
* Return true if external accounts can be created.
* #return bool
* #public
*/
function canCreateAccounts() {
return false;
}
/**
* Add a user to the external authentication database.
* Return true if successful.
*
* #param User $user - only the name should be assumed valid at this point
* #param string $password
* #param string $email
* #param string $realname
* #return bool
* #public
*/
function addUser( $user, $password, $email='', $realname='' ) {
return false;
}
/**
* Return true to prevent logins that don't authenticate here from being
* checked against the local database's password fields.
*
* This is just a question, and shouldn't perform any actions.
*
* #return bool
* #public
*/
function strict() {
return true;
}
/**
* When creating a user account, optionally fill in preferences and such.
* For instance, you might pull the email address or real name from the
* external user database.
*
* The User object is passed by reference so it can be modified; don't
* forget the & on your function declaration.
*
* #param $user User object.
* #param $autocreate bool True if user is being autocreated on login
* #public
*/
function initUser( $user, $autocreate=false ) {
# Override this to do something.
}
/**
* If you want to munge the case of an account name before the final
* check, now is your chance.
*/
function getCanonicalName( $username ) {
return $username;
}
}
And in LocalSettings.php, I should add this code:
// add ExtAuthDB
// MySQL Host Name.
$wgExtAuthDB_MySQL_Host = 'localhost';
// MySQL Username.
$wgExtAuthDB_MySQL_Username = 'dbuser';
// MySQL Password.
$wgExtAuthDB_MySQL_Password = 'dbpassword';
// MySQL Database Name.
$wgExtAuthDB_MySQL_Database = 'base';
// MySQL Database Table of users data.
$wgExtAuthDB_MySQL_Table = 'members';
// MySQL Database username column label.
$wgExtAuthDB_MySQL_Login = 'username';
// MySQL Database login password column label
$wgExtAuthDB_MySQL_Pswrd = 'password';
$wgExtAuthDB_MySQL_Salt='salt';
// MySQL Database email column label
$wgExtAuthDB_MySQL_Email = 'email';
// MySQL Database user real name column label
$wgExtAuthDB_MySQL_RealN = 'real_name';
require_once("$IP/extensions/ExtAuthDB/ExtAuthDB.php");
$wgAuth = new ExtAuthDB();
Sorry, I had to copy full script, because I don't know where is the exact fault. And my question is: Why doesn't it work? Where is the mistake?
EDIT:
My external user table consists of id, username, password, salt, email, real_name. I thought it could be because of seperate password and salt fields, so I tried to implement salt in ExtAuthDB.php file manually. Unfortunately, it didn't work either. Then I commented this line.
I was able to setup SSO (Single sign-on) from WordPress to media wiki using OAuth 2.0 server, I have posted my solution on this post
Or you can follow these steps:
First you need an OAuth 2.0 server, you could implement it your self see details here Run your own OAuth 2.0 Server or the easiest way is to use the WordPress plugin WP Oauth 2.0 server you don't have to buy the pro, you can also implement SSO by using the Grant type Authorization codes which comes free.
You need OAuth 2.0 client extension installed on your media wiki, the extension can be found here, follow the installation instructions there.
Go to WordPress plugin page and activate OAuth server, then navigate to OAuth Server and add a new client, give your client a name and in Redirect URI add the link mention on the media wiki extension page i.e http://your.wiki.domain/path/to/wiki/Special:OAuth2Client/callback, then go to OAuth>clients page where you can see your newly created client, click edit and here you can see clientID and Client secret add this ID and secret in the localSettings.php of your media wiki.
Create a page on WordPress and put the following button with your client id in it
< a href="https://your-Domain-Where-OAuth-server-is-running.de/oauth/authorize?response_type=code&client_id=YOURCLIENTID&state=RANDOM-STRING&scope=basic">
go to wiki</a>
don't forget to put scope otherwise you will get a media wiki internal error.
If everything worked fine then you should automatically go to the media wiki main page after clicking this button from your WordPress. media wiki will show you as logged in. It took me some time to figure it out I hope this helps anyone who comes here.
You need to run the MediaWiki update script for this extension.
Many extensions need an update with update.php script!
From the browser
If you do not have access to the command line of your server, then use the web updater to run the update script.
From the command line
From the command line, or an SSH shell or similar:
Change to the maintenance directory!
Run the update script with php update.php command!
Related
I'm trying to utilize the external class on my idp install of simple saml. The idp works fine for logging in user/pass but I need the external to help verify some types of users.
Here is the class I'm attempting to modify and use
https://github.com/simplesamlphp/simplesamlphp/blob/56a8949141a3aa2d783763aaaaccaa0ccf6164c2/modules/exampleauth/lib/Auth/Source/External.php
I keep getting this php fatal error
PHP Fatal error: Declaration of SimpleSAML\Module\mymodule\Auth\Source\MyAuth::authenticate() must be compatible with SimpleSAML\Auth\Source::authenticate(&$state) in /var/simplesamlphp/modules/mymodule/lib/Auth/Source/MyAuth.php on line 355, referer: https://xxxxxxxyyyyyzzzzz.com/
I've googled and cannot seem to find anything relating to this. Any ideas where I might have gone wrong or what I may try to fix?
<?php
declare(strict_types=1);
namespace SimpleSAML\Module\mymodule\Auth\Source;
use SimpleSAML\Assert\Assert;
use SimpleSAML\Auth;
use SimpleSAML\Error;
use SimpleSAML\Module;
use SimpleSAML\Utils;
//use Symfony\Component\HttpFoundation\Request;
//use Symfony\Component\HttpFoundation\Session\Session as SymfonySession;
/**
* Example external authentication source.
*
* This class is an example authentication source which is designed to
* hook into an external authentication system.
*
* To adapt this to your own web site, you should:
* 1. Create your own module directory.
* 2. Enable to module in the config by adding '<module-dir>' => true to the $config['module.enable'] array.
* 3. Copy this file to its corresponding location in the new module.
* 4. Replace all occurrences of "mymodule" in this file with the name of your module.
* 5. Adapt the getUser()-function, the authenticate()-function and the logout()-function to your site.
* 6. Add an entry in config/authsources.php referencing your module. E.g.:
* 'myauth' => [
* '<mymodule>:External',
* ],
*
* #package SimpleSAMLphp
*/
class MyAuth extends Auth\Source
{
/**
* The key of the AuthId field in the state.
*/
//public const AUTHID = 'SimpleSAML\Module\mymodule\Auth\Source\MyAuth.AuthId';
const AUTHID = 'SimpleSAML\Module\mymodule\Auth\Source\MyAuth.AuthId';
/**
* Constructor for this authentication source.
*
* #param array $info Information about this authentication source.
* #param array $config Configuration.
*/
public function __construct(array $info, array $config)
{
// Call the parent constructor first, as required by the interface
parent::__construct($info, $config);
// Do any other configuration we need here
}
/**
* Log in using an external authentication helper.
*
* #param array &$state Information about the current authentication.
*/
public function authenticate(array &$state)
//public function authenticate(array &$state)
{
require_once('/yyy/xxx/some_server/public_html/web/setup.php');
$_users = new \Users;
$user = $_users->verifyUser($username,$password);
$attributes = [
'user_id' => [$user['user_id']],
'mfg_dealer_number' => [$user['mfg_dealer_number']],
'location_name' => [$location['name']],
'first_name' => [$name[0]],
'last_name' => [$name[1]],
'email' => [$user['email']],
'address1' => [$location['address']],
'address2' => [$location['address2']],
'city' => [$location['city']],
'country' => [$location['country_abbrev']],
'state' => [$location['state_abbrev']],
'zip' => [$location['zip']],
'phone' => [$location['phone']],
'user_type' => [$user_type],
];
if ($attributes !== null) {
/*
* The user is already authenticated.
*
* Add the users attributes to the $state-array, and return control
* to the authentication process.
*/
$state['Attributes'] = $attributes;
return;
}
/*
* The user isn't authenticated. We therefore need to
* send the user to the login page.
*/
/*
* First we add the identifier of this authentication source
* to the state array, so that we know where to resume.
*/
$state['mymodule:AuthID'] = $this->authId;
/*
* We need to save the $state-array, so that we can resume the
* login process after authentication.
*
* Note the second parameter to the saveState-function. This is a
* unique identifier for where the state was saved, and must be used
* again when we retrieve the state.
*
* The reason for it is to prevent
* attacks where the user takes a $state-array saved in one location
* and restores it in another location, and thus bypasses steps in
* the authentication process.
*/
$stateId = Auth\State::saveState($state, 'mymodule:MyAuth');
/*
* Now we generate a URL the user should return to after authentication.
* We assume that whatever authentication page we send the user to has an
* option to return the user to a specific page afterwards.
*/
$returnTo = Module::getModuleURL('mymodule/resume', [
'State' => $stateId,
]);
/*
* Get the URL of the authentication page.
*
* Here we use the getModuleURL function again, since the authentication page
* is also part of this module, but in a real example, this would likely be
* the absolute URL of the login page for the site.
*/
$authPage = Module::getModuleURL('mymodule/authpage');
/*
* The redirect to the authentication page.
*
* Note the 'ReturnTo' parameter. This must most likely be replaced with
* the real name of the parameter for the login page.
*/
$httpUtils = new Utils\HTTP();
$httpUtils->redirectTrustedURL($authPage, [
'ReturnTo' => $returnTo,
]);
/*
* The redirect function never returns, so we never get this far.
*/
Assert::true(false);
}
/**
* Resume authentication process.
*
* This function resumes the authentication process after the user has
* entered his or her credentials.
*
* #param \Symfony\Component\HttpFoundation\Request $request
*
* #throws \SimpleSAML\Error\BadRequest
* #throws \SimpleSAML\Error\Exception
*/
public static function resume(Request $request)
{
/*
* First we need to restore the $state-array. We should have the identifier for
* it in the 'State' request parameter.
*/
if (!$request->query->has('State')) {
throw new Error\BadRequest('Missing "State" parameter.');
}
/*
* Once again, note the second parameter to the loadState function. This must
* match the string we used in the saveState-call above.
*/
$state = Auth\State::loadState($request->query->get('State'), 'mymodule:MyAuth');
/*
* Now we have the $state-array, and can use it to locate the authentication
* source.
*/
$source = Auth\Source::getById($state['mymodule:AuthID']);
if ($source === null) {
/*
* The only way this should fail is if we remove or rename the authentication source
* while the user is at the login page.
*/
throw new Error\Exception('Could not find authentication source with id ' . $state[self::AUTHID]);
}
/*
* Make sure that we haven't switched the source type while the
* user was at the authentication page. This can only happen if we
* change config/authsources.php while an user is logging in.
*/
if (!($source instanceof self)) {
throw new Error\Exception('Authentication source type changed.');
}
/*
* OK, now we know that our current state is sane. Time to actually log the user in.
*
* First we check that the user is acutally logged in, and didn't simply skip the login page.
*/
require_once('/yyy/xxx/some_server/public_html/web/setup.php');
$_users = new \Users;
$user = $_users->verifyUser($username,$password);
$attributes = [
'user_id' => [$user['user_id']],
'mfg_dealer_number' => [$user['mfg_dealer_number']],
'location_name' => [$location['name']],
'first_name' => [$name[0]],
'last_name' => [$name[1]],
'email' => [$user['email']],
'address1' => [$location['address']],
'address2' => [$location['address2']],
'city' => [$location['city']],
'country' => [$location['country_abbrev']],
'state' => [$location['state_abbrev']],
'zip' => [$location['zip']],
'phone' => [$location['phone']],
'user_type' => [$user_type],
];
if ($attributes === null) {
/*
* The user isn't authenticated.
*
* Here we simply throw an exception, but we could also redirect the user back to the
* login page.
*/
throw new Error\Exception('User not authenticated after login page.');
}
/*
* So, we have a valid user. Time to resume the authentication process where we
* paused it in the authenticate()-function above.
*/
$state['Attributes'] = $attributes;
Auth\Source::completeAuth($state);
/*
* The completeAuth-function never returns, so we never get this far.
*/
Assert::true(false);
}
/**
* This function is called when the user start a logout operation, for example
* by logging out of a SP that supports single logout.
*
* #param array &$state The logout state array.
*/
public function logout(array &$state)
{
//$session = new SymfonySession();
//if (!$session->getId()) {
// $session->start();
//}
$session->clear();
/*
* If we need to do a redirect to a different page, we could do this
* here, but in this example we don't need to do this.
*/
}
}
You seem to be mixing up two SimpleSAML versions.
This code:
public function authenticate(array &$state)
has "array" typehint in it while the class you're extending in SimpleSAMLphp does not, as evidenced from the error message:
must be compatible with SimpleSAML\Auth\Source::authenticate(&$state)
So remove the array keyword. If you use code/documentation that matches the SimpleSAMLphp version you are running this should match.
i'm trying to keep my php rest server properly documents, so i was wondering how do we document that the return value can be one of 2 ?
/**
* send email to a user that contain reset data
* it also create reset token, so if there was an old reset token it will be changed
*
* #param email $email {#type email"
* #return SuccessMessage|FailMessage
*/
public function getSearch($email){
// search for $email, if success return
if($this->doOperation()===true){
return new SuccessMessage($email);
}
return new FailMessage($email);
}
nowing that both FailMessage and SuccessMessage are just empty classes to act as a consistent data structure
something more like this.
class FailMessage extends Messages{
function __construct(string $message, int $uid) {
if(is_callable("parent::__construct")){
parent::__construct(...func_get_args());
}
$this->uid = $uid;
$this->message = $data;
}
}
/**
* send email to a user that contain reset data
* it also create reset token, so if there was an old reset token it will be changed
*
* #param email $email {#type email"
* #return object SuccessMessage|FailMessage
*/
Your own comment is Ok but for more you can add object type. Also read the following document from the official website:
returnTags-PHPDoc
I exploited the last days, but I wasn't able to find a real useful tutorial to intigrate LDAP Authentication into a vTiger CRM 6 (running on a Linux CentOS 6.5 distribution).
Any one experienced out here or some people who might share some useful manuals ?
Make directory into your crm destination:
/var/www/html/crm/modules/Users/authTypes/
Then, Download the ldap file from :
http://downloads.sourceforge.net/project/adldap/adLDAP/adLDAP_4.0.4/adLDAP_4.0
Just open and customize the settings for your needs. The following settings match those needed for a 2012R2 Active Directory.
...
class adLDAP {
/**
* Define the different types of account in AD
*/
const ADLDAP_NORMAL_ACCOUNT = 805306368;
const ADLDAP_WORKSTATION_TRUST = 805306369;
const ADLDAP_INTERDOMAIN_TRUST = 805306370;
const ADLDAP_SECURITY_GLOBAL_GROUP = 268435456;
const ADLDAP_DISTRIBUTION_GROUP = 268435457;
const ADLDAP_SECURITY_LOCAL_GROUP = 536870912;
const ADLDAP_DISTRIBUTION_LOCAL_GROUP = 536870913;
const ADLDAP_FOLDER = 'OU';
const ADLDAP_CONTAINER = 'CN';
/**
* The default port for LDAP non-SSL connections
*/
const ADLDAP_LDAP_PORT = '389';
/**
* The default port for LDAPS SSL connections
*/
const ADLDAP_LDAPS_PORT = '636';
/**
* The account suffix for your domain, can be set when the class is invoked
*
* #var string
*/
protected $accountSuffix = "#cortoso.com";
/**
* The base dn for your domain
*
* If this is set to null then adLDAP will attempt to obtain this automatically from the rootDSE
*
* #var string
*/
protected $baseDn = "";
/**
* Port used to talk to the domain controllers.
*
* #var int
*/
protected $adPort = self::ADLDAP_LDAP_PORT;
/**
* Array of domain controllers. Specifiy multiple controllers if you
* would like the class to balance the LDAP queries amongst multiple servers
*
* #var array
*/
protected $domainControllers = array("dc01.cortoso.com", "dc02.cortoso.com");
/**
* Optional account with higher privileges for searching
* This should be set to a domain admin account
*
* #var string
* #var string
*/
protected $adminUsername = "ldap-binduser";
protected $adminPassword = "super-password";
/**
* AD does not return the primary group. http://support.microsoft.com/?kbid=321360
* This tweak will resolve the real primary group.
* Setting to false will fudge "Domain Users" and is much faster. Keep in mind though that if
* someone's primary group is NOT domain users, this is obviously going to mess up the results
*
* #var bool
*/
protected $realPrimaryGroup = false;
/**
* Use SSL (LDAPS), your server needs to be setup, please see
* http://adldap.sourceforge.net/wiki/doku.php?id=ldap_over_ssl
*
* #var bool
*/
protected $useSSL = false;
/**
* Use TLS
* If you wish to use TLS you should ensure that $useSSL is set to false and vice-versa
*
* #var bool
*/
protected $useTLS = true;
/**
* Use SSO
* To indicate to adLDAP to reuse password set by the brower through NTLM or Kerberos
*
* #var bool
*/
protected $useSSO = false;
/**
* When querying group memberships, do it recursively
* eg. User Fred is a member of Group A, which is a member of Group B, which is a member of Group C
* user_ingroup("Fred","C") will returns true with this option turned on, false if turned off
*
* #var bool
*/
protected $recursiveGroups = true;
...
?>
To be able to test adLDAP, it is much easier to write a small php sniplet than doing it directly with vTiger CRM. Just create a small adldap_test.php file, in the same directory where adLDAP.php resides, with following content:
<?php
require_once(dirname(FILE) . '/adLDAP.php');
try {
$adldap = new adLDAP();
}
catch (adLDAPException $e) {
echo $e;
exit();
}
$authUser = $adldap->authenticate('user-to-authenticate', 'users-password');
if ($authUser == true) {
echo "User authenticated successfully";
}
else {
// getLastError is not needed, but may be helpful for finding out why:
echo "\n";
echo $adldap->getLastError();
echo "\n";
echo "User authentication unsuccessful";
}
echo "\n";
$result=$adldap->user()->infoCollection('ldap', array("*"));
echo "User:\n";
echo $result->displayName;
echo "Mail:\n";
echo $result->mail;
?>
In restler, is is possible to use nested resources? For example, with restler I would do the normal /api/account/123 call to get that specific account. Now I want to get the clients that belong to that account. So I'd want to also call /api/account/123/client/456 for example to get the specific client for the specific account.
You can use manual routing to define such routes. See the following example
use Luracast\Restler\RestException;
class Accounts
{
/**
* Get specific client for the given account
*
* #param int $id account id
* #param int $client_id
*
* #throws RestException 404
*
* #return Client
*
* #url GET accounts/{id}/clients/{client_id}
*/
public function getClient($id, $client_id)
{
$r = Client::where('account_id', '=', $id)->where('id', '=', $client_id)->firstOrFail();
if (empty($r))
throw RestException(404, 'Client is not found associated with the account');
return $r;
}
/**
* Get all clients associated with the given account
*
* #param int $id account id
*
* #return array {#type Client}
*
* #url GET accounts/{id}/clients
*/
public function getClients($id)
{
return Client::where('account_id', '=', $id)->all();
}
}
*I'm trying to find out how the "simplest" form of identification can be achieve in order for my partners website to access my web service to retrieve information.*
I want to make sure that only my partner has access to retrieve this information. There is no sensitive data as the data will be shown on my partners website. But I don't want other websites taking advantage of my web service and retrieve data without having access to do so.
I know I can get the IP address using the HttpRequest object, then do a reverse lookup. But not all websites has dedicated ip address and a ISP may use the same IP Address for multiple websites.
I can't see how passing unique identifiers as parameter in the URL can help, because "anyone" can catch that data and use it themselves. But I will use it as an added check.
So the only "secure" way I come up with, is identifying the website accessing my website, then control this against a list on my server.
I would appreciate feedback on what methods would be "most secure".
It's common practice amongst web services to use public/private keys to authenticate API requests. A few example sites that use them: Google, Twitter, EventBrite, Last.FM, GitHub, etc.
These work by having a public or consumer key which is known to everyone. Then each user is given a private or secret key to allow authentication. The cool thing about using this, is since you know exactly who is making the request you have the ability to track activity and potentially throttle number of requests if abused.
The most secure method is to use SSL channel and ask for authentication. If authentication passes then you can give back to client some sort of session key(which can be randomly generated string) and check it on every request.
If your service don't allow to use SSL, then you can try just adding simple username/password authentication for your partners, but in this case if someone intercept your communication they can access your service with same credentials.
Other way is using signatures on every request. For example you can use GPG for this purpose. Your server is holding public keys of all your partners. When partner wants to make query to your server he just signs his request with his private key and upon receiving you will be able to securely verify that this request was sent by particular partner and it's 100% not forged.
Edit
For GPG you need to install PECL module called gnupg. Here is class from our framework that utilize GPG functionality.
class GPG
{
/**
* Encrypt given data to one or more recipients
*
* #param string $string
* #param string|array $encryptKeyID
* #param bollean $armour
* #return string
*/
public static function encrypt($string, $encryptKeyID, $armour = true){
$gpg = new Crypt_GPG();
if(is_array($encryptKeyID)){
foreach($encryptKeyID as $keyId){
$gpg->addEncryptKey($keyId);
}
}
else{
$gpg->addEncryptKey($encryptKeyID);
}
return $gpg->encrypt($string, $armour);
}
/**
* Decrypt given data
*
* #param string $string
* #param string $keyPassword
* #param string $keyID
* #return string
*/
public static function decrypt($string, $keyID, $keyPassword = null){
$gpg = new Crypt_GPG();
$gpg->addDecryptKey($keyID, $keyPassword);
return $gpg->decrypt($string);
}
/**
* Sign given string
*
* #param string $string
* #param string $keyID
* #param string $keyPassword
* #param boolean $mode
* #param boolean $armor
* #return string
*/
public static function sign($string, $keyID, $keyPassword = null, $mode = null, $armor = true){
$gpg = new Crypt_GPG();
if($mode === null){
$mode = Crypt_GPG::SIGN_MODE_CLEAR;
}
$gpg->addSignKey($keyID, $keyPassword);
return $gpg->sign($string, $mode);
}
/**
* Verify signature of given message
*
* #param string $string
* #return boolean
*/
public static function verify($string){
$gpg = new Crypt_GPG();
$signatures = $gpg->verify($string);
if ($signatures[0]->isValid()) {
return true;
}
else{
return false;
}
}
/**
* Encrypt and sign given string to one or more recipients
*
* #param string $string
* #param string|array $encryptKeyID
* #param string $signkeyID
* #param string $signkeyPassword
* #param boolean $mode
* #param boolean $armor
* #return string
*/
public static function encryptAndSign($string, $encryptKeyID, $signkeyID, $signkeyPassword = null, $mode = null, $armor = true){
$gpg = new Crypt_GPG();
if($mode === null){
$mode = Crypt_GPG::SIGN_MODE_CLEAR;
}
$gpg->addSignKey($signkeyID, $signkeyPassword);
if(is_array($encryptKeyID)){
foreach($encryptKeyID as $keyId){
$gpg->addEncryptKey($keyId);
}
}
else{
$gpg->addEncryptKey($encryptKeyID);
}
return $gpg->encryptAndSign($string, $armor);
}
/**
* Decrypt and verify given string
*
* #param string $string
* #param string $keyID
* #param string $keyPassword
* #return array|false
*/
public static function decryptAndVerify($string, $keyID, $keyPassword = null){
$gpg = new Crypt_GPG();
$gpg->addDecryptKey($keyID, $keyPassword);
$result = $gpg->decryptAndVerify($string);
if(empty($result['data']) and empty($result['signatures'])){
return false;
}
if(isset($result['signatures'][0])){
$result['signature'] = $result['signatures'][0]->isValid();
unset($result['signatures']);
}
return $result;
}
}