first import
This commit is contained in:
69
sites/all/modules/oauth/CHANGELOG
Normal file
69
sites/all/modules/oauth/CHANGELOG
Normal file
@@ -0,0 +1,69 @@
|
||||
OAuth 7.x-3.0, 2012-2-8
|
||||
------------------------------
|
||||
#1378078 by jobeirne: Make Authorizations editable.
|
||||
#1412226 by jobeirne, sun: allow for deletion of authentications.
|
||||
#54136 by sun: fix cancel button for authentication delete form.
|
||||
#1412258 by jobeirne: authentication_options fix
|
||||
|
||||
OAuth 7.x-3.0-alpha2, 2011-10-25
|
||||
------------------------------
|
||||
#1155760 by deviantintegral, voxpelli: Catching more exceptions, eg. when the consumer key is incorrect
|
||||
#1199360 by Sepehr Lajevardi, voxpelli: Fixed E_NOTICE in OAuth request context loader
|
||||
#1199346 by Sepehr Lajevardi: Fixed E_NOTICE in oauth_common_form_consumer_submit()
|
||||
#1134444 by grendzy, voxpelli: Warn if Autoload 2.x isn't used
|
||||
#1141658 by grendzy, voxpelli: Clear Autoload cache on install
|
||||
#1201166 by voxpelli: Fixed error on deletion of accounts
|
||||
|
||||
OAuth 7.x-3.0-alpha1, 2011-06-21
|
||||
------------------------------
|
||||
by voxpelli: Initial port to Drupal 7
|
||||
|
||||
OAuth 6.x-3.0-beta4, 2011-04-06
|
||||
------------------------------
|
||||
by voxpelli: Better coding style
|
||||
#1002482 by voxpelli: Large database refactoring focused on basing relations on internal id numbers
|
||||
by voxpelli: Improved nonces, nonces can now be longer and too long nonces will throw errors
|
||||
by voxpelli: Removed consumer UI as it became complicated with new DB and there's no apparent need for it
|
||||
by voxpelli: Updated to use Autoload 6.x-2.0:s D7 backport for specifying included classes
|
||||
by voxpelli: Don't show key/secret on consumer adding
|
||||
#1017220 by voxpelli: Deactivated body_hash-checking when Inpustream isn't installed
|
||||
#1024812 by voxpelli: Fix for deleting consumer
|
||||
#857584 by voxpelli: Make SHA1 the default signature method in client
|
||||
|
||||
OAuth 6.x-3.0-beta3, 2010-11-05
|
||||
------------------------------
|
||||
by voxpelli: Fixed user consumer listing
|
||||
by hugowetterberg: Fixed handling of empty and 'oob' callback urls.
|
||||
by SimmeLj, voxpelli: Added removal of old non-used nonces on cron
|
||||
#899506 by nicholasThompson, ezra-g: Fixed missing class in oauth_common_autoload_info()
|
||||
by voxpelli: Fixed bug assigning context to OAuthServer
|
||||
by voxpelli: Code cleanup
|
||||
by voxpelli: Made inputstream a suggestion instead of a dependency
|
||||
by voxpelli: Made ctools an optional recommendation instead of a dependency
|
||||
by voxpelli: Changed to use API-version in ctools exports
|
||||
|
||||
OAuth 6.x-3.0-beta2, 2010-07-18
|
||||
------------------------------
|
||||
by voxpelli: Fixed non-used default access endpoint in client
|
||||
by voxpelli: Updated the OAuth library
|
||||
#749920 by twom, voxpelli: Fixed character limits for tokens, secrets and keys
|
||||
#849832 by Jose Reyero, voxpelli: Fixed use of signatureMethod() in client
|
||||
by voxpelli: Added possibility to provide default signature method
|
||||
by voxpelli: Fixed the saving of a token when a provider reuses it
|
||||
by voxpelli: Added support for authentication realms
|
||||
by voxpelli: Changed token requests to use POST method as default
|
||||
by voxpelli: Added the possibility of defining endpoints as full URL:s
|
||||
by voxpelli: Added OAuth 1.0a support in the client
|
||||
by voxpelli: Added possibility to provide a default authentication realm
|
||||
#849832 by Jose Reyero: Fixed proper visibility for DrupalOAuthClient properties/methods
|
||||
#835824 by Schnitzel, voxpelli: Changed OAuthSignatureMethod_HMAC_SHA1 to OAuthSignatureMethod_HMAC
|
||||
by voxpelli: Changed the class for the plaintext signature method in autoload
|
||||
by voxpelli: Refactored some DrupalOAuthClient functions for consistency and flexibility
|
||||
#846744 by alex_b, voxpelli: Fixed notice when deleting user
|
||||
#846734 by alex_b: Fixed undefined variable: provider_consumer
|
||||
#773264 by toemaz: Fixed absolute url as destination in _oauth_common_authorize()
|
||||
#743638 by toemaz, voxpelli: Fixed failed upgrade oauth_common_update_6100
|
||||
|
||||
OAuth 6.x-3.0-beta1, 2010-05-20
|
||||
------------------------------
|
||||
by Hugo Wetterberg: First release under the project name "OAuth"
|
339
sites/all/modules/oauth/LICENSE.txt
Normal file
339
sites/all/modules/oauth/LICENSE.txt
Normal file
@@ -0,0 +1,339 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
5
sites/all/modules/oauth/README
Normal file
5
sites/all/modules/oauth/README
Normal file
@@ -0,0 +1,5 @@
|
||||
OAuth implements the OAuth classes for use with Drupal and acts as a support module for other modules that wish to use OAuth.
|
||||
|
||||
OAuth Client flow:
|
||||
|
||||
The callback to be used is /oauth/authorized/% where % is the id of the consumer used by the client. We need the id of the consumer to be able to find the token correctly.
|
22
sites/all/modules/oauth/css/admin.css
Normal file
22
sites/all/modules/oauth/css/admin.css
Normal file
@@ -0,0 +1,22 @@
|
||||
.oauth-common-left-panel {
|
||||
float: left;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.oauth-common-right-panel {
|
||||
float: right;
|
||||
width: 49.9%;
|
||||
}
|
||||
|
||||
.oauth-common-auth-level {
|
||||
border-bottom: 1px solid #999999;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.oauth-common-auth-level > div .form-item:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.oauth-common-auth-level .clear {
|
||||
clear: both;
|
||||
}
|
397
sites/all/modules/oauth/includes/DrupalOAuthClient.inc
Normal file
397
sites/all/modules/oauth/includes/DrupalOAuthClient.inc
Normal file
@@ -0,0 +1,397 @@
|
||||
<?php
|
||||
|
||||
class DrupalOAuthClient {
|
||||
public $version = OAUTH_COMMON_VERSION_1_RFC;
|
||||
|
||||
protected $consumer;
|
||||
protected $requestToken;
|
||||
protected $accessToken;
|
||||
protected $signatureMethod;
|
||||
|
||||
/**
|
||||
* Creates an instance of the DrupalOAuthClient.
|
||||
*
|
||||
* @param DrupalOAuthConsumer $consumer
|
||||
* The consumer.
|
||||
* @param OAuthToken $request_token
|
||||
* Optional. A request token to use.
|
||||
* @param OAuthSignatureMethod $signature_method
|
||||
* Optional. The signature method to use.
|
||||
* @param integer $version
|
||||
* Optional. The version to use - either OAUTH_COMMON_VERSION_1_RFC or OAUTH_COMMON_VERSION_1.
|
||||
*/
|
||||
public function __construct($consumer, $request_token = NULL, $signature_method = NULL, $version = NULL) {
|
||||
$this->consumer = $consumer;
|
||||
$this->requestToken = $request_token;
|
||||
$this->signatureMethod = $signature_method;
|
||||
|
||||
if ($version) {
|
||||
$this->version = $version;
|
||||
}
|
||||
|
||||
// Set to the default signature method if no method was specified
|
||||
if (!$this->signatureMethod) {
|
||||
if (!empty($this->consumer->configuration['signature_method'])) {
|
||||
$signature_method = substr(strtolower($this->consumer->configuration['signature_method']), 5);
|
||||
}
|
||||
else {
|
||||
$signature_method = 'SHA1';
|
||||
}
|
||||
$this->signatureMethod = self::signatureMethod($signature_method);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function to get signing method implementations.
|
||||
*
|
||||
* @param string $method
|
||||
* Optional. The hmac hashing algorithm to use. Defaults to 'sha512' which
|
||||
* has superseded sha1 as the recommended alternative.
|
||||
* @param bool $fallback_to_sha1
|
||||
* Optional. Whether sha1 should be used as a fallback if the selected
|
||||
* hashing algorithm is unavailable.
|
||||
* @return OAuthSignatureMethod
|
||||
* The signature method object.
|
||||
*/
|
||||
public static function signatureMethod($method = 'SHA1', $fallback_to_sha1 = TRUE) {
|
||||
$sign = NULL;
|
||||
|
||||
if (in_array(drupal_strtolower($method), hash_algos())) {
|
||||
$sign = new OAuthSignatureMethod_HMAC($method);
|
||||
}
|
||||
else if ($fallback_to_sha1) {
|
||||
$sign = new OAuthSignatureMethod_HMAC('SHA1');
|
||||
}
|
||||
|
||||
return $sign;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a request token from the provider.
|
||||
*
|
||||
* @param string $endpoint
|
||||
* Optional. The endpoint path for the provider.
|
||||
* - If you provide the full URL (e.g. "http://example.com/oauth/request_token"),
|
||||
* then it will be used.
|
||||
* - If you provide only the path (e.g. "oauth/request_token"), it will
|
||||
* be converted into a full URL by prepending the provider_url.
|
||||
* - If you provide nothing it will default to '/oauth/request_token'.
|
||||
* @param array $options
|
||||
* An associative array of additional optional options, with the following keys:
|
||||
* - 'params'
|
||||
* An associative array of parameters that should be included in the
|
||||
* request.
|
||||
* - 'realm'
|
||||
* A string to be used as the http authentication realm in the request.
|
||||
* - 'get' (default FALSE)
|
||||
* Whether to use GET as the HTTP-method instead of POST.
|
||||
* - 'callback'
|
||||
* A full URL of where the user should be sent after the request token
|
||||
* has been authorized.
|
||||
* Only used by versions higher than OAUTH_COMMON_VERSION_1.
|
||||
* @return DrupalOAuthToken
|
||||
* The returned request token.
|
||||
*/
|
||||
public function getRequestToken($endpoint = NULL, $options = array()) {
|
||||
if ($this->requestToken) {
|
||||
return clone $this->requestToken;
|
||||
}
|
||||
|
||||
$options += array(
|
||||
'params' => array(),
|
||||
'realm' => NULL,
|
||||
'get' => FALSE,
|
||||
'callback' => NULL,
|
||||
);
|
||||
|
||||
if (empty($endpoint)) {
|
||||
if (!empty($this->consumer->configuration['request_endpoint'])) {
|
||||
$endpoint = $this->consumer->configuration['request_endpoint'];
|
||||
}
|
||||
else {
|
||||
$endpoint = '/oauth/request_token';
|
||||
}
|
||||
}
|
||||
if ($this->version > OAUTH_COMMON_VERSION_1) {
|
||||
$options['params']['oauth_callback'] = $options['callback'] ? $options['callback'] : 'oob';
|
||||
}
|
||||
|
||||
$response = $this->get($endpoint, array(
|
||||
'params' => $options['params'],
|
||||
'realm' => $options['realm'],
|
||||
'get' => $options['get'],
|
||||
));
|
||||
|
||||
$params = array();
|
||||
parse_str($response, $params);
|
||||
|
||||
if (empty($params['oauth_token']) || empty($params['oauth_token_secret'])) {
|
||||
throw new Exception('No valid request token was returned');
|
||||
}
|
||||
|
||||
if ($this->version > OAUTH_COMMON_VERSION_1 && empty($params['oauth_callback_confirmed'])) {
|
||||
$this->version = OAUTH_COMMON_VERSION_1;
|
||||
}
|
||||
|
||||
$this->requestToken = new DrupalOAuthToken($params['oauth_token'], $params['oauth_token_secret'], $this->consumer, array(
|
||||
'type' => OAUTH_COMMON_TOKEN_TYPE_REQUEST,
|
||||
'version' => $this->version,
|
||||
));
|
||||
|
||||
return clone $this->requestToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs the url that the user should be sent to to authorize the
|
||||
* request token.
|
||||
*
|
||||
* @param string $endpoint
|
||||
* Optional. The endpoint path for the provider.
|
||||
* - If you provide the full URL (e.g. "http://example.com/oauth/authorize"),
|
||||
* then it will be used.
|
||||
* - If you provide only the path (e.g. "oauth/authorize"), it will
|
||||
* be converted into a full URL by prepending the provider_url.
|
||||
* - If you provide nothing it will default to '/oauth/authorize'.
|
||||
* @param array $options
|
||||
* An associative array of additional optional options, with the following keys:
|
||||
* - 'params'
|
||||
* An associative array of parameters that should be included in the
|
||||
* request.
|
||||
* - 'callback'
|
||||
* A full URL of where the user should be sent after the request token
|
||||
* has been authorized.
|
||||
* Only used by version OAUTH_COMMON_VERSION_1.
|
||||
* @return string
|
||||
* The url.
|
||||
*/
|
||||
public function getAuthorizationUrl($endpoint = NULL, $options = array()) {
|
||||
$options += array(
|
||||
'params' => array(),
|
||||
'callback' => NULL,
|
||||
);
|
||||
|
||||
if (empty($endpoint)) {
|
||||
if (!empty($this->consumer->configuration['authorization_endpoint'])) {
|
||||
$endpoint = $this->consumer->configuration['authorization_endpoint'];
|
||||
}
|
||||
else {
|
||||
$endpoint = '/oauth/authorize';
|
||||
}
|
||||
}
|
||||
if ($this->version == OAUTH_COMMON_VERSION_1 && $options['callback']) {
|
||||
$options['params']['oauth_callback'] = $options['callback'];
|
||||
}
|
||||
$options['params']['oauth_token'] = $this->requestToken->key;
|
||||
|
||||
$endpoint = $this->getAbsolutePath($endpoint);
|
||||
$append_query = strpos($endpoint, '?') === FALSE ? '?' : '&';
|
||||
return $endpoint . $append_query . http_build_query($options['params'], NULL, '&');
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the access token using the request token.
|
||||
*
|
||||
* @param string $endpoint
|
||||
* Optional. The endpoint path for the provider.
|
||||
* - If you provide the full URL (e.g. "http://example.com/oauth/access_token"),
|
||||
* then it will be used.
|
||||
* - If you provide only the path (e.g. "oauth/access_token"), it will
|
||||
* be converted into a full URL by prepending the provider_url.
|
||||
* - If you provide nothing it will default to '/oauth/access_token'.
|
||||
* @param array $options
|
||||
* An associative array of additional optional options, with the following keys:
|
||||
* - 'params'
|
||||
* An associative array of parameters that should be included in the
|
||||
* request.
|
||||
* - 'realm'
|
||||
* A string to be used as the http authentication realm in the request.
|
||||
* - 'get' (default FALSE)
|
||||
* Whether to use GET as the HTTP-method instead of POST.
|
||||
* - 'verifier'
|
||||
* A string containing a verifier for he user from the provider.
|
||||
* Only used by versions higher than OAUTH_COMMON_VERSION_1.
|
||||
* @return DrupalOAuthToken
|
||||
* The access token.
|
||||
*/
|
||||
public function getAccessToken($endpoint = NULL, $options = array()) {
|
||||
if ($this->accessToken) {
|
||||
return clone $this->accessToken;
|
||||
}
|
||||
|
||||
$options += array(
|
||||
'params' => array(),
|
||||
'realm' => NULL,
|
||||
'get' => FALSE,
|
||||
'verifier' => NULL,
|
||||
);
|
||||
|
||||
if (empty($endpoint)) {
|
||||
if (!empty($this->consumer->configuration['access_endpoint'])) {
|
||||
$endpoint = $this->consumer->configuration['access_endpoint'];
|
||||
}
|
||||
else {
|
||||
$endpoint = '/oauth/access_token';
|
||||
}
|
||||
}
|
||||
if ($this->version > OAUTH_COMMON_VERSION_1 && $options['verifier'] !== NULL) {
|
||||
$options['params']['oauth_verifier'] = $options['verifier'];
|
||||
}
|
||||
|
||||
$response = $this->get($endpoint, array(
|
||||
'token' => TRUE,
|
||||
'params' => $options['params'],
|
||||
'realm' => $options['realm'],
|
||||
'get' => $options['get'],
|
||||
));
|
||||
|
||||
$params = array();
|
||||
parse_str($response, $params);
|
||||
|
||||
if (empty($params['oauth_token']) || empty($params['oauth_token_secret'])) {
|
||||
throw new Exception('No valid access token was returned');
|
||||
}
|
||||
|
||||
// Check if we've has recieved this token previously and if so use the old one
|
||||
//TODO: Is this safe!? What if eg. multiple users are getting the same access token from the provider?
|
||||
$this->accessToken = DrupalOAuthToken::loadByKey($params['oauth_token'], $this->consumer);
|
||||
//TODO: Can a secret change even though the token doesn't? If so it needs to be changed.
|
||||
if (!$this->accessToken) {
|
||||
$this->accessToken = new DrupalOAuthToken($params['oauth_token'], $params['oauth_token_secret'], $this->consumer, array(
|
||||
'type' => OAUTH_COMMON_TOKEN_TYPE_ACCESS,
|
||||
));
|
||||
}
|
||||
|
||||
return clone $this->accessToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an OAuth request.
|
||||
*
|
||||
* @param string $path
|
||||
* The path being requested.
|
||||
* - If you provide the full URL (e.g. "http://example.com/oauth/request_token"),
|
||||
* then it will be used.
|
||||
* - If you provide only the path (e.g. "oauth/request_token"), it will
|
||||
* be converted into a full URL by prepending the provider_url.
|
||||
* @param array $options
|
||||
* An associative array of additional options, with the following keys:
|
||||
* - 'token' (default FALSE)
|
||||
* Whether a token should be used or not.
|
||||
* - 'params'
|
||||
* An associative array of parameters that should be included in the
|
||||
* request.
|
||||
* - 'realm'
|
||||
* A string to be used as the http authentication realm in the request.
|
||||
* - 'get' (default FALSE)
|
||||
* Whether to use GET as the HTTP-method instead of POST.
|
||||
* @return string
|
||||
* a string containing the response body.
|
||||
*/
|
||||
protected function get($path, $options = array()) {
|
||||
$options += array(
|
||||
'token' => FALSE,
|
||||
'params' => array(),
|
||||
'realm' => NULL,
|
||||
'get' => FALSE,
|
||||
);
|
||||
|
||||
if (empty($options['realm']) && !empty($this->consumer->configuration['authentication_realm'])) {
|
||||
$options['realm'] = $this->consumer->configuration['authentication_realm'];
|
||||
}
|
||||
|
||||
$token = $options['token'] ? $this->requestToken : NULL;
|
||||
$path = $this->getAbsolutePath($path);
|
||||
|
||||
$req = OAuthRequest::from_consumer_and_token($this->consumer, $token,
|
||||
$options['get'] ? 'GET' : 'POST', $path, $options['params']);
|
||||
$req->sign_request($this->signatureMethod, $this->consumer, $token);
|
||||
|
||||
$url = $req->get_normalized_http_url();
|
||||
$params = array();
|
||||
foreach ($req->get_parameters() as $param_key => $param_value) {
|
||||
if (substr($param_key, 0, 5) != 'oauth') {
|
||||
$params[$param_key] = $param_value;
|
||||
}
|
||||
}
|
||||
if (!empty($params)) {
|
||||
$url .= '?' . http_build_query($params);
|
||||
}
|
||||
|
||||
$headers = array(
|
||||
'Accept: application/x-www-form-urlencoded',
|
||||
$req->to_header($options['realm']),
|
||||
);
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
if (!$options['get']) {
|
||||
curl_setopt($ch, CURLOPT_POST, 1);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, '');
|
||||
}
|
||||
$oauth_version = _oauth_common_version();
|
||||
curl_setopt($ch, CURLOPT_USERAGENT, 'Drupal/' . VERSION . ' OAuth/' . $oauth_version);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
||||
curl_setopt($ch, CURLOPT_HEADER, 1);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$error = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
if ($error) {
|
||||
throw new Exception($error);
|
||||
}
|
||||
|
||||
$result = $this->interpretResponse($response);
|
||||
if ($result->responseCode != 200) {
|
||||
throw new Exception('Failed to fetch data from url "' . $path . '" (HTTP response code ' . $result->responseCode . ' ' . $result->responseMessage . '): ' . $result->body, $result->responseCode);
|
||||
}
|
||||
|
||||
return $result->body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure a path is an absolute path
|
||||
*
|
||||
* Prepends provider url if the path isn't absolute.
|
||||
*
|
||||
* @param string $path
|
||||
* The path to make absolute.
|
||||
* @return string
|
||||
* The absolute path.
|
||||
*/
|
||||
protected function getAbsolutePath($path) {
|
||||
$protocols = array(
|
||||
'http',
|
||||
'https'
|
||||
);
|
||||
$protocol = strpos($path, '://');
|
||||
$protocol = $protocol ? substr($path, 0, $protocol) : '';
|
||||
if (!in_array($protocol, $protocols)) {
|
||||
$path = $this->consumer->configuration['provider_url'] . $path;
|
||||
}
|
||||
return $path;
|
||||
}
|
||||
|
||||
protected function interpretResponse($res) {
|
||||
list($headers, $body) = preg_split('/\r\n\r\n/', $res, 2);
|
||||
|
||||
$obj = (object) array(
|
||||
'headers' => $headers,
|
||||
'body' => $body,
|
||||
);
|
||||
|
||||
$matches = array();
|
||||
if (preg_match('/HTTP\/1.\d (\d{3}) (.*)/', $headers, $matches)) {
|
||||
$obj->responseCode = trim($matches[1]);
|
||||
$obj->responseMessage = trim($matches[2]);
|
||||
|
||||
// Handle HTTP/1.1 100 Continue
|
||||
if ($obj->responseCode == 100) {
|
||||
return $this->interpretResponse($body);
|
||||
}
|
||||
}
|
||||
|
||||
return $obj;
|
||||
}
|
||||
}
|
228
sites/all/modules/oauth/includes/DrupalOAuthConsumer.inc
Normal file
228
sites/all/modules/oauth/includes/DrupalOAuthConsumer.inc
Normal file
@@ -0,0 +1,228 @@
|
||||
<?php
|
||||
|
||||
class DrupalOAuthConsumer extends OAuthConsumer {
|
||||
public $csid = 0;
|
||||
|
||||
public $uid = 0;
|
||||
public $name = '';
|
||||
public $context = '';
|
||||
public $created = 0;
|
||||
public $changed = 0;
|
||||
public $callback_url = 'oob';
|
||||
public $configuration = array();
|
||||
|
||||
public $provider_consumer = FALSE;
|
||||
public $in_database = FALSE;
|
||||
|
||||
function __construct($key, $secret, $params = array()) {
|
||||
// Backwards compatibility with 6.x-3.0-beta3
|
||||
if (is_string($params)) {
|
||||
$callback_url = $params;
|
||||
if (func_num_args() > 4) {
|
||||
$params = func_get_arg(4);
|
||||
}
|
||||
else {
|
||||
$params = array();
|
||||
}
|
||||
$params['callback_url'] = $callback_url;
|
||||
}
|
||||
|
||||
foreach ($params as $param_key => $value) {
|
||||
if (isset($this->$param_key)) {
|
||||
$this->$param_key = $value;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($this->created)) {
|
||||
$this->provider_consumer = TRUE;
|
||||
}
|
||||
|
||||
parent::__construct($key, $secret, $this->callback_url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the consumer to the database
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function write() {
|
||||
$update = !empty($this->csid);
|
||||
$primary = $update ? array('csid') : array();
|
||||
|
||||
if ($this->provider_consumer) {
|
||||
$this->changed = REQUEST_TIME;
|
||||
|
||||
$values = array(
|
||||
'consumer_key' => $this->key,
|
||||
'created' => $this->created,
|
||||
'changed' => $this->changed,
|
||||
'uid' => $this->uid,
|
||||
'name' => $this->name,
|
||||
'context' => $this->context,
|
||||
'callback_url' => $this->callback_url,
|
||||
);
|
||||
|
||||
if ($update) {
|
||||
$values['csid'] = $this->csid;
|
||||
}
|
||||
else {
|
||||
$this->created = REQUEST_TIME;
|
||||
$values['created'] = $this->created;
|
||||
}
|
||||
|
||||
$ready = drupal_write_record('oauth_common_provider_consumer', $values, $primary);
|
||||
|
||||
if (!$ready) {
|
||||
throw new OAuthException("Couldn't save consumer");
|
||||
}
|
||||
}
|
||||
|
||||
$values = array(
|
||||
'key_hash' => sha1($this->key),
|
||||
'consumer_key' => $this->key,
|
||||
'secret' => $this->secret,
|
||||
'configuration' => serialize(empty($this->configuration) ? array() : $this->configuration),
|
||||
);
|
||||
|
||||
if ($update) {
|
||||
$values['csid'] = $this->csid;
|
||||
}
|
||||
|
||||
drupal_write_record('oauth_common_consumer', $values, $primary);
|
||||
|
||||
$this->csid = $values['csid'];
|
||||
$this->in_database = TRUE;
|
||||
|
||||
if (!$update) {
|
||||
$values = array(
|
||||
'csid' => $this->csid,
|
||||
'consumer_key' => $this->key,
|
||||
);
|
||||
drupal_write_record('oauth_common_provider_consumer', $values, array('consumer_key'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the consumer from the database
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function delete() {
|
||||
self::deleteConsumer($this->csid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the consumer with the id from the database.
|
||||
*
|
||||
* @param string $csid
|
||||
* The consumer id.
|
||||
* @return void
|
||||
*/
|
||||
public static function deleteConsumer($csid) {
|
||||
//TODO: Add compatibility layer?
|
||||
$condition = db_and()->condition('csid', $csid);
|
||||
|
||||
db_delete('oauth_common_provider_token')
|
||||
->condition('tid', db_select('oauth_common_token', 't')->condition($condition)->fields('t', array('tid')), 'IN')
|
||||
->execute();
|
||||
|
||||
foreach (array('oauth_common_token', 'oauth_common_provider_consumer', 'oauth_common_consumer') as $table) {
|
||||
db_delete($table)
|
||||
->condition($condition)
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated - Gets the consumer with the specified key
|
||||
*
|
||||
* @param string $key
|
||||
* The key of the consumer to get
|
||||
* @param bool $provider_consumer
|
||||
* Optional. Whether the consumer we're about to load is a provider or
|
||||
* consumer consumer. Defaults to TRUE.
|
||||
* @return DrupalOAuthConsumer
|
||||
* The loaded consumer object or FALSE if load failed
|
||||
*/
|
||||
public static function load($key, $provider_consumer = TRUE) {
|
||||
return DrupalOAuthConsumer::loadProviderByKey($key, $provider_consumer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a provider consumer with the specified id
|
||||
*
|
||||
* @param int $id
|
||||
* The id of the consumer to get
|
||||
* @param boolean $load_provider_data
|
||||
* Whether to load provider related data or not
|
||||
* @return DrupalOAuthConsumer
|
||||
* The loaded consumer object or FALSE if load failed
|
||||
*/
|
||||
public static function loadById($csid, $load_provider_data = TRUE) {
|
||||
$query = db_select('oauth_common_consumer', 'c');
|
||||
|
||||
$query
|
||||
->condition('c.csid', $csid)
|
||||
->fields('c', array('csid', 'consumer_key', 'secret', 'configuration'));
|
||||
|
||||
if ($load_provider_data) {
|
||||
$query->leftJoin('oauth_common_provider_consumer', 'pc', 'pc.csid = c.csid');
|
||||
$query->fields('pc', array('created', 'changed', 'uid', 'name', 'context', 'callback_url'));
|
||||
}
|
||||
|
||||
return self::fromResult($query->execute());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a provider consumer with the specified key
|
||||
*
|
||||
* @param string $key
|
||||
* The key of the consumer to get
|
||||
* @param boolean $provider
|
||||
* Used internally for backwards compatibility with ::load()
|
||||
* @return DrupalOAuthConsumer
|
||||
* The loaded consumer object or FALSE if load failed
|
||||
*/
|
||||
public static function loadProviderByKey($key, $provider = TRUE) {
|
||||
$query = db_select('oauth_common_consumer', 'c');
|
||||
|
||||
$query
|
||||
->condition('c.key_hash', sha1($key))
|
||||
->fields('c', array('secret', 'configuration'));
|
||||
|
||||
if ($provider) {
|
||||
$query->join('oauth_common_provider_consumer', 'pc', 'pc.csid = c.csid');
|
||||
$query->fields('pc');
|
||||
}
|
||||
else {
|
||||
// For backwards compatibility with deprecated DrupalOAuthConsumer::load() from 6.x-3.0-beta3
|
||||
$query->leftJoin('oauth_common_provider_consumer', 'pc', 'pc.csid = c.csid');
|
||||
$query
|
||||
->fields('c', array('csid', 'consumer_key'))
|
||||
->fields('pc', array('created', 'changed', 'uid', 'name', 'context', 'callback_url'))
|
||||
->isNull('pc.csid');
|
||||
}
|
||||
|
||||
return self::fromResult($query->execute());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a consumer from a db-result resource
|
||||
*
|
||||
* @param resource $res
|
||||
* A database result resource
|
||||
* @return DrupalOAuthConsumer
|
||||
* The constructed consumer object or NULL if no rows could be read or construction failed
|
||||
*/
|
||||
public static function fromResult($res) {
|
||||
//TODO: Ensure this works with old inputs?
|
||||
if ($data = $res->fetchAssoc()) {
|
||||
if (!empty($data['configuration'])) {
|
||||
$data['configuration'] = unserialize($data['configuration']);
|
||||
}
|
||||
$data['in_database'] = TRUE;
|
||||
return new DrupalOAuthConsumer($data['consumer_key'], $data['secret'], $data);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
}
|
141
sites/all/modules/oauth/includes/DrupalOAuthDataStore.inc
Normal file
141
sites/all/modules/oauth/includes/DrupalOAuthDataStore.inc
Normal file
@@ -0,0 +1,141 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Database abstraction class
|
||||
*/
|
||||
class DrupalOAuthDataStore extends OAuthDataStore {
|
||||
private $context;
|
||||
|
||||
public function __construct($context) {
|
||||
$this->context = $context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if consumer exists from a given consumer key.
|
||||
*
|
||||
* @param string $consumer_key
|
||||
* The consumer key.
|
||||
* @param bool $provider_consumer
|
||||
* Whether the consumer should be a provider consumer
|
||||
* @return OAuthConsumer
|
||||
* The consumer
|
||||
* @throws OAuthException
|
||||
* An exception is thrown when the consumer cannot be found
|
||||
*/
|
||||
public function lookup_consumer($consumer_key, $provider_consumer = TRUE) {
|
||||
$consumer = DrupalOAuthConsumer::loadProviderByKey($consumer_key);
|
||||
if (!$consumer) {
|
||||
throw new OAuthException('Consumer not found');
|
||||
}
|
||||
return $consumer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the token exists.
|
||||
*
|
||||
* @param OAuthConsumer $consumer
|
||||
* The consumer.
|
||||
* @param string $token_type
|
||||
* The type of the token: 'request' or 'access'.
|
||||
* @param string $token
|
||||
* The token key.
|
||||
* @return DrupalOauthToken
|
||||
* The matching token
|
||||
* @throws OAuthException
|
||||
* An exception is thrown when the token cannot be found or doesn't match
|
||||
*/
|
||||
public function lookup_token($consumer, $token_type, $token) {
|
||||
$type = ($token_type == 'request' ? OAUTH_COMMON_TOKEN_TYPE_REQUEST : OAUTH_COMMON_TOKEN_TYPE_ACCESS);
|
||||
$token = DrupalOAuthToken::loadByKey($token, $consumer, $type);
|
||||
if (!$token) {
|
||||
throw new OAuthException('Token not found');
|
||||
}
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the nonce value exists. If not, generate one.
|
||||
*
|
||||
* @param OAuthConsumer $consumer
|
||||
* The service consumer information with both key
|
||||
* and secret values.
|
||||
* @param OAuthToken $token
|
||||
* The current token.
|
||||
* @param string $nonce
|
||||
* A new nonce value, in case a one doesn't current exit.
|
||||
* @param int $timestamp
|
||||
* The current time.
|
||||
* @return string
|
||||
* The existing nonce value or NULL in
|
||||
* case it doesn't exist.
|
||||
*/
|
||||
public function lookup_nonce($consumer, $token, $nonce, $timestamp) {
|
||||
if (strlen($nonce) > 255) {
|
||||
throw new OAuthException('Nonces may not be longer than 255 characters');
|
||||
}
|
||||
|
||||
$stored_nonce = db_query(
|
||||
"SELECT nonce FROM {oauth_common_nonce}
|
||||
WHERE nonce = :nonce AND timestamp <= :timestamp and token_key = :token_key", array(
|
||||
':nonce' => $nonce,
|
||||
':timestamp' => $timestamp,
|
||||
':token_key' => $token ? $token->key : '',
|
||||
));
|
||||
|
||||
if (!$stored_nonce->rowCount()) {
|
||||
$values = array(
|
||||
'nonce' => $nonce,
|
||||
'timestamp' => $timestamp,
|
||||
'token_key' => $token ? $token->key : '',
|
||||
);
|
||||
drupal_write_record('oauth_common_nonce', $values);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return $stored_nonce;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a new request token.
|
||||
*
|
||||
* @param OAuthConsumer $consumer
|
||||
* The consumer to generate a token for.
|
||||
* @return DrupalOAuthToken
|
||||
* The request token
|
||||
*/
|
||||
function new_request_token($consumer, $callback = NULL) {
|
||||
$token = new DrupalOAuthToken(user_password(32), user_password(32), $consumer, array(
|
||||
'type' => OAUTH_COMMON_TOKEN_TYPE_REQUEST,
|
||||
'uid' => 0,
|
||||
'expires' => REQUEST_TIME + variable_get('oauth_common_request_token_lifetime', 7200),
|
||||
));
|
||||
$token->write();
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a new access token and delete the old request token.
|
||||
*
|
||||
* @param DrupalOAuthToken $token_old
|
||||
* The old request token.
|
||||
* @param OAuthConsumer $consumer
|
||||
* The service consumer information.
|
||||
*/
|
||||
function new_access_token($token_old, $consumer, $verifier = NULL) {
|
||||
module_load_include('inc', 'oauth_common');
|
||||
|
||||
if ($token_old && $token_old->authorized) {
|
||||
$token_new = new DrupalOAuthToken(user_password(32), user_password(32), $consumer, array(
|
||||
'type' => OAUTH_COMMON_TOKEN_TYPE_ACCESS,
|
||||
'uid' => $token_old->uid,
|
||||
'services' => isset($token_old->services) ? $token_old->services : NULL,
|
||||
'authorized' => 1,
|
||||
));
|
||||
$token_old->delete();
|
||||
$token_new->write();
|
||||
return $token_new;
|
||||
}
|
||||
|
||||
throw new OAuthException('Invalid request token');
|
||||
}
|
||||
}
|
56
sites/all/modules/oauth/includes/DrupalOAuthRequest.inc
Normal file
56
sites/all/modules/oauth/includes/DrupalOAuthRequest.inc
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
class DrupalOAuthRequest extends OAuthRequest {
|
||||
/**
|
||||
* Creates a OAuthRequest object from the current request
|
||||
*
|
||||
* @param string $http_method
|
||||
* @param string $http_url
|
||||
* @param array $parameters
|
||||
* @return OAuthRequest
|
||||
* A OAuthRequest generated from the request
|
||||
*/
|
||||
public static function from_request($http_method = NULL, $http_url = NULL, $parameters = NULL) {
|
||||
// Preparations that has to be made if we're going to detect parameters
|
||||
if ($parameters == NULL) {
|
||||
$qs = $_SERVER['QUERY_STRING'];
|
||||
$q = $_GET['q'];
|
||||
|
||||
// Unset $_GET['q'] if it was created by a redirect
|
||||
if (isset($_SERVER['REDIRECT_URL'])) {
|
||||
$q = FALSE;
|
||||
}
|
||||
// Check that the q parameter hasn't been created or altered by drupal
|
||||
elseif (isset($_GET['q'])) {
|
||||
$get = array();
|
||||
parse_str($_SERVER['QUERY_STRING'], $get);
|
||||
// The q parameter was in the original request, make sure it hasn't been altered
|
||||
if (isset($get['q'])) {
|
||||
$q = $get['q'];
|
||||
}
|
||||
// The q parameter was set by drupal, unset it
|
||||
else {
|
||||
$q = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
$parsed = array();
|
||||
parse_str($_SERVER['QUERY_STRING'], $parsed);
|
||||
if ($q === FALSE) {
|
||||
unset($parsed['q']);
|
||||
}
|
||||
else {
|
||||
$parsed['q'] = $q;
|
||||
}
|
||||
$_SERVER['QUERY_STRING'] = http_build_query($parsed, '', '&');
|
||||
}
|
||||
$req = parent::from_request($http_method, $http_url, $parameters);
|
||||
|
||||
// Restore $_SERVER['QUERY_STRING'] if it was touched
|
||||
if (isset($qs)) {
|
||||
$_SERVER['QUERY_STRING'] = $qs;
|
||||
}
|
||||
|
||||
return $req;
|
||||
}
|
||||
}
|
27
sites/all/modules/oauth/includes/DrupalOAuthServer.inc
Normal file
27
sites/all/modules/oauth/includes/DrupalOAuthServer.inc
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
class DrupalOAuthServer extends OAuthServer {
|
||||
public function __construct($context) {
|
||||
parent::__construct(new DrupalOAuthDataStore($context));
|
||||
|
||||
if (isset($this->context->authorization_options['signature_methods'])) {
|
||||
$sig_methods = $this->context->authorization_options['signature_methods'];
|
||||
}
|
||||
else {
|
||||
$sig_methods = array('HMAC-SHA1', 'HMAC-SHA256', 'HMAC-SHA384', 'HMAC-SHA512');
|
||||
}
|
||||
|
||||
foreach ($sig_methods as $signature_method) {
|
||||
if ($signature_method == 'PLAINTEXT') {
|
||||
$this->add_signature_method(new OAuthSignatureMethod_PLAINTEXT());
|
||||
}
|
||||
else {
|
||||
// Check if the system supports the hashing algorithm
|
||||
$algo = explode('-', $signature_method, 2);
|
||||
if ($algo[0] == 'HMAC' && in_array(strtolower($algo[1]), hash_algos())) {
|
||||
$this->add_signature_method(new OAuthSignatureMethod_HMAC($algo[1]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
244
sites/all/modules/oauth/includes/DrupalOAuthToken.inc
Normal file
244
sites/all/modules/oauth/includes/DrupalOAuthToken.inc
Normal file
@@ -0,0 +1,244 @@
|
||||
<?php
|
||||
|
||||
class DrupalOAuthToken extends OAuthToken {
|
||||
public $tid = 0;
|
||||
public $expires = 0;
|
||||
public $type = OAUTH_COMMON_TOKEN_TYPE_REQUEST;
|
||||
public $uid = 0;
|
||||
|
||||
public $created = 0;
|
||||
public $changed = 0;
|
||||
public $services = array();
|
||||
public $authorized = 0;
|
||||
|
||||
public $in_database = FALSE;
|
||||
|
||||
public function __construct($key, $secret, $consumer, $params = array()) {
|
||||
foreach ($params as $param_key => $value) {
|
||||
if (isset($this->$param_key)) {
|
||||
$this->$param_key = $value;
|
||||
}
|
||||
}
|
||||
|
||||
// Backwards compatibility with 6.x-3.0-beta3
|
||||
if (empty($consumer) || is_array($consumer)) {
|
||||
if (is_array($consumer)) {
|
||||
$params = $consumer;
|
||||
}
|
||||
if (!empty($params['csid'])) {
|
||||
$consumer = DrupalOAuthConsumer::loadById($params['csid'], isset($params['services']));
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_object($consumer)) {
|
||||
throw new OAuthException("Needs an associated consumer");
|
||||
}
|
||||
else {
|
||||
$this->consumer = $consumer;
|
||||
}
|
||||
|
||||
parent::__construct($key, $secret);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the token to the database
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function write() {
|
||||
$update = !empty($this->tid);
|
||||
|
||||
$primary = $update ? array('tid') : array();
|
||||
|
||||
if ($this->consumer->provider_consumer) {
|
||||
$this->changed = REQUEST_TIME;
|
||||
|
||||
$values = array(
|
||||
'token_key' => $this->key,
|
||||
'changed' => $this->changed,
|
||||
'services' => json_encode($this->services),
|
||||
'authorized' => $this->authorized,
|
||||
);
|
||||
|
||||
if ($update) {
|
||||
$values['tid'] = $this->tid;
|
||||
}
|
||||
else {
|
||||
$this->created = REQUEST_TIME;
|
||||
$values['created'] = $this->created;
|
||||
}
|
||||
|
||||
$ready = drupal_write_record('oauth_common_provider_token', $values, $primary);
|
||||
|
||||
if (!$ready) {
|
||||
throw new OAuthException("Couldn't save token");
|
||||
}
|
||||
}
|
||||
|
||||
$values = array(
|
||||
'csid' => $this->consumer->csid,
|
||||
'key_hash' => sha1($this->key),
|
||||
'token_key' => $this->key,
|
||||
'secret' => $this->secret,
|
||||
'expires' => $this->expires,
|
||||
'type' => $this->type,
|
||||
'uid' => $this->uid,
|
||||
);
|
||||
|
||||
if ($update) {
|
||||
$values['tid'] = $this->tid;
|
||||
}
|
||||
|
||||
drupal_write_record('oauth_common_token', $values, $primary);
|
||||
|
||||
$this->tid = $values['tid'];
|
||||
$this->in_database = TRUE;
|
||||
|
||||
if (!$update) {
|
||||
$values = array(
|
||||
'tid' => $this->tid,
|
||||
'token_key' => $this->key,
|
||||
);
|
||||
drupal_write_record('oauth_common_provider_token', $values, array('token_key'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the token from the database
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function delete() {
|
||||
self::deleteToken($this->key, $this->consumer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the token with the key from the database
|
||||
*
|
||||
* @param string $key
|
||||
* The key of the token to delete.
|
||||
* @param object $consumer
|
||||
* The consumer for which to fetch a token
|
||||
* @return void
|
||||
*/
|
||||
public static function deleteToken($key, $consumer) {
|
||||
//TODO: Ensure backwards compatibility
|
||||
$condition = db_and()->condition('key_hash', sha1($key))->condition('csid', $consumer->csid);
|
||||
|
||||
db_delete('oauth_common_provider_token')
|
||||
->condition('tid', db_select('oauth_common_token', 't')->condition($condition)->fields('t', array('tid')), 'IN')
|
||||
->execute();
|
||||
|
||||
db_delete('oauth_common_token')
|
||||
->condition($condition)
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated - Gets the token with the specified key
|
||||
*
|
||||
* @param string $key
|
||||
* The key of the token to get
|
||||
* @param bool $provider_token
|
||||
* Whether the token to load is a provider token.
|
||||
* @return DrupalOAuthToken
|
||||
* The loaded token object or FALSE if load failed
|
||||
*/
|
||||
public static function load($key, $provider_token = TRUE) {
|
||||
return DrupalOAuthToken::loadByKey($key, !$provider_token, FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the token with the specified key
|
||||
*
|
||||
* @param string $key
|
||||
* The key of the token to get
|
||||
* @param boolean|object $consumer
|
||||
* The consumer for which to fetch a token or FALSE to fetch a provider token
|
||||
* @param int $type
|
||||
* Used internally for backwards compatibility with ::load()
|
||||
* @return DrupalOAuthToken
|
||||
* The loaded token object or FALSE if load failed
|
||||
*/
|
||||
public static function loadByKey($key, $consumer = FALSE, $type = OAUTH_COMMON_TOKEN_TYPE_ACCESS) {
|
||||
$query = db_select('oauth_common_token', 't');
|
||||
|
||||
$query
|
||||
->condition('t.key_hash', sha1($key))
|
||||
->fields('t');
|
||||
|
||||
// Only add if defined - needed for backwards compatibility with deprecated DrupalOAuthToken::load() from 6.x-3.0-beta3
|
||||
if ($type !== FALSE) {
|
||||
$query->condition('t.type', $type);
|
||||
}
|
||||
|
||||
if (!$consumer || is_object($consumer) && $consumer->provider_consumer) {
|
||||
$query->join('oauth_common_provider_token', 'pt', 'pt.tid = t.tid');
|
||||
$query->fields('pt', array('created', 'changed', 'services', 'authorized'));
|
||||
}
|
||||
|
||||
// Only fetch non-provider tokens - needed for backwards compatibility with deprecated DrupalOAuthToken::load() from 6.x-3.0-beta3
|
||||
if ($consumer === TRUE) {
|
||||
$query->leftJoin('oauth_common_provider_token', 'pt', 'pt.tid = t.tid');
|
||||
$query->isNull('pt.tid');
|
||||
}
|
||||
else if ($consumer) {
|
||||
$query->condition('t.csid', $consumer->csid);
|
||||
}
|
||||
|
||||
return self::fromResult($query->execute(), $consumer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the token with the specified id
|
||||
*
|
||||
* @param int $id
|
||||
* The id of the token to get
|
||||
* @param boolean $load_provider_data
|
||||
* Whether to load provider related data or not
|
||||
* @return DrupalOAuthToken
|
||||
* The loaded token object or FALSE if load failed
|
||||
*/
|
||||
public static function loadById($tid, $load_provider_data = TRUE) {
|
||||
$query = db_select('oauth_common_token', 't');
|
||||
|
||||
$query
|
||||
->condition('t.tid', $tid)
|
||||
->fields('t');
|
||||
|
||||
if ($load_provider_data) {
|
||||
$query->join('oauth_common_provider_token', 'pt', 'pt.tid = t.tid');
|
||||
$query->fields('pt', array('created', 'changed', 'services', 'authorized'));
|
||||
}
|
||||
|
||||
return self::fromResult($query->execute());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a token from a db-result resource
|
||||
*
|
||||
* @param resource $res
|
||||
* A database result resource
|
||||
* @return DrupalOAuthToken
|
||||
* The constructed token object or NULL if no rows could be read or construction failed
|
||||
*/
|
||||
public static function fromResult($res, $consumer = FALSE) {
|
||||
//TODO: Ensure this works with old inputs?
|
||||
if ($data = $res->fetchAssoc()) {
|
||||
if (isset($data['services'])) {
|
||||
$data['services'] = json_decode($data['services']);
|
||||
}
|
||||
$data['in_database'] = TRUE;
|
||||
|
||||
if (is_object($consumer) && $consumer->csid == $data['csid']) {
|
||||
$token_consumer = $consumer;
|
||||
}
|
||||
else {
|
||||
$token_consumer = DrupalOAuthConsumer::loadById($data['csid'], isset($data['services']));
|
||||
}
|
||||
|
||||
return new DrupalOAuthToken($data['token_key'], $data['secret'], $token_consumer, $data);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
}
|
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* A generic signature method implementation that leverages hash_hmac() for
|
||||
* increased flexibility.
|
||||
*/
|
||||
class OAuthSignatureMethod_HMAC extends OAuthSignatureMethod {
|
||||
private $algo = NULL;
|
||||
|
||||
/**
|
||||
* Create a HMAC oauth signature method object using the (or one of the)
|
||||
* specified algorithm implementations.
|
||||
*
|
||||
* @param mixed $algo
|
||||
* Pass the name of a algorithm supported by hash_hmac() or an array of
|
||||
* names when you have several candidate algorithms that may be used. The
|
||||
* first algorithm int the array that is supported on the system will be used.
|
||||
* @throws Exception
|
||||
* A exception is thrown when none of the provided algorithms are supported
|
||||
* by the system.
|
||||
*/
|
||||
public function __construct($algo) {
|
||||
$algos = hash_algos();
|
||||
// Create a single-element array from strings to simplify the logic of
|
||||
// support checking and failure handling.
|
||||
if (is_string($algo)) {
|
||||
$algo = array($algo);
|
||||
}
|
||||
|
||||
// Find a supported algorithm among the candidates
|
||||
foreach ($algo as $a) {
|
||||
if (in_array(strtolower($a), $algos)) {
|
||||
$this->algo = strtolower($a);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Throw a exception if no matching algorithm can be found
|
||||
if (empty($this->algo)) {
|
||||
throw new OAuthException(t('None of the suggested hash algorithms (@cand) were '
|
||||
. 'supported by the server. Try one of the following: !algos.', array(
|
||||
'@cand' => join($algo, ', '),
|
||||
'!algos' => join($algos, ', '),
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
public function get_name() {
|
||||
return "HMAC-" . strtoupper($this->algo);
|
||||
}
|
||||
|
||||
public function build_signature($request, $consumer, $token) {
|
||||
$base_string = $request->get_signature_base_string();
|
||||
$request->base_string = $base_string;
|
||||
|
||||
$key_parts = array(
|
||||
$consumer->secret,
|
||||
($token) ? $token->secret : ""
|
||||
);
|
||||
|
||||
$key_parts = OAuthUtil::urlencode_rfc3986($key_parts);
|
||||
$key = implode('&', $key_parts);
|
||||
|
||||
return base64_encode(hash_hmac($this->algo, $base_string, $key, TRUE));
|
||||
}
|
||||
}
|
3
sites/all/modules/oauth/lib/LICENCE.txt
Normal file
3
sites/all/modules/oauth/lib/LICENCE.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
The OAuth php library is originally licensed under the MIT license. And is to be found at google code: http://code.google.com/p/oauth/
|
||||
|
||||
I, the maintainer of this module, will NOT accept any patches to this library, that haven't been submitted to the original project first. This is to avoid any license hijacking, and to further the development of a common OAuth library for PHP.
|
882
sites/all/modules/oauth/lib/OAuth.php
Normal file
882
sites/all/modules/oauth/lib/OAuth.php
Normal file
@@ -0,0 +1,882 @@
|
||||
<?php
|
||||
// vim: foldmethod=marker
|
||||
|
||||
/* Generic exception class
|
||||
*/
|
||||
class OAuthException extends Exception {
|
||||
// pass
|
||||
}
|
||||
|
||||
class OAuthConsumer {
|
||||
public $key;
|
||||
public $secret;
|
||||
|
||||
function __construct($key, $secret, $callback_url=NULL) {
|
||||
$this->key = $key;
|
||||
$this->secret = $secret;
|
||||
$this->callback_url = $callback_url;
|
||||
}
|
||||
|
||||
function __toString() {
|
||||
return "OAuthConsumer[key=$this->key,secret=$this->secret]";
|
||||
}
|
||||
}
|
||||
|
||||
class OAuthToken {
|
||||
// access tokens and request tokens
|
||||
public $key;
|
||||
public $secret;
|
||||
|
||||
/**
|
||||
* key = the token
|
||||
* secret = the token secret
|
||||
*/
|
||||
function __construct($key, $secret) {
|
||||
$this->key = $key;
|
||||
$this->secret = $secret;
|
||||
}
|
||||
|
||||
/**
|
||||
* generates the basic string serialization of a token that a server
|
||||
* would respond to request_token and access_token calls with
|
||||
*/
|
||||
function to_string() {
|
||||
return "oauth_token=" .
|
||||
OAuthUtil::urlencode_rfc3986($this->key) .
|
||||
"&oauth_token_secret=" .
|
||||
OAuthUtil::urlencode_rfc3986($this->secret);
|
||||
}
|
||||
|
||||
function __toString() {
|
||||
return $this->to_string();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A class for implementing a Signature Method
|
||||
* See section 9 ("Signing Requests") in the spec
|
||||
*/
|
||||
abstract class OAuthSignatureMethod {
|
||||
/**
|
||||
* Needs to return the name of the Signature Method (ie HMAC-SHA1)
|
||||
* @return string
|
||||
*/
|
||||
abstract public function get_name();
|
||||
|
||||
/**
|
||||
* Build up the signature
|
||||
* NOTE: The output of this function MUST NOT be urlencoded.
|
||||
* the encoding is handled in OAuthRequest when the final
|
||||
* request is serialized
|
||||
* @param OAuthRequest $request
|
||||
* @param OAuthConsumer $consumer
|
||||
* @param OAuthToken $token
|
||||
* @return string
|
||||
*/
|
||||
abstract public function build_signature($request, $consumer, $token);
|
||||
|
||||
/**
|
||||
* Verifies that a given signature is correct
|
||||
* @param OAuthRequest $request
|
||||
* @param OAuthConsumer $consumer
|
||||
* @param OAuthToken $token
|
||||
* @param string $signature
|
||||
* @return bool
|
||||
*/
|
||||
public function check_signature($request, $consumer, $token, $signature) {
|
||||
$built = $this->build_signature($request, $consumer, $token);
|
||||
return $built == $signature;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The HMAC-SHA1 signature method uses the HMAC-SHA1 signature algorithm as defined in [RFC2104]
|
||||
* where the Signature Base String is the text and the key is the concatenated values (each first
|
||||
* encoded per Parameter Encoding) of the Consumer Secret and Token Secret, separated by an '&'
|
||||
* character (ASCII code 38) even if empty.
|
||||
* - Chapter 9.2 ("HMAC-SHA1")
|
||||
*/
|
||||
class OAuthSignatureMethod_HMAC_SHA1 extends OAuthSignatureMethod {
|
||||
function get_name() {
|
||||
return "HMAC-SHA1";
|
||||
}
|
||||
|
||||
public function build_signature($request, $consumer, $token) {
|
||||
$base_string = $request->get_signature_base_string();
|
||||
$request->base_string = $base_string;
|
||||
|
||||
$key_parts = array(
|
||||
$consumer->secret,
|
||||
($token) ? $token->secret : ""
|
||||
);
|
||||
|
||||
$key_parts = OAuthUtil::urlencode_rfc3986($key_parts);
|
||||
$key = implode('&', $key_parts);
|
||||
|
||||
return base64_encode(hash_hmac('sha1', $base_string, $key, true));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The PLAINTEXT method does not provide any security protection and SHOULD only be used
|
||||
* over a secure channel such as HTTPS. It does not use the Signature Base String.
|
||||
* - Chapter 9.4 ("PLAINTEXT")
|
||||
*/
|
||||
class OAuthSignatureMethod_PLAINTEXT extends OAuthSignatureMethod {
|
||||
public function get_name() {
|
||||
return "PLAINTEXT";
|
||||
}
|
||||
|
||||
/**
|
||||
* oauth_signature is set to the concatenated encoded values of the Consumer Secret and
|
||||
* Token Secret, separated by a '&' character (ASCII code 38), even if either secret is
|
||||
* empty. The result MUST be encoded again.
|
||||
* - Chapter 9.4.1 ("Generating Signatures")
|
||||
*
|
||||
* Please note that the second encoding MUST NOT happen in the SignatureMethod, as
|
||||
* OAuthRequest handles this!
|
||||
*/
|
||||
public function build_signature($request, $consumer, $token) {
|
||||
$key_parts = array(
|
||||
$consumer->secret,
|
||||
($token) ? $token->secret : ""
|
||||
);
|
||||
|
||||
$key_parts = OAuthUtil::urlencode_rfc3986($key_parts);
|
||||
$key = implode('&', $key_parts);
|
||||
$request->base_string = $key;
|
||||
|
||||
return $key;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The RSA-SHA1 signature method uses the RSASSA-PKCS1-v1_5 signature algorithm as defined in
|
||||
* [RFC3447] section 8.2 (more simply known as PKCS#1), using SHA-1 as the hash function for
|
||||
* EMSA-PKCS1-v1_5. It is assumed that the Consumer has provided its RSA public key in a
|
||||
* verified way to the Service Provider, in a manner which is beyond the scope of this
|
||||
* specification.
|
||||
* - Chapter 9.3 ("RSA-SHA1")
|
||||
*/
|
||||
abstract class OAuthSignatureMethod_RSA_SHA1 extends OAuthSignatureMethod {
|
||||
public function get_name() {
|
||||
return "RSA-SHA1";
|
||||
}
|
||||
|
||||
// Up to the SP to implement this lookup of keys. Possible ideas are:
|
||||
// (1) do a lookup in a table of trusted certs keyed off of consumer
|
||||
// (2) fetch via http using a url provided by the requester
|
||||
// (3) some sort of specific discovery code based on request
|
||||
//
|
||||
// Either way should return a string representation of the certificate
|
||||
protected abstract function fetch_public_cert(&$request);
|
||||
|
||||
// Up to the SP to implement this lookup of keys. Possible ideas are:
|
||||
// (1) do a lookup in a table of trusted certs keyed off of consumer
|
||||
//
|
||||
// Either way should return a string representation of the certificate
|
||||
protected abstract function fetch_private_cert(&$request);
|
||||
|
||||
public function build_signature($request, $consumer, $token) {
|
||||
$base_string = $request->get_signature_base_string();
|
||||
$request->base_string = $base_string;
|
||||
|
||||
// Fetch the private key cert based on the request
|
||||
$cert = $this->fetch_private_cert($request);
|
||||
|
||||
// Pull the private key ID from the certificate
|
||||
$privatekeyid = openssl_get_privatekey($cert);
|
||||
|
||||
// Sign using the key
|
||||
$ok = openssl_sign($base_string, $signature, $privatekeyid);
|
||||
|
||||
// Release the key resource
|
||||
openssl_free_key($privatekeyid);
|
||||
|
||||
return base64_encode($signature);
|
||||
}
|
||||
|
||||
public function check_signature($request, $consumer, $token, $signature) {
|
||||
$decoded_sig = base64_decode($signature);
|
||||
|
||||
$base_string = $request->get_signature_base_string();
|
||||
|
||||
// Fetch the public key cert based on the request
|
||||
$cert = $this->fetch_public_cert($request);
|
||||
|
||||
// Pull the public key ID from the certificate
|
||||
$publickeyid = openssl_get_publickey($cert);
|
||||
|
||||
// Check the computed signature against the one passed in the query
|
||||
$ok = openssl_verify($base_string, $decoded_sig, $publickeyid);
|
||||
|
||||
// Release the key resource
|
||||
openssl_free_key($publickeyid);
|
||||
|
||||
return $ok == 1;
|
||||
}
|
||||
}
|
||||
|
||||
class OAuthRequest {
|
||||
protected $parameters;
|
||||
protected $http_method;
|
||||
protected $http_url;
|
||||
// for debug purposes
|
||||
public $base_string;
|
||||
public static $version = '1.0';
|
||||
public static $POST_INPUT = 'php://input';
|
||||
|
||||
function __construct($http_method, $http_url, $parameters=NULL) {
|
||||
$parameters = ($parameters) ? $parameters : array();
|
||||
$parameters = array_merge( OAuthUtil::parse_parameters(parse_url($http_url, PHP_URL_QUERY)), $parameters);
|
||||
$this->parameters = $parameters;
|
||||
$this->http_method = $http_method;
|
||||
$this->http_url = $http_url;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* attempt to build up a request from what was passed to the server
|
||||
*/
|
||||
public static function from_request($http_method=NULL, $http_url=NULL, $parameters=NULL) {
|
||||
$scheme = (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != "on")
|
||||
? 'http'
|
||||
: 'https';
|
||||
$http_url = ($http_url) ? $http_url : $scheme .
|
||||
'://' . $_SERVER['HTTP_HOST'] .
|
||||
':' .
|
||||
$_SERVER['SERVER_PORT'] .
|
||||
$_SERVER['REQUEST_URI'];
|
||||
$http_method = ($http_method) ? $http_method : $_SERVER['REQUEST_METHOD'];
|
||||
|
||||
// We weren't handed any parameters, so let's find the ones relevant to
|
||||
// this request.
|
||||
// If you run XML-RPC or similar you should use this to provide your own
|
||||
// parsed parameter-list
|
||||
if (!$parameters) {
|
||||
// Find request headers
|
||||
$request_headers = OAuthUtil::get_headers();
|
||||
|
||||
// Parse the query-string to find GET parameters
|
||||
$parameters = OAuthUtil::parse_parameters($_SERVER['QUERY_STRING']);
|
||||
|
||||
// It's a POST request of the proper content-type, so parse POST
|
||||
// parameters and add those overriding any duplicates from GET
|
||||
if ($http_method == "POST"
|
||||
&& isset($request_headers['Content-Type'])
|
||||
&& strstr($request_headers['Content-Type'],
|
||||
'application/x-www-form-urlencoded')
|
||||
) {
|
||||
$post_data = OAuthUtil::parse_parameters(
|
||||
file_get_contents(self::$POST_INPUT)
|
||||
);
|
||||
$parameters = array_merge($parameters, $post_data);
|
||||
}
|
||||
|
||||
// We have a Authorization-header with OAuth data. Parse the header
|
||||
// and add those overriding any duplicates from GET or POST
|
||||
if (isset($request_headers['Authorization']) && substr($request_headers['Authorization'], 0, 6) == 'OAuth ') {
|
||||
$header_parameters = OAuthUtil::split_header(
|
||||
$request_headers['Authorization']
|
||||
);
|
||||
$parameters = array_merge($parameters, $header_parameters);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return new OAuthRequest($http_method, $http_url, $parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* pretty much a helper function to set up the request
|
||||
*/
|
||||
public static function from_consumer_and_token($consumer, $token, $http_method, $http_url, $parameters=NULL) {
|
||||
$parameters = ($parameters) ? $parameters : array();
|
||||
$defaults = array("oauth_version" => OAuthRequest::$version,
|
||||
"oauth_nonce" => OAuthRequest::generate_nonce(),
|
||||
"oauth_timestamp" => OAuthRequest::generate_timestamp(),
|
||||
"oauth_consumer_key" => $consumer->key);
|
||||
if ($token)
|
||||
$defaults['oauth_token'] = $token->key;
|
||||
|
||||
$parameters = array_merge($defaults, $parameters);
|
||||
|
||||
return new OAuthRequest($http_method, $http_url, $parameters);
|
||||
}
|
||||
|
||||
public function set_parameter($name, $value, $allow_duplicates = true) {
|
||||
if ($allow_duplicates && isset($this->parameters[$name])) {
|
||||
// We have already added parameter(s) with this name, so add to the list
|
||||
if (is_scalar($this->parameters[$name])) {
|
||||
// This is the first duplicate, so transform scalar (string)
|
||||
// into an array so we can add the duplicates
|
||||
$this->parameters[$name] = array($this->parameters[$name]);
|
||||
}
|
||||
|
||||
$this->parameters[$name][] = $value;
|
||||
} else {
|
||||
$this->parameters[$name] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
public function get_parameter($name) {
|
||||
return isset($this->parameters[$name]) ? $this->parameters[$name] : null;
|
||||
}
|
||||
|
||||
public function get_parameters() {
|
||||
return $this->parameters;
|
||||
}
|
||||
|
||||
public function unset_parameter($name) {
|
||||
unset($this->parameters[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* The request parameters, sorted and concatenated into a normalized string.
|
||||
* @return string
|
||||
*/
|
||||
public function get_signable_parameters() {
|
||||
// Grab all parameters
|
||||
$params = $this->parameters;
|
||||
|
||||
// Remove oauth_signature if present
|
||||
// Ref: Spec: 9.1.1 ("The oauth_signature parameter MUST be excluded.")
|
||||
if (isset($params['oauth_signature'])) {
|
||||
unset($params['oauth_signature']);
|
||||
}
|
||||
|
||||
return OAuthUtil::build_http_query($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the base string of this request
|
||||
*
|
||||
* The base string defined as the method, the url
|
||||
* and the parameters (normalized), each urlencoded
|
||||
* and the concated with &.
|
||||
*/
|
||||
public function get_signature_base_string() {
|
||||
$parts = array(
|
||||
$this->get_normalized_http_method(),
|
||||
$this->get_normalized_http_url(),
|
||||
$this->get_signable_parameters()
|
||||
);
|
||||
|
||||
$parts = OAuthUtil::urlencode_rfc3986($parts);
|
||||
|
||||
return implode('&', $parts);
|
||||
}
|
||||
|
||||
/**
|
||||
* just uppercases the http method
|
||||
*/
|
||||
public function get_normalized_http_method() {
|
||||
return strtoupper($this->http_method);
|
||||
}
|
||||
|
||||
/**
|
||||
* parses the url and rebuilds it to be
|
||||
* scheme://host/path
|
||||
*/
|
||||
public function get_normalized_http_url() {
|
||||
$parts = parse_url($this->http_url);
|
||||
|
||||
$scheme = (isset($parts['scheme'])) ? $parts['scheme'] : 'http';
|
||||
$port = (isset($parts['port'])) ? $parts['port'] : (($scheme == 'https') ? '443' : '80');
|
||||
$host = (isset($parts['host'])) ? $parts['host'] : '';
|
||||
$path = (isset($parts['path'])) ? $parts['path'] : '';
|
||||
|
||||
if (($scheme == 'https' && $port != '443')
|
||||
|| ($scheme == 'http' && $port != '80')) {
|
||||
$host = "$host:$port";
|
||||
}
|
||||
return "$scheme://$host$path";
|
||||
}
|
||||
|
||||
/**
|
||||
* builds a url usable for a GET request
|
||||
*/
|
||||
public function to_url() {
|
||||
$post_data = $this->to_postdata();
|
||||
$out = $this->get_normalized_http_url();
|
||||
if ($post_data) {
|
||||
$out .= '?'.$post_data;
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* builds the data one would send in a POST request
|
||||
*/
|
||||
public function to_postdata() {
|
||||
return OAuthUtil::build_http_query($this->parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* builds the Authorization: header
|
||||
*/
|
||||
public function to_header($realm=null) {
|
||||
$first = true;
|
||||
if($realm) {
|
||||
$out = 'Authorization: OAuth realm="' . OAuthUtil::urlencode_rfc3986($realm) . '"';
|
||||
$first = false;
|
||||
} else
|
||||
$out = 'Authorization: OAuth';
|
||||
|
||||
$total = array();
|
||||
foreach ($this->parameters as $k => $v) {
|
||||
if (substr($k, 0, 5) != "oauth") continue;
|
||||
if (is_array($v)) {
|
||||
throw new OAuthException('Arrays not supported in headers');
|
||||
}
|
||||
$out .= ($first) ? ' ' : ',';
|
||||
$out .= OAuthUtil::urlencode_rfc3986($k) .
|
||||
'="' .
|
||||
OAuthUtil::urlencode_rfc3986($v) .
|
||||
'"';
|
||||
$first = false;
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
public function __toString() {
|
||||
return $this->to_url();
|
||||
}
|
||||
|
||||
|
||||
public function sign_request($signature_method, $consumer, $token) {
|
||||
$this->set_parameter(
|
||||
"oauth_signature_method",
|
||||
$signature_method->get_name(),
|
||||
false
|
||||
);
|
||||
$signature = $this->build_signature($signature_method, $consumer, $token);
|
||||
$this->set_parameter("oauth_signature", $signature, false);
|
||||
}
|
||||
|
||||
public function build_signature($signature_method, $consumer, $token) {
|
||||
$signature = $signature_method->build_signature($this, $consumer, $token);
|
||||
return $signature;
|
||||
}
|
||||
|
||||
/**
|
||||
* util function: current timestamp
|
||||
*/
|
||||
private static function generate_timestamp() {
|
||||
return time();
|
||||
}
|
||||
|
||||
/**
|
||||
* util function: current nonce
|
||||
*/
|
||||
private static function generate_nonce() {
|
||||
$mt = microtime();
|
||||
$rand = mt_rand();
|
||||
|
||||
return md5($mt . $rand); // md5s look nicer than numbers
|
||||
}
|
||||
}
|
||||
|
||||
class OAuthServer {
|
||||
protected $timestamp_threshold = 300; // in seconds, five minutes
|
||||
protected $version = '1.0'; // hi blaine
|
||||
protected $signature_methods = array();
|
||||
|
||||
protected $data_store;
|
||||
|
||||
function __construct($data_store) {
|
||||
$this->data_store = $data_store;
|
||||
}
|
||||
|
||||
public function add_signature_method($signature_method) {
|
||||
$this->signature_methods[$signature_method->get_name()] =
|
||||
$signature_method;
|
||||
}
|
||||
|
||||
// high level functions
|
||||
|
||||
/**
|
||||
* process a request_token request
|
||||
* returns the request token on success
|
||||
*/
|
||||
public function fetch_request_token(&$request) {
|
||||
$this->get_version($request);
|
||||
|
||||
$consumer = $this->get_consumer($request);
|
||||
|
||||
// no token required for the initial token request
|
||||
$token = NULL;
|
||||
|
||||
$this->check_signature($request, $consumer, $token);
|
||||
|
||||
// Rev A change
|
||||
$callback = $request->get_parameter('oauth_callback');
|
||||
$new_token = $this->data_store->new_request_token($consumer, $callback);
|
||||
|
||||
return $new_token;
|
||||
}
|
||||
|
||||
/**
|
||||
* process an access_token request
|
||||
* returns the access token on success
|
||||
*/
|
||||
public function fetch_access_token(&$request) {
|
||||
$this->get_version($request);
|
||||
|
||||
$consumer = $this->get_consumer($request);
|
||||
|
||||
// requires authorized request token
|
||||
$token = $this->get_token($request, $consumer, "request");
|
||||
|
||||
$this->check_signature($request, $consumer, $token);
|
||||
|
||||
// Rev A change
|
||||
$verifier = $request->get_parameter('oauth_verifier');
|
||||
$new_token = $this->data_store->new_access_token($token, $consumer, $verifier);
|
||||
|
||||
return $new_token;
|
||||
}
|
||||
|
||||
/**
|
||||
* verify an api call, checks all the parameters
|
||||
*/
|
||||
public function verify_request(&$request) {
|
||||
$this->get_version($request);
|
||||
$consumer = $this->get_consumer($request);
|
||||
$token = $this->get_token($request, $consumer, "access");
|
||||
$this->check_signature($request, $consumer, $token);
|
||||
return array($consumer, $token);
|
||||
}
|
||||
|
||||
// Internals from here
|
||||
/**
|
||||
* version 1
|
||||
*/
|
||||
private function get_version(&$request) {
|
||||
$version = $request->get_parameter("oauth_version");
|
||||
if (!$version) {
|
||||
// Service Providers MUST assume the protocol version to be 1.0 if this parameter is not present.
|
||||
// Chapter 7.0 ("Accessing Protected Ressources")
|
||||
$version = '1.0';
|
||||
}
|
||||
if ($version !== $this->version) {
|
||||
throw new OAuthException("OAuth version '$version' not supported");
|
||||
}
|
||||
return $version;
|
||||
}
|
||||
|
||||
/**
|
||||
* figure out the signature with some defaults
|
||||
*/
|
||||
private function get_signature_method($request) {
|
||||
$signature_method = $request instanceof OAuthRequest
|
||||
? $request->get_parameter("oauth_signature_method")
|
||||
: NULL;
|
||||
|
||||
if (!$signature_method) {
|
||||
// According to chapter 7 ("Accessing Protected Ressources") the signature-method
|
||||
// parameter is required, and we can't just fallback to PLAINTEXT
|
||||
throw new OAuthException('No signature method parameter. This parameter is required');
|
||||
}
|
||||
|
||||
if (!in_array($signature_method,
|
||||
array_keys($this->signature_methods))) {
|
||||
throw new OAuthException(
|
||||
"Signature method '$signature_method' not supported " .
|
||||
"try one of the following: " .
|
||||
implode(", ", array_keys($this->signature_methods))
|
||||
);
|
||||
}
|
||||
return $this->signature_methods[$signature_method];
|
||||
}
|
||||
|
||||
/**
|
||||
* try to find the consumer for the provided request's consumer key
|
||||
*/
|
||||
private function get_consumer($request) {
|
||||
$consumer_key = $request instanceof OAuthRequest
|
||||
? $request->get_parameter("oauth_consumer_key")
|
||||
: NULL;
|
||||
|
||||
if (!$consumer_key) {
|
||||
throw new OAuthException("Invalid consumer key");
|
||||
}
|
||||
|
||||
$consumer = $this->data_store->lookup_consumer($consumer_key);
|
||||
if (!$consumer) {
|
||||
throw new OAuthException("Invalid consumer");
|
||||
}
|
||||
|
||||
return $consumer;
|
||||
}
|
||||
|
||||
/**
|
||||
* try to find the token for the provided request's token key
|
||||
*/
|
||||
private function get_token($request, $consumer, $token_type="access") {
|
||||
$token_field = $request instanceof OAuthRequest
|
||||
? $request->get_parameter('oauth_token')
|
||||
: NULL;
|
||||
|
||||
if (!empty($token_field)) {
|
||||
$token = $this->data_store->lookup_token(
|
||||
$consumer, $token_type, $token_field
|
||||
);
|
||||
if (!$token) {
|
||||
throw new OAuthException("Invalid $token_type token: $token_field");
|
||||
}
|
||||
}
|
||||
else {
|
||||
$token = new OAuthToken('', '');
|
||||
}
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* all-in-one function to check the signature on a request
|
||||
* should guess the signature method appropriately
|
||||
*/
|
||||
private function check_signature($request, $consumer, $token) {
|
||||
// this should probably be in a different method
|
||||
$timestamp = $request instanceof OAuthRequest
|
||||
? $request->get_parameter('oauth_timestamp')
|
||||
: NULL;
|
||||
$nonce = $request instanceof OAuthRequest
|
||||
? $request->get_parameter('oauth_nonce')
|
||||
: NULL;
|
||||
|
||||
$this->check_timestamp($timestamp);
|
||||
$this->check_nonce($consumer, $token, $nonce, $timestamp);
|
||||
|
||||
$signature_method = $this->get_signature_method($request);
|
||||
|
||||
$signature = $request->get_parameter('oauth_signature');
|
||||
$valid_sig = $signature_method->check_signature(
|
||||
$request,
|
||||
$consumer,
|
||||
$token,
|
||||
$signature
|
||||
);
|
||||
|
||||
if (!$valid_sig) {
|
||||
throw new OAuthException("Invalid signature");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* check that the timestamp is new enough
|
||||
*/
|
||||
private function check_timestamp($timestamp) {
|
||||
if( ! $timestamp )
|
||||
throw new OAuthException(
|
||||
'Missing timestamp parameter. The parameter is required'
|
||||
);
|
||||
|
||||
// verify that timestamp is recentish
|
||||
$now = time();
|
||||
if (abs($now - $timestamp) > $this->timestamp_threshold) {
|
||||
throw new OAuthException(
|
||||
"Expired timestamp, yours $timestamp, ours $now"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* check that the nonce is not repeated
|
||||
*/
|
||||
private function check_nonce($consumer, $token, $nonce, $timestamp) {
|
||||
if( ! $nonce )
|
||||
throw new OAuthException(
|
||||
'Missing nonce parameter. The parameter is required'
|
||||
);
|
||||
|
||||
// verify that the nonce is uniqueish
|
||||
$found = $this->data_store->lookup_nonce(
|
||||
$consumer,
|
||||
$token,
|
||||
$nonce,
|
||||
$timestamp
|
||||
);
|
||||
if ($found) {
|
||||
throw new OAuthException("Nonce already used: $nonce");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class OAuthDataStore {
|
||||
function lookup_consumer($consumer_key) {
|
||||
// implement me
|
||||
}
|
||||
|
||||
function lookup_token($consumer, $token_type, $token) {
|
||||
// implement me
|
||||
}
|
||||
|
||||
function lookup_nonce($consumer, $token, $nonce, $timestamp) {
|
||||
// implement me
|
||||
}
|
||||
|
||||
function new_request_token($consumer, $callback = null) {
|
||||
// return a new token attached to this consumer
|
||||
}
|
||||
|
||||
function new_access_token($token, $consumer, $verifier = null) {
|
||||
// return a new access token attached to this consumer
|
||||
// for the user associated with this token if the request token
|
||||
// is authorized
|
||||
// should also invalidate the request token
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class OAuthUtil {
|
||||
public static function urlencode_rfc3986($input) {
|
||||
if (is_array($input)) {
|
||||
return array_map(array('OAuthUtil', 'urlencode_rfc3986'), $input);
|
||||
} else if (is_scalar($input)) {
|
||||
return str_replace(
|
||||
'+',
|
||||
' ',
|
||||
str_replace('%7E', '~', rawurlencode($input))
|
||||
);
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// This decode function isn't taking into consideration the above
|
||||
// modifications to the encoding process. However, this method doesn't
|
||||
// seem to be used anywhere so leaving it as is.
|
||||
public static function urldecode_rfc3986($string) {
|
||||
return urldecode($string);
|
||||
}
|
||||
|
||||
// Utility function for turning the Authorization: header into
|
||||
// parameters, has to do some unescaping
|
||||
// Can filter out any non-oauth parameters if needed (default behaviour)
|
||||
// May 28th, 2010 - method updated to tjerk.meesters for a speed improvement.
|
||||
// see http://code.google.com/p/oauth/issues/detail?id=163
|
||||
public static function split_header($header, $only_allow_oauth_parameters = true) {
|
||||
$params = array();
|
||||
if (preg_match_all('/('.($only_allow_oauth_parameters ? 'oauth_' : '').'[a-z_-]*)=(:?"([^"]*)"|([^,]*))/', $header, $matches)) {
|
||||
foreach ($matches[1] as $i => $h) {
|
||||
$params[$h] = OAuthUtil::urldecode_rfc3986(empty($matches[3][$i]) ? $matches[4][$i] : $matches[3][$i]);
|
||||
}
|
||||
if (isset($params['realm'])) {
|
||||
unset($params['realm']);
|
||||
}
|
||||
}
|
||||
return $params;
|
||||
}
|
||||
|
||||
// helper to try to sort out headers for people who aren't running apache
|
||||
public static function get_headers() {
|
||||
if (function_exists('apache_request_headers')) {
|
||||
// we need this to get the actual Authorization: header
|
||||
// because apache tends to tell us it doesn't exist
|
||||
$headers = apache_request_headers();
|
||||
|
||||
// sanitize the output of apache_request_headers because
|
||||
// we always want the keys to be Cased-Like-This and arh()
|
||||
// returns the headers in the same case as they are in the
|
||||
// request
|
||||
$out = array();
|
||||
foreach ($headers AS $key => $value) {
|
||||
$key = str_replace(
|
||||
" ",
|
||||
"-",
|
||||
ucwords(strtolower(str_replace("-", " ", $key)))
|
||||
);
|
||||
$out[$key] = $value;
|
||||
}
|
||||
} else {
|
||||
// otherwise we don't have apache and are just going to have to hope
|
||||
// that $_SERVER actually contains what we need
|
||||
$out = array();
|
||||
if( isset($_SERVER['CONTENT_TYPE']) )
|
||||
$out['Content-Type'] = $_SERVER['CONTENT_TYPE'];
|
||||
if( isset($_ENV['CONTENT_TYPE']) )
|
||||
$out['Content-Type'] = $_ENV['CONTENT_TYPE'];
|
||||
|
||||
foreach ($_SERVER as $key => $value) {
|
||||
if (substr($key, 0, 5) == "HTTP_") {
|
||||
// this is chaos, basically it is just there to capitalize the first
|
||||
// letter of every word that is not an initial HTTP and strip HTTP
|
||||
// code from przemek
|
||||
$key = str_replace(
|
||||
" ",
|
||||
"-",
|
||||
ucwords(strtolower(str_replace("_", " ", substr($key, 5))))
|
||||
);
|
||||
$out[$key] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
// This function takes a input like a=b&a=c&d=e and returns the parsed
|
||||
// parameters like this
|
||||
// array('a' => array('b','c'), 'd' => 'e')
|
||||
public static function parse_parameters( $input ) {
|
||||
if (!isset($input) || !$input) return array();
|
||||
|
||||
$pairs = explode('&', $input);
|
||||
|
||||
$parsed_parameters = array();
|
||||
foreach ($pairs as $pair) {
|
||||
$split = explode('=', $pair, 2);
|
||||
$parameter = OAuthUtil::urldecode_rfc3986($split[0]);
|
||||
$value = isset($split[1]) ? OAuthUtil::urldecode_rfc3986($split[1]) : '';
|
||||
|
||||
if (isset($parsed_parameters[$parameter])) {
|
||||
// We have already recieved parameter(s) with this name, so add to the list
|
||||
// of parameters with this name
|
||||
|
||||
if (is_scalar($parsed_parameters[$parameter])) {
|
||||
// This is the first duplicate, so transform scalar (string) into an array
|
||||
// so we can add the duplicates
|
||||
$parsed_parameters[$parameter] = array($parsed_parameters[$parameter]);
|
||||
}
|
||||
|
||||
$parsed_parameters[$parameter][] = $value;
|
||||
} else {
|
||||
$parsed_parameters[$parameter] = $value;
|
||||
}
|
||||
}
|
||||
return $parsed_parameters;
|
||||
}
|
||||
|
||||
public static function build_http_query($params) {
|
||||
if (!$params) return '';
|
||||
|
||||
// Urlencode both keys and values
|
||||
$keys = OAuthUtil::urlencode_rfc3986(array_keys($params));
|
||||
$values = OAuthUtil::urlencode_rfc3986(array_values($params));
|
||||
$params = array_combine($keys, $values);
|
||||
|
||||
// Parameters are sorted by name, using lexicographical byte value ordering.
|
||||
// Ref: Spec: 9.1.1 (1)
|
||||
uksort($params, 'strcmp');
|
||||
|
||||
$pairs = array();
|
||||
foreach ($params as $parameter => $value) {
|
||||
if (is_array($value)) {
|
||||
// If two or more parameters share the same name, they are sorted by their value
|
||||
// Ref: Spec: 9.1.1 (1)
|
||||
// June 12th, 2010 - changed to sort because of issue 164 by hidetaka
|
||||
sort($value, SORT_STRING);
|
||||
foreach ($value as $duplicate_value) {
|
||||
$pairs[] = $parameter . '=' . $duplicate_value;
|
||||
}
|
||||
} else {
|
||||
$pairs[] = $parameter . '=' . $value;
|
||||
}
|
||||
}
|
||||
// For each parameter, the name is separated from the corresponding value by an '=' character (ASCII code 61)
|
||||
// Each name-value pair is separated by an '&' character (ASCII code 38)
|
||||
return implode('&', $pairs);
|
||||
}
|
||||
}
|
574
sites/all/modules/oauth/oauth_common.admin.inc
Normal file
574
sites/all/modules/oauth/oauth_common.admin.inc
Normal file
@@ -0,0 +1,574 @@
|
||||
<?php
|
||||
|
||||
function _oauth_common_admin() {
|
||||
$form = array();
|
||||
|
||||
$form['oauth_common_enable_provider'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Enable the oauth provider'),
|
||||
'#default_value' => variable_get('oauth_common_enable_provider', TRUE),
|
||||
'#description' => t('This option controls whether this site should act as a OAuth provider or not'),
|
||||
);
|
||||
|
||||
$form['oauth_common_request_token_lifetime'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Request token lifetime (in seconds)'),
|
||||
'#default_value' => variable_get('oauth_common_request_token_lifetime', 7200),
|
||||
);
|
||||
|
||||
$form['#validate'][] = '_oauth_common_admin_settings_validate';
|
||||
|
||||
return system_settings_form($form);
|
||||
}
|
||||
|
||||
function _oauth_common_admin_settings_validate($form, $form_state) {
|
||||
$values = $form_state['values'];
|
||||
|
||||
$lifetime = intval($values['oauth_common_request_token_lifetime'], 10);
|
||||
if (!$lifetime) {
|
||||
form_set_error('oauth_common_request_token_lifetime', t('The request token lifetime must be a non-zero integer value.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Output a list of contexts.
|
||||
*/
|
||||
function oauth_common_list_context($js = NULL) {
|
||||
$header = array(
|
||||
array('data' => t('Title'), 'class' => array('oauth-common-contexts-title')),
|
||||
array('data' => t('Storage'), 'class' => array('oauth-common-contexts-storage')),
|
||||
array('data' => t('Operations'), 'class' => array('oauth-common-contexts-operations')),
|
||||
);
|
||||
|
||||
$contexts = oauth_common_context_load_all();
|
||||
$rows = array();
|
||||
|
||||
if (!$contexts) {
|
||||
$contexts = array();
|
||||
}
|
||||
|
||||
foreach ($contexts as $context) {
|
||||
$operations = array();
|
||||
|
||||
if (empty($context->disabled)) {
|
||||
$operations[] = array(
|
||||
'title' => t('Edit'),
|
||||
'href' => 'admin/config/services/oauth/' . $context->name . '/edit',
|
||||
);
|
||||
$operations[] = array(
|
||||
'title' => t('Export'),
|
||||
'href' => 'admin/config/services/oauth/' . $context->name . '/export',
|
||||
);
|
||||
}
|
||||
|
||||
if ($context->export_type == (EXPORT_IN_CODE | EXPORT_IN_DATABASE)) {
|
||||
$operations[] = array(
|
||||
'title' => t('Revert'),
|
||||
'href' => 'admin/config/services/oauth/' . $context->name . '/delete',
|
||||
);
|
||||
}
|
||||
elseif ($context->export_type != EXPORT_IN_CODE) {
|
||||
$operations[] = array(
|
||||
'title' => t('Delete'),
|
||||
'href' => 'admin/config/services/oauth/' . $context->name . '/delete',
|
||||
);
|
||||
}
|
||||
elseif (empty($context->disabled)) {
|
||||
$operations[] = array(
|
||||
'title' => t('Disable'),
|
||||
'href' => 'admin/config/services/oauth/' . $context->name . '/disable',
|
||||
'query' => drupal_get_destination(),
|
||||
);
|
||||
}
|
||||
else {
|
||||
$operations[] = array(
|
||||
'title' => t('Enable'),
|
||||
'href' => 'admin/config/services/oauth/' . $context->name . '/enable',
|
||||
'query' => drupal_get_destination(),
|
||||
);
|
||||
}
|
||||
|
||||
$rows[$context->name] = array(
|
||||
'data' => array(
|
||||
'title' => array(
|
||||
'data' => check_plain($context->title),
|
||||
'class' => array('oauth-common-contexts-title'),
|
||||
),
|
||||
'storage' => array(
|
||||
'data' => ($context->export_type == EXPORT_IN_CODE) ? t('In code') : t('In database'),
|
||||
'class' => array('oauth-common-contexts-storage'),
|
||||
),
|
||||
'operations' => array(
|
||||
'data' => theme('links', array('links' => $operations)),
|
||||
'class' => array('oauth-common-contexts-operations'),
|
||||
),
|
||||
),
|
||||
'class' => array('oauth-common-contexts-' . $context->name)
|
||||
);
|
||||
|
||||
if (!empty($context->disabled)) {
|
||||
$rows[$context->name]['class'][] = 'oauth-common-contexts-disabled';
|
||||
}
|
||||
}
|
||||
|
||||
$table = theme('table', array(
|
||||
'header' => $header,
|
||||
'rows' => $rows,
|
||||
'attributes' => array('id' => 'oauth-common-list-contexts')
|
||||
));
|
||||
|
||||
return $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the add context page.
|
||||
*/
|
||||
function oauth_common_add_context() {
|
||||
$context = oauth_common_context_new();
|
||||
drupal_set_title(t('Add context'));
|
||||
if (!$context) {
|
||||
drupal_set_message(t("Can't create contexts, check that you've installed !ctools.", array(
|
||||
'!ctools' => l('Chaos tool suite', 'http://drupal.org/project/ctools'),
|
||||
)), 'error');
|
||||
$result = '';
|
||||
}
|
||||
else {
|
||||
$result = oauth_common_edit_context($context);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit an context.
|
||||
*
|
||||
* Called from both the add and edit points to provide for common flow.
|
||||
*/
|
||||
function oauth_common_edit_context($context) {
|
||||
if (!is_object($context)) {
|
||||
$context = oauth_common_context_load($context);
|
||||
}
|
||||
if ($context && !empty($context->title)) {
|
||||
drupal_set_title($context->title);
|
||||
}
|
||||
return drupal_get_form('oauth_common_edit_form_context', $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Form to edit the settings of an context.
|
||||
*/
|
||||
function oauth_common_edit_form_context($form, &$form_state, $context) {
|
||||
$form['#attached']['css'] = array(
|
||||
drupal_get_path('module', 'oauth_common') . '/css/admin.css'
|
||||
);
|
||||
|
||||
$form['cid'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => isset($context->cid) ? $context->cid : '',
|
||||
);
|
||||
|
||||
$form['context_object'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $context,
|
||||
);
|
||||
|
||||
$form['title'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#size' => 24,
|
||||
'#maxlength' => 100,
|
||||
'#default_value' => $context->title,
|
||||
'#title' => t('Context title'),
|
||||
'#required' => TRUE,
|
||||
);
|
||||
|
||||
$form['name'] = array(
|
||||
'#type' => 'machine_name',
|
||||
'#size' => 24,
|
||||
'#maxlength' => 32,
|
||||
'#default_value' => $context->name,
|
||||
'#title' => t('Context name'),
|
||||
'#description' => t('A unique name used to identify this preset internally. It must be only be alpha characters and underscores. No spaces, numbers or uppercase characters.'),
|
||||
'#machine_name' => array(
|
||||
'source' => array('title'),
|
||||
'exists' => 'oauth_common_edit_form_context_exists',
|
||||
),
|
||||
'#required' => TRUE,
|
||||
);
|
||||
|
||||
$sign_methods = array(
|
||||
'PLAINTEXT' => t('Plaintext'),
|
||||
);
|
||||
foreach (hash_algos() as $algo) {
|
||||
$sign_methods['HMAC-' . strtoupper($algo)] = 'HMAC-' . strtoupper($algo);
|
||||
}
|
||||
|
||||
$form['signature_methods'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Signature methods'),
|
||||
'#collapsible' => TRUE,
|
||||
'#collapsed' => TRUE,
|
||||
'#tree' => TRUE,
|
||||
'selected' => array(
|
||||
'#type' => 'checkboxes',
|
||||
'#title' => t('Supported signature methods'),
|
||||
'#options' => $sign_methods,
|
||||
'#default_value' => !empty($context->authorization_options['signature_methods']) ?
|
||||
$context->authorization_options['signature_methods'] :
|
||||
array('HMAC-SHA1', 'HMAC-SHA256', 'HMAC-SHA384', 'HMAC-SHA512'),
|
||||
)
|
||||
);
|
||||
|
||||
$form['authorization_options'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Authorization options'),
|
||||
'#tree' => TRUE,
|
||||
'#collapsible' => TRUE,
|
||||
'#collapsed' => TRUE,
|
||||
);
|
||||
|
||||
$form['authorization_options']['page_title'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Page title'),
|
||||
'#description' => t('The title of the authorization page.'),
|
||||
'#size' => 40,
|
||||
'#maxlength' => 255,
|
||||
'#default_value' => empty($context->authorization_options['page_title']) ? '' : $context->authorization_options['page_title'],
|
||||
);
|
||||
|
||||
$form['authorization_options']['message'] = array(
|
||||
'#type' => 'textarea',
|
||||
'#title' => t('Message'),
|
||||
'#description' => t('The message shown to the user when authorizing.'),
|
||||
'#default_value' => empty($context->authorization_options['message']) ? '' : $context->authorization_options['message'],
|
||||
);
|
||||
|
||||
$form['authorization_options']['warning'] = array(
|
||||
'#type' => 'textarea',
|
||||
'#title' => t('Warning'),
|
||||
'#description' => t('The warning shown to the user when authorizing.'),
|
||||
'#default_value' => empty($context->authorization_options['warning']) ? '' : $context->authorization_options['warning'],
|
||||
);
|
||||
|
||||
$form['authorization_options']['deny_access_title'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Deny access title'),
|
||||
'#description' => t('The title of deny access link.'),
|
||||
'#size' => 40,
|
||||
'#maxlength' => 255,
|
||||
'#default_value' => empty($context->authorization_options['deny_access_title']) ? '' : $context->authorization_options['deny_access_title'],
|
||||
);
|
||||
|
||||
$form['authorization_options']['grant_access_title'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Grant access title'),
|
||||
'#description' => t('The title of grant access button.'),
|
||||
'#size' => 40,
|
||||
'#maxlength' => 255,
|
||||
'#default_value' => empty($context->authorization_options['grant_access_title']) ? '' : $context->authorization_options['grant_access_title'],
|
||||
);
|
||||
|
||||
$form['authorization_options']['access_token_lifetime'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Access token lifetime'),
|
||||
'#description' => t('The time, in seconds, for which an access token should be valid, use 0 to never expire access tokens.'),
|
||||
'#size' => 10,
|
||||
'#maxlength' => 255,
|
||||
'#default_value' => empty($context->authorization_options['access_token_lifetime']) ? 0 : $context->authorization_options['access_token_lifetime'],
|
||||
);
|
||||
|
||||
$form['authorization_options']['disable_auth_level_selection'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Disable authorization level selection'),
|
||||
'#description' => t('If this is checked the user won\'t be able to choose the authorization level, and the default authorization level(s) will be used.'),
|
||||
'#default_value' => !empty($context->authorization_options['disable_auth_level_selection']),
|
||||
);
|
||||
|
||||
$form['authorization_levels'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Authorization levels'),
|
||||
'#tree' => TRUE,
|
||||
'#prefix' => '<div id="auth-level-wrapper">',
|
||||
'#suffix' => '</div>',
|
||||
'add_authorization_level' => array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Add authorization level'),
|
||||
'#weight' => 10,
|
||||
'#submit' => array('oauth_common_edit_form_auth_level_ajax_add'),
|
||||
'#limit_validation_errors' => array(),
|
||||
'#ajax' => array(
|
||||
'callback' => 'oauth_common_edit_form_auth_level_ajax_callback',
|
||||
'wrapper' => 'auth-level-wrapper',
|
||||
)
|
||||
),
|
||||
);
|
||||
|
||||
foreach ($context->authorization_levels as $name => $level) {
|
||||
$title = !empty($name) ? check_plain($name) : t('New level');
|
||||
if ($title == '*') {
|
||||
$title = t('Full access');
|
||||
}
|
||||
$l = oauth_common_edit_form_auth_level($context, $title, $name, $level);
|
||||
$form['authorization_levels'][] = $l;
|
||||
}
|
||||
|
||||
if (!isset($form_state['authorization_level_new'])) {
|
||||
$form_state['authorization_level_new'] = 0;
|
||||
}
|
||||
for ($i = 0; $i < $form_state['authorization_level_new']; $i++) {
|
||||
$form['authorization_levels'][] = oauth_common_edit_form_auth_level($context, t('Authorization level'));
|
||||
}
|
||||
|
||||
$form['actions'] = array('#type' => 'actions');
|
||||
$form['actions']['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Save'),
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for constructing a auth level fieldset.
|
||||
*
|
||||
* @param object $context
|
||||
* @param int $idx
|
||||
* @param string $title
|
||||
* @param string $name
|
||||
* @param array $level
|
||||
* @return array.
|
||||
*/
|
||||
function oauth_common_edit_form_auth_level($context, $title, $name = '', $level = array()) {
|
||||
$level = $level + array(
|
||||
'title' => '',
|
||||
'description' => '',
|
||||
);
|
||||
|
||||
$element = array(
|
||||
"name" => array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Name'),
|
||||
'#description' => t('The name of the authorization level.'),
|
||||
'#size' => 40,
|
||||
'#maxlength' => 32,
|
||||
'#default_value' => $name,
|
||||
),
|
||||
"title" => array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Title'),
|
||||
'#description' => t('The title of the authorization level.'),
|
||||
'#size' => 40,
|
||||
'#maxlength' => 100,
|
||||
'#default_value' => $level['title'],
|
||||
),
|
||||
"default" => array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Selected by default'),
|
||||
'#description' => t('Whether the authentication level should be checked by default.'),
|
||||
'#default_value' => is_array($context->authorization_options['default_authorization_levels']) && in_array($name, $context->authorization_options['default_authorization_levels']),
|
||||
),
|
||||
"delete" => array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Delete'),
|
||||
'#description' => t('Check this to delete the authorization level.'),
|
||||
'#default_value' => FALSE,
|
||||
),
|
||||
"description" => array(
|
||||
'#type' => 'textarea',
|
||||
'#title' => t('Description'),
|
||||
'#description' => t('The description of the authorization level.'),
|
||||
'#default_value' => $level['description'],
|
||||
),
|
||||
);
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit handler for adding auth levels
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function oauth_common_edit_form_auth_level_ajax_add($form, &$form_state) {
|
||||
$form_state['authorization_level_new']++;
|
||||
$form_state['rebuild'] = TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX callback for dealing with auth levels
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function oauth_common_edit_form_auth_level_ajax_callback($form, $form_state) {
|
||||
return $form['authorization_levels'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a given context exists.
|
||||
*
|
||||
* @param $name the name parameter for the context.
|
||||
* @return boolean
|
||||
*/
|
||||
function oauth_common_edit_form_context_exists($name) {
|
||||
$exists = FALSE;
|
||||
|
||||
if (!empty($name)) {
|
||||
$result = db_query('SELECT cid FROM {oauth_common_context} WHERE name = :name', array(
|
||||
':name' => $name
|
||||
))->fetchField();
|
||||
$exists = $result ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
return $exists;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate submission of the preset edit form.
|
||||
*/
|
||||
function oauth_common_edit_form_context_validate(&$form, &$form_state) {
|
||||
$values = $form_state['values'];
|
||||
|
||||
// Check that the authorization level names are unique within the context
|
||||
$levels = array();
|
||||
$default_exists = FALSE;
|
||||
foreach ($values['authorization_levels'] as $key => $level) {
|
||||
if (is_numeric($key) && !empty($level['name']) && !$level['delete']) {
|
||||
if (!empty($levels[$level['name']])) {
|
||||
form_error($form['authorization_levels'][$key]['name'], t('Authorization level name must be unique.'));
|
||||
}
|
||||
else if (preg_match("/[^A-Za-z0-9_\*]/", $level['name'])) {
|
||||
form_error($form['authorization_levels'][$key]['name'], t('Authorization level name must be alphanumeric or underscores only.'));
|
||||
}
|
||||
if (empty($level['title'])) {
|
||||
form_error($form['authorization_levels'][$key]['title'], t('Authorization levels must have a title.'));
|
||||
}
|
||||
$default_exists = $default_exists || $level['default'];
|
||||
$levels[$level['name']] = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
// Check that we actually got a number as access token lifetime
|
||||
if (!is_numeric($values['authorization_options']['access_token_lifetime'])) {
|
||||
form_error($form['authorization_options']['access_token_lifetime'], t('The access token lifetime must be numeric.'));
|
||||
}
|
||||
|
||||
// Check that at least one default authorization level is checked when
|
||||
// authorization level selection is disabled.
|
||||
if (!$default_exists && $values['authorization_options']['disable_auth_level_selection']) {
|
||||
form_error($form['authorization_options']['disable_auth_level_selection'], t('You must select at least one default authorirization level if level selection is disabled.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process submission of the mini panel edit form.
|
||||
*/
|
||||
function oauth_common_edit_form_context_submit($form, &$form_state) {
|
||||
$context = $form_state['values']['context_object'];
|
||||
$values = $form_state['values'];
|
||||
|
||||
$context->name = $values['name'];
|
||||
$context->title = $values['title'];
|
||||
|
||||
$auth_options = array(
|
||||
'access_token_lifetime' => 0,
|
||||
);
|
||||
|
||||
foreach ($values['authorization_options'] as $key => $value) {
|
||||
$auth_options[$key] = empty($value) ? null : $value;
|
||||
}
|
||||
$context->authorization_options = $auth_options;
|
||||
|
||||
// Collect the names of the selected signature methods.
|
||||
$sig_options = array();
|
||||
foreach ($values['signature_methods']['selected'] as $name => $selected) {
|
||||
if ($selected) {
|
||||
$sig_options[] = $name;
|
||||
}
|
||||
}
|
||||
$context->authorization_options['signature_methods'] = $sig_options;
|
||||
|
||||
// Set the auth levels and default levels for the context
|
||||
$levels = array();
|
||||
$default_levels = array();
|
||||
foreach ($values['authorization_levels'] as $key => $level) {
|
||||
if (is_numeric($key) && !empty($level['name']) && !$level['delete']) {
|
||||
$name = $level['name'];
|
||||
if ($level['default']) {
|
||||
$default_levels[] = $name;
|
||||
}
|
||||
$levels[$name] = $level;
|
||||
}
|
||||
}
|
||||
$context->authorization_levels = $levels;
|
||||
$context->authorization_options['default_authorization_levels'] = $default_levels;
|
||||
|
||||
oauth_common_context_save($context);
|
||||
|
||||
if (empty($context->cid)) {
|
||||
drupal_set_message(t('Your new context %title has been saved.', array('%title' => $context->title)));
|
||||
$form_state['values']['cid'] = $context->cid;
|
||||
}
|
||||
else {
|
||||
drupal_set_message(t('Your changes have been saved.'));
|
||||
}
|
||||
|
||||
$form_state['redirect'] = 'admin/config/services/oauth';
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a form to confirm deletion of a context.
|
||||
*/
|
||||
function oauth_common_delete_confirm_context($form, &$form_state, $context) {
|
||||
if (!is_object($context)) {
|
||||
$context = oauth_common_context_load($context);
|
||||
}
|
||||
if ($context->export_type == (EXPORT_IN_CODE | EXPORT_IN_DATABASE)) {
|
||||
$title = t('Are you sure you want to revert the context "@title"?', array('@title' => $context->title));
|
||||
$submit = t('Revert');
|
||||
}
|
||||
elseif ($context->export_type != EXPORT_IN_CODE) {
|
||||
$title = t('Are you sure you want to delete the context "@title"?', array('@title' => $context->title));
|
||||
$submit = t('Delete');
|
||||
}
|
||||
else {
|
||||
drupal_not_found();
|
||||
die;
|
||||
}
|
||||
$form['context'] = array('#type' => 'value', '#value' => $context->name);
|
||||
$form['cid'] = array('#type' => 'value', '#value' => $context->cid);
|
||||
return confirm_form($form,
|
||||
$title,
|
||||
!empty($_GET['destination']) ? $_GET['destination'] : 'admin/config/services/oauth',
|
||||
t('This action cannot be undone.'),
|
||||
$submit, t('Cancel')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the submit button to delete a context.
|
||||
*/
|
||||
function oauth_common_delete_confirm_context_submit($form, &$form_state) {
|
||||
$context = oauth_common_context_load($form_state['values']['context']);
|
||||
if ($context->cid == $form_state['values']['cid']) {
|
||||
oauth_common_context_delete($context);
|
||||
$form_state['redirect'] = 'admin/config/services/oauth';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Page callback to export a context to PHP code.
|
||||
*/
|
||||
function oauth_common_export_context($form, &$form_state, $context) {
|
||||
if (!is_object($context)) {
|
||||
$context = oauth_common_context_load($context);
|
||||
}
|
||||
drupal_set_title($context->title);
|
||||
$code = oauth_common_context_export($context);
|
||||
|
||||
$lines = substr_count($code, "\n") + 4;
|
||||
$form['code'] = array(
|
||||
'#type' => 'textarea',
|
||||
'#title' => $context->title,
|
||||
'#default_value' => $code,
|
||||
'#rows' => $lines,
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
289
sites/all/modules/oauth/oauth_common.authorizations.inc
Normal file
289
sites/all/modules/oauth/oauth_common.authorizations.inc
Normal file
@@ -0,0 +1,289 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Functions related to a user's authorization section
|
||||
*/
|
||||
|
||||
function oauth_common_page_user_authorizations($account) {
|
||||
$header = array(
|
||||
array('data' => t('Application'), 'class' => array("oauth-common-authorization-application")),
|
||||
array('data' => t('Key'), 'class' => array("oauth-common-authorization-key")),
|
||||
array('data' => t('Created'), 'class' => array("oauth-common-authorization-created")),
|
||||
array('data' => t('Expires'), 'class' => array("oauth-common-authorization-expires")),
|
||||
array('data' => t('Operations'), 'class' => array("oauth-common-authorization-operations")),
|
||||
);
|
||||
|
||||
$access_tokens = oauth_common_get_user_provider_tokens($account->uid);
|
||||
$rows = array();
|
||||
|
||||
foreach ($access_tokens as $token) {
|
||||
$consumer = $token->consumer;
|
||||
|
||||
$data = array(
|
||||
'application' => array(
|
||||
'data' => check_plain($consumer->name),
|
||||
'class' => array("oauth-common-authorization-application"),
|
||||
),
|
||||
'key' => array(
|
||||
'data' => substr($token->key, 0, 6) . '...',
|
||||
'class' => array("oauth-common-authorization-key"),
|
||||
),
|
||||
'created' => array(
|
||||
'data' => format_date($token->created),
|
||||
'class' => array("oauth-common-authorization-created"),
|
||||
),
|
||||
);
|
||||
$operations = array();
|
||||
|
||||
$operations[] = array(
|
||||
'title' => t('Edit'),
|
||||
'href' => sprintf('user/%d/oauth/authorizations/%s', $account->uid, $token->tid),
|
||||
'query' => array('destination' => $_GET['q']),
|
||||
);
|
||||
|
||||
$operations[] = array(
|
||||
'title' => t('Delete'),
|
||||
'href' => sprintf('user/%d/oauth/authorizations/%s', $account->uid, $token->tid) . '/delete',
|
||||
'query' => array('destination' => $_GET['q']),
|
||||
);
|
||||
|
||||
$data['expires'] = array(
|
||||
'data' => $token->expires ? format_date($token->expires) : t('Never'),
|
||||
'class' => array("oauth-common-authorization-expires"),
|
||||
);
|
||||
|
||||
$rows[] = array(
|
||||
'data' => $data + array(
|
||||
'operations' => array(
|
||||
'data' => theme('links', array('links' => $operations)),
|
||||
'class' => array("oauth-common-authorization-operations"),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
$table = theme('table', array(
|
||||
'header' => $header,
|
||||
'rows' => $rows,
|
||||
'attributes' => array('id' => 'oauth-common-list-authorization')
|
||||
));
|
||||
|
||||
return $table;
|
||||
}
|
||||
|
||||
function oauth_common_authorization_add($consumer) {
|
||||
$token = new DrupalOAuthToken(user_password(32), user_password(32), $consumer, array(
|
||||
'uid' => $account->uid,
|
||||
));
|
||||
return drupal_get_form('oauth_common_form_authorization', $token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a form to edit and add authorizations.
|
||||
*
|
||||
* Despite what appears above, this function is actually invoked by
|
||||
* `drupal_retrieve_form` (by way of `drupal_build_form`, by way of
|
||||
* `drupal_get_form`), so the second argument isn't the token, but a reference
|
||||
* to the form state. Luckily, PHP made that incredibly non-obvious by
|
||||
* neglecting to notify me that this function was being called with one too few
|
||||
* arguments. Go team.
|
||||
*/
|
||||
function oauth_common_form_authorization($form_id, &$form_state, $token) {
|
||||
$form = array();
|
||||
|
||||
$consumer = $token->consumer;
|
||||
$context = oauth_common_context_load($consumer->context);
|
||||
|
||||
drupal_set_title(t('Authorization for @app', array('@app' => $consumer->name)), PASS_THROUGH);
|
||||
|
||||
$form['token_object'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $token,
|
||||
);
|
||||
|
||||
$form['authorized'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Authorized'),
|
||||
'#default_value' => $token->authorized,
|
||||
);
|
||||
|
||||
$form['created'] = array(
|
||||
'#type' => 'item',
|
||||
'#title' => t('Created'),
|
||||
'#markup' => format_date($token->created),
|
||||
);
|
||||
|
||||
$form['changed'] = array(
|
||||
'#type' => 'item',
|
||||
'#title' => t('Changed'),
|
||||
'#markup' => format_date($token->changed),
|
||||
);
|
||||
|
||||
$form['key'] = array(
|
||||
'#type' => 'item',
|
||||
'#title' => t('Key'),
|
||||
'#markup' => $token->key,
|
||||
);
|
||||
|
||||
if ($token->in_database) {
|
||||
$form['secret'] = array(
|
||||
'#type' => 'item',
|
||||
'#prefix' => '<div id="token-secret-wrapper">',
|
||||
'#title' => t('Secret'),
|
||||
'#markup' => substr($token->secret, 0, 6) . '...',
|
||||
);
|
||||
|
||||
$form['show_secret'] = array(
|
||||
'#type' => 'button',
|
||||
'#value' => t('Show secret'),
|
||||
'#ajax' => array(
|
||||
'callback' => 'oauth_common_form_authorization_secret_ajax_callback',
|
||||
'wrapper' => 'token-secret-wrapper',
|
||||
),
|
||||
'#suffix' => '</div>',
|
||||
);
|
||||
}
|
||||
else {
|
||||
$form['secret'] = array(
|
||||
'#type' => 'item',
|
||||
'#title' => t('Secret'),
|
||||
'#markup' => $token->secret
|
||||
);
|
||||
}
|
||||
|
||||
$form['allowed'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Permissions'),
|
||||
);
|
||||
|
||||
global $user;
|
||||
oauth_common_permissions_form($user, $form['allowed'], $consumer, $context, $token->services);
|
||||
|
||||
$form['actions'] = array('#type' => 'actions');
|
||||
$form['actions']['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Save'),
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX callback for showing secrets
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function oauth_common_form_authorization_secret_ajax_callback($form, $form_state) {
|
||||
$form['secret']['#markup'] = $form_state['values']['token_object']->secret;
|
||||
$form['secret']['#prefix'] = '';
|
||||
return $form['secret'];
|
||||
}
|
||||
|
||||
function oauth_common_permissions_form($account, &$form, $consumer, $context, $default_services = array('*')) {
|
||||
$tvars = array(
|
||||
'@appname' => $consumer->name,
|
||||
'@user' => $account->name,
|
||||
'@sitename' => variable_get('site_name', ''),
|
||||
);
|
||||
|
||||
if ($context) {
|
||||
foreach ($context->authorization_levels as $name => $level) {
|
||||
$auth_opt = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t($level['title'], $tvars),
|
||||
'#description' => t($level['description'], $tvars),
|
||||
'#default_value' => in_array($name, $default_services),
|
||||
);
|
||||
$form['authorization']['levels'][$name] = $auth_opt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function oauth_common_form_authorization_submit($form, &$form_state) {
|
||||
$values = $form_state['values'];
|
||||
$token = $values['token_object'];
|
||||
$consumer = $token->consumer;
|
||||
|
||||
// Collect the authorization levels
|
||||
if (isset($values['levels'])) {
|
||||
$token->services = array_keys(array_filter($values['levels']));
|
||||
}
|
||||
|
||||
$token->authorized = $values['authorized'];
|
||||
$token->write(TRUE);
|
||||
|
||||
drupal_set_message(t('The @consumer token @token was updated.', array(
|
||||
'@consumer' => $consumer->name,
|
||||
'@token' => $token->key)));
|
||||
|
||||
drupal_goto(sprintf('user/%d/applications', $token->uid));
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a form for deleting an authorization.
|
||||
*
|
||||
* We've got the same symptom here that we do with
|
||||
* `oauth_common_form_authorization`, i.e., this is actually called by
|
||||
* `drupal_retrieve_form`.
|
||||
*/
|
||||
function oauth_common_form_authorization_delete($form_id, &$form_state, $user, $token) {
|
||||
$consumer = $token->consumer;
|
||||
|
||||
$cancel_url = 'user/%d/oauth/authorizations';
|
||||
|
||||
if (!empty($_GET['destination'])) {
|
||||
$cancel_url = $_GET['destination'];
|
||||
}
|
||||
|
||||
drupal_set_title(t('Deleting authorization for "@consumer"', array(
|
||||
'@consumer' => $consumer->name,
|
||||
)), PASS_THROUGH);
|
||||
|
||||
$form = array(
|
||||
'token_object' => array(
|
||||
'#type' => 'value',
|
||||
'#value' => $token,
|
||||
),
|
||||
);
|
||||
|
||||
$form['user'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $user->uid,
|
||||
);
|
||||
|
||||
$form['key'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $token->key,
|
||||
);
|
||||
|
||||
$form['description'] = array(
|
||||
'#type' => 'item',
|
||||
'#markup' => t('Are you sure that you want to delete the authorization for "@consumer"?', array(
|
||||
'@consumer' => $consumer->name,
|
||||
)),
|
||||
);
|
||||
|
||||
$form['actions'] = array('#type' => 'actions');
|
||||
|
||||
$form['actions']['cancel'] = array(
|
||||
'#markup' => l(t('Cancel'), sprintf($cancel_url, $user->uid, $token->key)),
|
||||
);
|
||||
|
||||
$form['actions']['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Delete'),
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
function oauth_common_form_authorization_delete_submit($form, &$form_state) {
|
||||
$values = $form_state['values'];
|
||||
$token = $values['token_object'];
|
||||
$consumer = $token->consumer;
|
||||
$token->delete();
|
||||
drupal_set_message(t('The @consumer token @token was deleted.', array(
|
||||
'@consumer' => $consumer->name,
|
||||
'@token' => $token->key)));
|
||||
}
|
259
sites/all/modules/oauth/oauth_common.consumers.inc
Normal file
259
sites/all/modules/oauth/oauth_common.consumers.inc
Normal file
@@ -0,0 +1,259 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Menu system callback for listing a users consumers.
|
||||
*
|
||||
* @param object $account
|
||||
*/
|
||||
function oauth_common_page_user_consumers($account) {
|
||||
module_load_include('inc', 'oauth_common');
|
||||
|
||||
$ci = oauth_common_user_consumers($account->uid);
|
||||
$header = array(
|
||||
array('data' => t('Name'), 'class' => array('oauth-common-consumer-name')),
|
||||
array('data' => t('Key'), 'class' => array('oauth-common-consumer-key')),
|
||||
array('data' => t('Created'), 'class' => array('oauth-common-consumer-created')),
|
||||
array('data' => t('Operations'), 'class' => array('oauth-common-consumer-operations')),
|
||||
);
|
||||
$rows = array();
|
||||
|
||||
foreach ($ci as $consumer) {
|
||||
$data = array(
|
||||
'name' => array(
|
||||
'data' => $consumer->name,
|
||||
'class' => array('oauth-common-consumer-name'),
|
||||
),
|
||||
'key' => array(
|
||||
'data' => substr($consumer->key, 0, 6) . '...',
|
||||
'class' => array('oauth-common-consumer-key'),
|
||||
),
|
||||
'created' => array(
|
||||
'data' => format_date($consumer->created),
|
||||
'class' => array('oauth-common-consumer-created'),
|
||||
),
|
||||
);
|
||||
$operations = array();
|
||||
|
||||
if (oauth_common_can_edit_consumer($consumer)) {
|
||||
$operations[] = array(
|
||||
'title' => t('Edit'),
|
||||
'href' => sprintf('user/%d/oauth/consumer/%s', $account->uid, $consumer->csid),
|
||||
);
|
||||
$operations[] = array(
|
||||
'title' => t('Delete'),
|
||||
'href' => sprintf('user/%d/oauth/consumer/%s', $account->uid, $consumer->csid) . '/delete',
|
||||
);
|
||||
}
|
||||
|
||||
$rows[] = array(
|
||||
'data' => $data + array(
|
||||
'operations' => array(
|
||||
'data' => theme('links', array('links' => $operations)),
|
||||
'class' => array('oauth-common-consumer-operations'),
|
||||
),
|
||||
),
|
||||
'class' => array('oauth-common-consumer'),
|
||||
);
|
||||
}
|
||||
|
||||
$table = theme('table', array(
|
||||
'header' => $header,
|
||||
'rows' => $rows,
|
||||
'attributes' => array('id' => 'oauth-common-list-consumers')
|
||||
));
|
||||
|
||||
return $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu system callback for the add consumer page.
|
||||
*/
|
||||
function oauth_common_add_consumer($account) {
|
||||
$consumer = new DrupalOAuthConsumer(user_password(32), user_password(32), array(
|
||||
'callback_url' => '',
|
||||
'uid' => $account->uid,
|
||||
'provider_consumer' => TRUE,
|
||||
));
|
||||
return drupal_get_form('oauth_common_form_consumer', $consumer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu system callback for the edit consumer page.
|
||||
*/
|
||||
function oauth_common_edit_consumer($consumer) {
|
||||
return drupal_get_form('oauth_common_form_consumer', $consumer);
|
||||
}
|
||||
|
||||
/**
|
||||
* The consumer form that is shared by the add and edit page.
|
||||
*/
|
||||
function oauth_common_form_consumer($form, &$form_state, $consumer) {
|
||||
$form = array();
|
||||
|
||||
$form['consumer_object'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $consumer,
|
||||
);
|
||||
|
||||
$form['name'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Consumer name'),
|
||||
'#required' => TRUE,
|
||||
'#default_value' => $consumer->name,
|
||||
);
|
||||
|
||||
$form['callback_url'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Callback url'),
|
||||
'#required' => FALSE,
|
||||
'#default_value' => $consumer->callback_url,
|
||||
);
|
||||
|
||||
if ($consumer->in_database) {
|
||||
$contexts = oauth_common_context_list();
|
||||
$form['context'] = array(
|
||||
'#type' => 'item',
|
||||
'#title' => t('Application context'),
|
||||
'#markup' => isset($contexts[$consumer->context]) ? $contexts[$consumer->context] : $consumer->context,
|
||||
);
|
||||
}
|
||||
else {
|
||||
$allowed_contexts = array();
|
||||
foreach (oauth_common_context_list() as $context => $title) {
|
||||
if (user_access(sprintf('oauth register consumers in %s', $context))) {
|
||||
$allowed_contexts[$context] = $title;
|
||||
}
|
||||
}
|
||||
|
||||
$form['context'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Application context'),
|
||||
'#options' => $allowed_contexts,
|
||||
'#default_value' => $consumer->context,
|
||||
);
|
||||
}
|
||||
|
||||
if ($consumer->in_database) {
|
||||
$form['key'] = array(
|
||||
'#type' => 'item',
|
||||
'#title' => t('Key'),
|
||||
'#markup' => $consumer->key,
|
||||
);
|
||||
|
||||
$form['secret'] = array(
|
||||
'#type' => 'item',
|
||||
'#prefix' => '<div id="consumer-secret-wrapper">',
|
||||
'#title' => t('Secret'),
|
||||
'#markup' => substr($consumer->secret, 0, 6) . '...',
|
||||
);
|
||||
|
||||
$form['show_secret'] = array(
|
||||
'#type' => 'button',
|
||||
'#value' => t('Show secret'),
|
||||
'#ajax' => array(
|
||||
'callback' => 'oauth_common_form_consumer_secret_ajax_callback',
|
||||
'wrapper' => 'consumer-secret-wrapper',
|
||||
),
|
||||
'#suffix' => '</div>',
|
||||
);
|
||||
}
|
||||
|
||||
$form['actions'] = array('#type' => 'actions');
|
||||
$form['actions']['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Save'),
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX callback for showing secrets
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function oauth_common_form_consumer_secret_ajax_callback($form, $form_state) {
|
||||
$form['secret']['#markup'] = $form_state['values']['consumer_object']->secret;
|
||||
$form['secret']['#prefix'] = '';
|
||||
return $form['secret'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit handler for oauth_common_form_consumer.
|
||||
*/
|
||||
function oauth_common_form_consumer_submit($form, &$form_state) {
|
||||
$values = $form_state['values'];
|
||||
$consumer = $values['consumer_object'];
|
||||
|
||||
// Translate empty callback url to oob (out of band).
|
||||
if (empty($values['callback_url'])) {
|
||||
$values['callback_url'] = 'oob';
|
||||
}
|
||||
// Add scheme if missing, and if the callback_url isn't out of band.
|
||||
else if ($values['callback_url'] != 'oob' && preg_match('/^http:\/\/|https:\/\//', $values['callback_url']) === 0) {
|
||||
//TODO: What about custom callback url:s used by eg iphone-apps? We should allow them - right?
|
||||
$values['callback_url'] = 'http://' . $values['callback_url'];
|
||||
}
|
||||
|
||||
// Remove trailing slash
|
||||
$values['callback_url'] = rtrim($values['callback_url'], '/');
|
||||
|
||||
// Transfer editable attributes to the consumer.
|
||||
$names = array('name', 'callback_url', 'context');
|
||||
foreach ($names as $name) {
|
||||
if (isset($values[$name])) {
|
||||
$consumer->$name = $values[$name];
|
||||
}
|
||||
}
|
||||
|
||||
// Update or create the consumer.
|
||||
$update = $consumer->in_database;
|
||||
$consumer->write();
|
||||
|
||||
if ($update) {
|
||||
drupal_set_message(t('Updated the consumer @name', array('@name' => $values['name'])));
|
||||
}
|
||||
else {
|
||||
drupal_set_message(t('Added the consumer @name', array('@name' => $values['name'])));
|
||||
}
|
||||
|
||||
drupal_goto(sprintf('user/%d/oauth/consumers', $consumer->uid));
|
||||
}
|
||||
|
||||
/**
|
||||
* Consumer deletion form.
|
||||
*/
|
||||
function oauth_common_form_consumer_delete($form_state, $consumer) {
|
||||
$form = array(
|
||||
'consumer_object' => array(
|
||||
'#type' => 'value',
|
||||
'#value' => $consumer,
|
||||
),
|
||||
'confirm' => array(
|
||||
'#type' => 'item',
|
||||
'#markup' => t('Are you sure you want to delete application <strong>@a</strong>?', array('@a' => $consumer->name)),
|
||||
),
|
||||
'actions' => array(
|
||||
'#type' => 'actions',
|
||||
'delete' => array(
|
||||
'#type' => 'submit',
|
||||
'#title' => t('Delete'),
|
||||
'#default_value' => t('Delete'),
|
||||
)
|
||||
),
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit handler for oauth_common_form_consumer_delete.
|
||||
*/
|
||||
function oauth_common_form_consumer_delete_submit($form, &$form_state) {
|
||||
$consumer = $form_state['values']['consumer_object'];
|
||||
|
||||
$consumer->delete();
|
||||
drupal_set_message(t('Deleted the consumer @name', array('@name' => $consumer->name)));
|
||||
|
||||
drupal_goto(sprintf('user/%d/oauth/consumers', $consumer->uid));
|
||||
}
|
97
sites/all/modules/oauth/oauth_common.inc
Normal file
97
sites/all/modules/oauth/oauth_common.inc
Normal file
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Return information about consumers related to a user.
|
||||
*
|
||||
* @param int $uid
|
||||
* User ID to retrieve consumer info for.
|
||||
* @return array
|
||||
* An array of consumer info
|
||||
*/
|
||||
function oauth_common_user_consumers($uid) {
|
||||
$result = db_query('SELECT c.secret, c.configuration, pc.*
|
||||
FROM {oauth_common_consumer} c
|
||||
INNER JOIN {oauth_common_provider_consumer} pc ON pc.csid = c.csid
|
||||
WHERE pc.uid = :uid', array(':uid' => $uid));
|
||||
|
||||
$consumers = array();
|
||||
while ($consumer = DrupalOAuthConsumer::fromResult($result)) {
|
||||
$consumers[] = $consumer;
|
||||
}
|
||||
|
||||
return $consumers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return information about access tokens related to a user.
|
||||
*
|
||||
* @param int $uid
|
||||
* User ID to retrieve consumer info for.
|
||||
* @return array
|
||||
* An array of consumer info
|
||||
*/
|
||||
function oauth_common_user_access_tokens($uid) {
|
||||
$result = db_query("SELECT * FROM {oauth_common_token} WHERE uid = :uid AND type = :type", array(
|
||||
':uid' => $uid,
|
||||
':type' => OAUTH_COMMON_TOKEN_TYPE_ACCESS,
|
||||
));
|
||||
$tokens = array();
|
||||
while ($token = DrupalOAuthToken::fromResult($result)) {
|
||||
$tokens[] = $token;
|
||||
}
|
||||
return $tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the request
|
||||
*
|
||||
* @return array
|
||||
* An array containing three elements. The first is TRUE if the request was
|
||||
* signed, otherwise FALSE. Then comes the validated consumer and token objects.
|
||||
*/
|
||||
function oauth_common_verify_request() {
|
||||
$req = DrupalOAuthRequest::from_request();
|
||||
// Verify
|
||||
$consumer_key = $req->get_parameter('oauth_consumer_key');
|
||||
if (!empty($consumer_key)) {
|
||||
$consumer = DrupalOAuthConsumer::loadProviderByKey($consumer_key);
|
||||
if ($consumer) {
|
||||
$context = oauth_common_context_load($consumer->context);
|
||||
|
||||
if (!$context) {
|
||||
throw new Exception('No OAuth context found');
|
||||
}
|
||||
|
||||
_oauth_common_verify_body_hash($req);
|
||||
|
||||
// Only verify request if we got a signature
|
||||
$signature = $req->get_parameter('oauth_signature');
|
||||
if (!empty($signature)) {
|
||||
$server = new DrupalOAuthServer($context);
|
||||
return array_merge(array(TRUE), $server->verify_request($req));
|
||||
}
|
||||
else {
|
||||
$token_key = $req->get_parameter('oauth_token');
|
||||
if (empty($token_key) || !($token = DrupalOAuthToken::loadbyKey($token_key, $consumer))) {
|
||||
$token = NULL;
|
||||
}
|
||||
return array(FALSE, $consumer, $token);
|
||||
}
|
||||
}
|
||||
}
|
||||
return array(FALSE, NULL, NULL);
|
||||
}
|
||||
|
||||
function _oauth_common_verify_body_hash($req) {
|
||||
$body_hash = $req->get_parameter('oauth_body_hash');
|
||||
if ($body_hash && module_exists('inputstream')) {
|
||||
$hres = hash_init('sha1');
|
||||
$stream = fopen('drupal://input', 'r');
|
||||
hash_update_stream($hres, $stream);
|
||||
fclose($stream);
|
||||
$sha1 = hash_final($hres, TRUE);
|
||||
if ($sha1 !== base64_decode($body_hash)) {
|
||||
throw new OAuthException("Invalid body hash");
|
||||
}
|
||||
}
|
||||
}
|
25
sites/all/modules/oauth/oauth_common.info
Normal file
25
sites/all/modules/oauth/oauth_common.info
Normal file
@@ -0,0 +1,25 @@
|
||||
name = OAuth
|
||||
description = Provides OAuth functionality
|
||||
configure = admin/config/services/oauth
|
||||
package = "OAuth"
|
||||
|
||||
recommends[] = ctools
|
||||
suggests[] = inputstream
|
||||
|
||||
core = 7.x
|
||||
|
||||
files[] = lib/OAuth.php
|
||||
files[] = includes/DrupalOAuthServer.inc
|
||||
files[] = includes/DrupalOAuthDataStore.inc
|
||||
files[] = includes/DrupalOAuthRequest.inc
|
||||
files[] = includes/DrupalOAuthToken.inc
|
||||
files[] = includes/DrupalOAuthConsumer.inc
|
||||
files[] = includes/DrupalOAuthClient.inc
|
||||
files[] = includes/OAuthSignatureMethod_HMAC.inc
|
||||
|
||||
; Information added by drupal.org packaging script on 2012-02-08
|
||||
version = "7.x-3.0"
|
||||
core = "7.x"
|
||||
project = "oauth"
|
||||
datestamp = "1328724350"
|
||||
|
360
sites/all/modules/oauth/oauth_common.install
Normal file
360
sites/all/modules/oauth/oauth_common.install
Normal file
@@ -0,0 +1,360 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Installation and schema related functions for the OAuth module
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_schema().
|
||||
*/
|
||||
function oauth_common_schema() {
|
||||
$schema = array();
|
||||
|
||||
//TODO: Schemas shouldn't be translated
|
||||
|
||||
$schema['oauth_common_context'] = array(
|
||||
'description' => 'Stores contexts for OAuth common',
|
||||
'export' => array(
|
||||
'identifier' => 'context',
|
||||
'export callback' => 'oauth_common_context_export',
|
||||
'list callback' => 'oauth_common_context_list',
|
||||
'key' => 'name',
|
||||
'api' => array(
|
||||
'owner' => 'oauth_common',
|
||||
'api' => 'oauth',
|
||||
'minimum_version' => 1,
|
||||
'current_version' => 1,
|
||||
),
|
||||
),
|
||||
'fields' => array(
|
||||
'cid' => array(
|
||||
'type' => 'serial',
|
||||
'description' => 'Primary ID field for the table. Not used for anything except internal lookups.',
|
||||
'not null' => TRUE,
|
||||
'no export' => TRUE,
|
||||
),
|
||||
'name' => array(
|
||||
'description' => 'The computer-readable name of the context.',
|
||||
'type' => 'varchar',
|
||||
'length' => 32,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'title' => array(
|
||||
'description' => 'The localizable title of the authorization context.',
|
||||
'type' => 'varchar',
|
||||
'length' => 100,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'authorization_options' => array(
|
||||
'description' => 'Authorization options.',
|
||||
'type' => 'text',
|
||||
'size' => 'big',
|
||||
'not null' => TRUE,
|
||||
'serialize' => TRUE,
|
||||
'object default' => array(),
|
||||
),
|
||||
'authorization_levels' => array(
|
||||
'description' => 'Authorization levels for the context.',
|
||||
'type' => 'text',
|
||||
'size' => 'big',
|
||||
'not null' => TRUE,
|
||||
'serialize' => TRUE,
|
||||
'object default' => array(),
|
||||
),
|
||||
),
|
||||
'primary key' => array('cid'),
|
||||
'unique keys' => array(
|
||||
'context' => array('name'),
|
||||
),
|
||||
);
|
||||
|
||||
$schema['oauth_common_consumer'] = _oauth_common_consumer_schema();
|
||||
$schema['oauth_common_provider_consumer'] = _oauth_common_provider_consumer_schema();
|
||||
$schema['oauth_common_token'] = _oauth_common_token_schema();
|
||||
$schema['oauth_common_provider_token'] = _oauth_common_provider_token_schema();
|
||||
|
||||
$schema['oauth_common_nonce'] = array(
|
||||
'description' => 'Stores timestamp against nonce for repeat attacks.',
|
||||
'fields' => array(
|
||||
'nonce' => array(
|
||||
'description' => 'The random string used on each request.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE
|
||||
),
|
||||
'timestamp' => array(
|
||||
'description' => 'The timestamp of the request.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE
|
||||
),
|
||||
'token_key' => array(
|
||||
'description' => 'Token key.',
|
||||
// This is our own internal key - it's 0 or 32 characters long
|
||||
'type' => 'varchar',
|
||||
'length' => 32,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
),
|
||||
'primary key' => array('nonce'),
|
||||
'indexes' => array(
|
||||
'timekey' => array('timestamp', 'token_key'),
|
||||
),
|
||||
);
|
||||
|
||||
return $schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains the consumer schema - used by oauth_common_schema() as well as latest related update function
|
||||
*/
|
||||
function _oauth_common_consumer_schema() {
|
||||
return array(
|
||||
'description' => 'Keys and secrets for OAuth consumers, both those provided by this site and other sites.',
|
||||
'fields' => array(
|
||||
'csid' => array(
|
||||
'type' => 'serial',
|
||||
'description' => 'Primary ID field for the table. Not used for anything except internal lookups.',
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'key_hash' => array(
|
||||
'description' => 'SHA1-hash of consumer_key.',
|
||||
'type' => 'char',
|
||||
'length' => 40,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
// Key is a reserved word in MySQL so lets avoid that
|
||||
'consumer_key' => array(
|
||||
'description' => 'Consumer key.',
|
||||
'type' => 'text',
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'secret' => array(
|
||||
'description' => 'Consumer secret.',
|
||||
'type' => 'text',
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'configuration' => array(
|
||||
'description' => 'Consumer configuration',
|
||||
'type' => 'text',
|
||||
'serialized' => TRUE,
|
||||
'size' => 'big',
|
||||
'not null' => TRUE,
|
||||
'object default' => array(),
|
||||
),
|
||||
),
|
||||
'primary key' => array('csid'),
|
||||
'indexes' => array(
|
||||
'key_hash' => array('key_hash'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains the provider consumer schema - used by oauth_common_schema() as well as latest related update function
|
||||
*/
|
||||
function _oauth_common_provider_consumer_schema() {
|
||||
return array(
|
||||
'description' => 'Additional data for OAuth consumers provided by this site.',
|
||||
'fields' => array(
|
||||
'csid' => array(
|
||||
'description' => 'The {oauth_common_consumer}.csid this data is related to.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'default' => 0
|
||||
),
|
||||
'consumer_key' => array(
|
||||
'description' => 'Consumer key.',
|
||||
// This is our own internal key - it's always 32 characters long
|
||||
'type' => 'char',
|
||||
'length' => 32,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'created' => array(
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'description' => 'The time that the consumer was created, as a Unix timestamp.',
|
||||
),
|
||||
'changed' => array(
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'description' => 'The last time the consumer was edited, as a Unix timestamp.',
|
||||
),
|
||||
'uid' => array(
|
||||
'description' => 'The application owner.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'name' => array(
|
||||
'description' => 'The application name.',
|
||||
'type' => 'varchar',
|
||||
'length' => 128,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'context' => array(
|
||||
'description' => 'The application context.',
|
||||
'type' => 'varchar',
|
||||
'length' => 32,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'callback_url' => array(
|
||||
'description' => 'Callback url.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
),
|
||||
'primary key' => array('consumer_key'),
|
||||
'unique keys' => array(
|
||||
'csid' => array('csid'),
|
||||
),
|
||||
'indexes' => array(
|
||||
'uid' => array('uid'),
|
||||
),
|
||||
'foreign keys' => array(
|
||||
'oauth_common_consumer' => array(
|
||||
'table' => 'oauth_common_consumer',
|
||||
'columns' => array('csid' => 'csid')
|
||||
),
|
||||
'users' => array(
|
||||
'table' => 'users',
|
||||
'columns' => array('uid' => 'uid'),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains the token schema - used by oauth_common_schema() as well as latest related update function
|
||||
*/
|
||||
function _oauth_common_token_schema() {
|
||||
return array(
|
||||
'description' => 'Tokens stored on behalf of providers or consumers for request and services accesses.',
|
||||
'fields' => array(
|
||||
'tid' => array(
|
||||
'type' => 'serial',
|
||||
'description' => 'Primary ID field for the table. Not used for anything except internal lookups.',
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'csid' => array(
|
||||
'description' => 'The {oauth_common_consumer}.csid this token is related to.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0
|
||||
),
|
||||
'key_hash' => array(
|
||||
'description' => 'SHA1-hash of token_key.',
|
||||
'type' => 'char',
|
||||
'length' => 40,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
// Key is a reserved word in MySQL so lets avoid that
|
||||
'token_key' => array(
|
||||
'description' => 'Token key.',
|
||||
'type' => 'text',
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'secret' => array(
|
||||
'description' => 'Token secret.',
|
||||
'type' => 'text',
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'expires' => array(
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'description' => 'The expiry time for the token, as a Unix timestamp.',
|
||||
),
|
||||
'type' => array(
|
||||
'description' => 'Token type: request or access.',
|
||||
'type' => 'int',
|
||||
'size' => 'tiny',
|
||||
'not null' => TRUE,
|
||||
'default' => 1, //OAUTH_COMMON_TOKEN_TYPE_ACCESS
|
||||
),
|
||||
'uid' => array(
|
||||
'description' => 'User ID from {user}.uid.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
),
|
||||
'primary key' => array('tid'),
|
||||
'indexes' => array(
|
||||
'key_hash' => array('key_hash'),
|
||||
),
|
||||
'foreign keys' => array(
|
||||
'oauth_common_consumer' => array(
|
||||
'table' => 'oauth_common_consumer',
|
||||
'columns' => array('csid' => 'csid')
|
||||
),
|
||||
'users' => array(
|
||||
'table' => 'users',
|
||||
'columns' => array('uid' => 'uid'),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains the provider token schema - used by oauth_common_schema() as well as latest related update function
|
||||
*/
|
||||
function _oauth_common_provider_token_schema() {
|
||||
return array(
|
||||
'description' => 'Additional data for OAuth tokens provided by this site.',
|
||||
'fields' => array(
|
||||
'tid' => array(
|
||||
'description' => 'The {oauth_common_token}.tid this data is related to.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'default' => 0
|
||||
),
|
||||
'token_key' => array(
|
||||
'description' => 'Token key.',
|
||||
// This is our own internal key - it's always 32 characters long
|
||||
'type' => 'char',
|
||||
'length' => 32,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'created' => array(
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'description' => 'The time that the token was created, as a Unix timestamp.',
|
||||
),
|
||||
'changed' => array(
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'description' => 'The last time the token was edited, as a Unix timestamp.',
|
||||
),
|
||||
'services' => array(
|
||||
'description' => 'An array of services that the user allowed the consumer to access.',
|
||||
'type' => 'text',
|
||||
),
|
||||
'authorized' => array(
|
||||
'description' => 'In case its a request token, it checks if the user already authorized the consumer to get an access token.',
|
||||
'type' => 'int',
|
||||
'size' => 'tiny',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
),
|
||||
'primary key' => array('token_key'),
|
||||
'unique keys' => array(
|
||||
'tid' => array('tid'),
|
||||
),
|
||||
'foreign keys' => array(
|
||||
'oauth_common_token' => array(
|
||||
'table' => 'oauth_common_token',
|
||||
'columns' => array('tid' => 'tid')
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
578
sites/all/modules/oauth/oauth_common.module
Normal file
578
sites/all/modules/oauth/oauth_common.module
Normal file
@@ -0,0 +1,578 @@
|
||||
<?php
|
||||
|
||||
define('OAUTH_COMMON_CODE_BRANCH', '6.x-3.x');
|
||||
|
||||
define('OAUTH_COMMON_TOKEN_TYPE_REQUEST', 0);
|
||||
define('OAUTH_COMMON_TOKEN_TYPE_ACCESS', 1);
|
||||
|
||||
define('OAUTH_COMMON_VERSION_1', 1); // The original 1.0 spec
|
||||
define('OAUTH_COMMON_VERSION_1_RFC', 2); // The RFC 5849 1.0 spec
|
||||
|
||||
//TODO: Don't act as a provider by default.
|
||||
//TODO: Check for other functions with breaking changes
|
||||
//TODO: Move admin pages to more regular places
|
||||
//TODO: Add watchdog messages about deprecated methods?
|
||||
//TODO: Move provider ui related pages to provider ui
|
||||
|
||||
/**
|
||||
* Implements hook_permission().
|
||||
*/
|
||||
function oauth_common_permission() {
|
||||
$permissions = array(
|
||||
'oauth authorize any consumers' => array(
|
||||
'title' => t('Authorize any OAuth consumers'),
|
||||
'restrict access' => TRUE,
|
||||
),
|
||||
'oauth register any consumers' => array(
|
||||
'title' => t('Register any OAuth consumers'),
|
||||
'restrict access' => TRUE,
|
||||
),
|
||||
'administer oauth' => array(
|
||||
'title' => t('Administer OAuth'),
|
||||
'restrict access' => TRUE,
|
||||
),
|
||||
'administer consumers' => array(
|
||||
'title' => t('Administer OAuth consumers'),
|
||||
'restrict access' => TRUE,
|
||||
),
|
||||
);
|
||||
|
||||
// Add seperate permissions for creating and
|
||||
// authorizing consumers in each context.
|
||||
foreach (oauth_common_context_list() as $name => $title) {
|
||||
$permissions[sprintf('oauth register consumers in %s', $name)] = array(
|
||||
'title' => t('Register OAuth consumers in %context', array('%context' => $title)),
|
||||
);
|
||||
$permissions[sprintf('oauth authorize consumers in %s', $name)] = array(
|
||||
'title' => t('Authorize OAuth consumers in %context', array('%context' => $title)),
|
||||
);
|
||||
}
|
||||
|
||||
return $permissions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_menu().
|
||||
*/
|
||||
function oauth_common_menu() {
|
||||
$menu = array();
|
||||
|
||||
$admin_base = array(
|
||||
'access arguments' => array('administer oauth'),
|
||||
'file' => 'oauth_common.admin.inc',
|
||||
);
|
||||
|
||||
$menu['admin/config/services/oauth'] = array(
|
||||
'title' => 'OAuth',
|
||||
'description' => 'Settings for OAuth',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('_oauth_common_admin'),
|
||||
'type' => MENU_NORMAL_ITEM,
|
||||
) + $admin_base;
|
||||
|
||||
$menu['admin/config/services/oauth/settings'] = array(
|
||||
'title' => 'Settings',
|
||||
'description' => 'Settings for OAuth',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('_oauth_common_admin'),
|
||||
'type' => MENU_DEFAULT_LOCAL_TASK,
|
||||
'weight' => 0,
|
||||
) + $admin_base;
|
||||
|
||||
// OAuth doesn't need different endpoints for the different context as all
|
||||
// actions are done with a specific consumer, which in itself is associated
|
||||
// with a context.
|
||||
|
||||
$provider_base = array(
|
||||
'access callback' => 'oauth_commmon_is_provider',
|
||||
'file' => 'oauth_common.pages.inc',
|
||||
'type' => MENU_CALLBACK,
|
||||
);
|
||||
|
||||
// The endpoint that consumers use to get a request token.
|
||||
$menu['oauth/request_token'] = array(
|
||||
'page callback' => 'oauth_common_callback_request_token',
|
||||
) + $provider_base;
|
||||
|
||||
// The page a user gets sent to to authorize a request token.
|
||||
$menu['oauth/authorize'] = array(
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('oauth_common_form_authorize'),
|
||||
) + $provider_base;
|
||||
|
||||
// The endpoint that consumers use to get a access token.
|
||||
$menu['oauth/access_token'] = array(
|
||||
'page callback' => 'oauth_common_callback_access_token',
|
||||
) + $provider_base;
|
||||
|
||||
// This page is used both in consumer and provider mode. For consumers it is
|
||||
// the callback url and triggers hook_oauth_common_authorized(). For
|
||||
// providers it is the page where users end up if no callback url exists.
|
||||
$menu['oauth/authorized'] = array(
|
||||
'title' => 'Authorization finished',
|
||||
'page callback' => 'oauth_common_page_authorized',
|
||||
'access arguments' => array('access content'),
|
||||
'file' => 'oauth_common.pages.inc',
|
||||
'type' => MENU_CALLBACK,
|
||||
);
|
||||
|
||||
// TODO: Different structures makes sense depending on whether oauth_common is
|
||||
// acting as a provider or as a consumer.
|
||||
|
||||
$menu['oauth/test/valid-consumer'] = array(
|
||||
'file' => 'oauth_common.pages.inc',
|
||||
'page callback' => '_oauth_common_validate_request_callback',
|
||||
'page arguments' => array('consumer'),
|
||||
'access callback' => 'oauth_commmon_is_provider',
|
||||
'type' => MENU_CALLBACK,
|
||||
);
|
||||
|
||||
$menu['oauth/test/valid-access-token'] = array(
|
||||
'file' => 'oauth_common.pages.inc',
|
||||
'page callback' => '_oauth_common_validate_request_callback',
|
||||
'page arguments' => array('access token'),
|
||||
'access callback' => 'oauth_commmon_is_provider',
|
||||
'type' => MENU_CALLBACK,
|
||||
);
|
||||
|
||||
return $menu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu system wildcard loader for provider consumers.
|
||||
*
|
||||
* @param string $key
|
||||
*/
|
||||
function oauth_common_consumer_load($csid) {
|
||||
$consumer = DrupalOAuthConsumer::loadById($csid, TRUE);
|
||||
if (!$consumer) {
|
||||
$consumer = FALSE;
|
||||
}
|
||||
return $consumer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu system wildcard loader for provider tokens.
|
||||
*
|
||||
* @param string $key
|
||||
*/
|
||||
function oauth_common_provider_token_load($tid) {
|
||||
$token = DrupalOAuthToken::loadByID($tid);
|
||||
if (!$token) {
|
||||
$token = FALSE;
|
||||
}
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_cron().
|
||||
*/
|
||||
function oauth_common_cron() {
|
||||
$token_condition = db_and()->condition('expires', 0, '<>')->condition('expires', REQUEST_TIME, '<=');
|
||||
|
||||
db_delete('oauth_common_provider_token')
|
||||
->condition('tid', db_select('oauth_common_token', 't')->condition($token_condition)->fields('t', array('tid')), 'IN')
|
||||
->execute();
|
||||
|
||||
db_delete('oauth_common_token')
|
||||
->condition($token_condition)
|
||||
->execute();
|
||||
|
||||
// Should 300 be overriden in DrupalOAuthServer and made configurable?
|
||||
db_delete('oauth_common_nonce')
|
||||
->condition('timestamp', REQUEST_TIME - 300, '<')
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_oauth_default_contexts().
|
||||
*/
|
||||
function oauth_common_default_oauth_common_context() {
|
||||
$contexts = array();
|
||||
|
||||
$context = new stdClass;
|
||||
$context->disabled = FALSE; /* Edit this to true to make a default context disabled initially */
|
||||
$context->name = 'default';
|
||||
$context->title = 'Default context';
|
||||
$context->authorization_options = array();
|
||||
$context->authorization_levels = array(
|
||||
'*' => array(
|
||||
'title' => 'Full access',
|
||||
'description' => 'This will give @appname the same permissions that you normally have and will allow it to access the full range of services that @sitename provides.',
|
||||
),
|
||||
'read' => array(
|
||||
'title' => 'Read access',
|
||||
'description' => 'This will allow @appname to fetch content that you have access to on @sitename.',
|
||||
),
|
||||
'update' => array(
|
||||
'title' => 'Update access',
|
||||
'description' => 'This will allow @appname to update content that you have permissions to edit.',
|
||||
),
|
||||
'create' => array(
|
||||
'title' => 'Create access',
|
||||
'description' => 'This will allow @appname to create new content on @sitename.',
|
||||
),
|
||||
'delete' => array(
|
||||
'title' => 'Delete access',
|
||||
'description' => 'This will allow @appname to delete content from @sitename.',
|
||||
),
|
||||
);
|
||||
$contexts[$context->name] = $context;
|
||||
|
||||
return $contexts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_user_delete().
|
||||
*/
|
||||
function oauth_common_user_delete($account) {
|
||||
// Delete all tokens and consumers related to a user
|
||||
module_load_include('inc', 'oauth_common');
|
||||
|
||||
$consumer_condition = db_select('oauth_common_provider_consumer', 'c')->condition('uid', $account->uid)->fields('c', array('csid'));
|
||||
$token_condition = db_or()->condition('uid', $account->uid)->condition('csid', $consumer_condition, 'IN');
|
||||
|
||||
db_delete('oauth_common_provider_token')
|
||||
->condition('tid', db_select('oauth_common_token', 't')->condition($token_condition)->fields('t', array('tid')), 'IN')
|
||||
->execute();
|
||||
|
||||
db_delete('oauth_common_token')
|
||||
->condition($token_condition)
|
||||
->execute();
|
||||
|
||||
db_delete('oauth_common_consumer')
|
||||
->condition('csid', $consumer_condition, 'IN')
|
||||
->execute();
|
||||
|
||||
db_delete('oauth_common_provider_consumer')
|
||||
->condition('uid', $account->uid)
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_xrds().
|
||||
*/
|
||||
function services_oauth_xrds() {
|
||||
$xrds = array();
|
||||
$xrds['oauth'] = array(
|
||||
'services' => array(
|
||||
array(
|
||||
'data' => array(
|
||||
'Type' => array('http://oauth.net/discovery/1.0'),
|
||||
'URI' => array('#main'),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'data' => array(
|
||||
'Type' => array(
|
||||
'http://oauth.net/core/1.0/endpoint/request',
|
||||
'http://oauth.net/core/1.0/parameters/auth-header',
|
||||
'http://oauth.net/core/1.0/parameters/uri-query',
|
||||
'http://oauth.net/core/1.0/signature/HMAC-SHA1',
|
||||
),
|
||||
'URI' => array(url('oauth/request_token', array('absolute' => TRUE))),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'data' => array(
|
||||
'Type' => array(
|
||||
'http://oauth.net/core/1.0/endpoint/authorize',
|
||||
'http://oauth.net/core/1.0/parameters/uri-query',
|
||||
),
|
||||
'URI' => array(url('oauth/authorize', array('absolute' => TRUE))),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'data' => array(
|
||||
'Type' => array(
|
||||
'http://oauth.net/core/1.0/endpoint/access',
|
||||
'http://oauth.net/core/1.0/parameters/auth-header',
|
||||
'http://oauth.net/core/1.0/parameters/uri-query',
|
||||
'http://oauth.net/core/1.0/signature/HMAC-SHA1',
|
||||
),
|
||||
'URI' => array(url('oauth/access_token', array('absolute' => TRUE))),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
return $xrds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access callback function used by several menu items.
|
||||
*
|
||||
* @param stdClass $user
|
||||
* A user object.
|
||||
* @param string $permission
|
||||
* The permission that is needed in addition to edit access on the $user.
|
||||
*/
|
||||
function _oauth_common_user_access($user, $permission = NULL) {
|
||||
return user_edit_access($user) && (empty($permission) || user_access($permission));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the user has permission to edit the consumer. Edit access is
|
||||
* granted if the user has the 'administer consumers' permission or may
|
||||
* edit the account connected to the consumer.
|
||||
*
|
||||
* @param DrupalOAuthConsumer $consumer
|
||||
* @return bool
|
||||
*/
|
||||
function oauth_common_can_edit_consumer($consumer) {
|
||||
$may_edit = user_access('administer consumers');
|
||||
|
||||
// If the user doesn't have consumer admin privileges, check for account
|
||||
// edit access.
|
||||
if (!$may_edit && $consumer->uid) {
|
||||
$user = user_load($consumer->uid);
|
||||
$may_edit = user_edit_access($user);
|
||||
}
|
||||
|
||||
return $may_edit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deterines if a user has the necessary permissions to create consumers.
|
||||
*
|
||||
* @param object $account
|
||||
* The user account to check permissions for. Defaults to the currently
|
||||
* logged in user.
|
||||
* @return bool
|
||||
*/
|
||||
function oauth_common_can_create_consumers($account = NULL) {
|
||||
global $user;
|
||||
if (!$account) {
|
||||
$account = $user;
|
||||
}
|
||||
|
||||
$can_register_consumers = user_access('oauth register any consumers', $account);
|
||||
if (!$can_register_consumers) {
|
||||
foreach (oauth_common_context_list() as $context => $title) {
|
||||
$can_register_consumers = $can_register_consumers || user_access(sprintf('oauth register consumers in %s', $context), $account);
|
||||
}
|
||||
}
|
||||
return $can_register_consumers;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is used as a access callback
|
||||
* when the authentication of the request shouldn't be
|
||||
* done by the menu system.
|
||||
*
|
||||
* @return bool
|
||||
* Always returns TRUE
|
||||
*/
|
||||
function _oauth_common_always_true() {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access callback that checks if a user may create authorizations in the
|
||||
* consumers context.
|
||||
*
|
||||
* @param DrupalOAuthConsumer $consumer
|
||||
* @return bool
|
||||
*/
|
||||
function oauth_common_can_authorize_consumer($consumer) {
|
||||
return user_access(sprintf('oauth authorize consumers in %s', $consumer->context));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if oauth_common is acting as a provider.
|
||||
*/
|
||||
function oauth_commmon_is_provider() {
|
||||
return variable_get('oauth_common_enable_provider', TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a request token from a oauth provider and returns the authorization
|
||||
* url. The request token is saved in the database.
|
||||
*
|
||||
* @param OAuthToken $consumer_token
|
||||
* The consumer token to use
|
||||
* @param string $request_endpoint
|
||||
* Optional. Pass a custom endpoint if needed. Defaults to '/oauth/request_token'.
|
||||
* @param string $authorize_endpoint
|
||||
* Optional. Pass a custom endpoint if needed. Defaults to '/oauth/authorize'.
|
||||
* @return string
|
||||
* The url that the client should be redirected to to authorize
|
||||
* the request token.
|
||||
*/
|
||||
function oauth_common_get_request_token($consumer_token, $request_endpoint = '/oauth/request_token', $authorize_endpoint = '/oauth/authorize') {
|
||||
$client = new DrupalOAuthClient($consumer_token);
|
||||
$request_token = $client->getRequestToken($request_endpoint);
|
||||
$request_token->write();
|
||||
return $client->getAuthorizationUrl($authorize_endpoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the tokens for a user.
|
||||
*
|
||||
* @param string $uid
|
||||
* @param string $type
|
||||
* @return array
|
||||
*/
|
||||
function oauth_common_get_user_provider_tokens($uid) {
|
||||
$res = db_query("SELECT t.*, pt.created, pt.changed, pt.services, pt.authorized FROM {oauth_common_token} t
|
||||
INNER JOIN {oauth_common_provider_token} pt WHERE t.uid = :uid AND t.type = :type", array(
|
||||
':uid' => $uid,
|
||||
':type' => OAUTH_COMMON_TOKEN_TYPE_ACCESS,
|
||||
));
|
||||
$tokens = array();
|
||||
while ($token = DrupalOAuthToken::fromResult($res)) {
|
||||
$tokens[] = $token;
|
||||
}
|
||||
return $tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new context with defaults appropriately set from schema.
|
||||
*
|
||||
* @return stdClass
|
||||
* An context initialized with the default values.
|
||||
*/
|
||||
function oauth_common_context_new() {
|
||||
if (!module_exists('ctools')) {
|
||||
return FALSE;
|
||||
}
|
||||
ctools_include('export');
|
||||
return ctools_export_new_object('oauth_common_context');
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a single context.
|
||||
*
|
||||
* @param string $name
|
||||
* The name of the context.
|
||||
* @return stdClass
|
||||
* The context configuration.
|
||||
*/
|
||||
function oauth_common_context_load($name) {
|
||||
if (!module_exists('ctools')) {
|
||||
return FALSE;
|
||||
}
|
||||
ctools_include('export');
|
||||
$result = ctools_export_load_object('oauth_common_context', 'names', array($name));
|
||||
if (isset($result[$name])) {
|
||||
return $result[$name];
|
||||
}
|
||||
else {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the context for a request.
|
||||
*
|
||||
* @param OAuthRequest $request
|
||||
* @return object
|
||||
* The context configuration.
|
||||
*/
|
||||
function oauth_common_context_from_request($request) {
|
||||
$context = NULL;
|
||||
$consumer_key = $request->get_parameter('oauth_consumer_key');
|
||||
$token_key = $request->get_parameter('oauth_token');
|
||||
|
||||
if (empty($consumer_key) && !empty($token_key)) {
|
||||
$token = DrupalOAuthToken::loadByKey($token_key, FALSE, OAUTH_COMMON_TOKEN_TYPE_REQUEST);
|
||||
if ($token) {
|
||||
$consumer = $token->consumer;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($consumer_key)) {
|
||||
$consumer = DrupalOAuthConsumer::loadProviderByKey($consumer_key);
|
||||
}
|
||||
|
||||
if (!empty($consumer)) {
|
||||
$context = oauth_common_context_load($consumer->context);
|
||||
}
|
||||
|
||||
return $context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all contexts.
|
||||
*
|
||||
* @return array
|
||||
* Array of context objects keyed by context names.
|
||||
*/
|
||||
function oauth_common_context_load_all() {
|
||||
if (!module_exists('ctools')) {
|
||||
return FALSE;
|
||||
}
|
||||
ctools_include('export');
|
||||
return ctools_export_load_object('oauth_common_context');
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves an context in the database.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function oauth_common_context_save($context) {
|
||||
$update = (isset($context->cid)) ? array('cid') : array();
|
||||
drupal_write_record('oauth_common_context', $context, $update);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an context.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function oauth_common_context_delete($context) {
|
||||
db_delete('oauth_common_context')
|
||||
->condition('name', $context->name)
|
||||
->condition('cid', $context->cid)
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Export an context.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function oauth_common_context_export($context, $indent = '') {
|
||||
if (!module_exists('ctools')) {
|
||||
return FALSE;
|
||||
}
|
||||
ctools_include('export');
|
||||
$output = ctools_export_object('oauth_common_context', $context, $indent);
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists all available contexts.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function oauth_common_context_list() {
|
||||
$return = array();
|
||||
$contexts = oauth_common_context_load_all();
|
||||
if ($contexts) {
|
||||
foreach ($contexts as $context) {
|
||||
$return[$context->name] = $context->title;
|
||||
}
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the current version of the OAuth module, used in eg. user agents
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function _oauth_common_version() {
|
||||
static $version;
|
||||
if (!isset($version)) {
|
||||
$info = db_query("SELECT info FROM {system} WHERE name = 'oauth_common'")->fetchField();
|
||||
$info = $info ? unserialize($info) : FALSE;
|
||||
if (!$info || empty($info['version'])) {
|
||||
$version = OAUTH_COMMON_CODE_BRANCH;
|
||||
}
|
||||
else {
|
||||
$version = $info['version'];
|
||||
}
|
||||
}
|
||||
return $version;
|
||||
}
|
386
sites/all/modules/oauth/oauth_common.pages.inc
Normal file
386
sites/all/modules/oauth/oauth_common.pages.inc
Normal file
@@ -0,0 +1,386 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Combined menu callback for tests of consumers and access tokens
|
||||
*/
|
||||
function _oauth_common_validate_request_callback($type, $unsigned = NULL) {
|
||||
try {
|
||||
module_load_include('inc', 'oauth_common');
|
||||
|
||||
list($signed, $consumer, $token) = oauth_common_verify_request();
|
||||
|
||||
if ($consumer == NULL) {
|
||||
throw new OAuthException('Missing consumer token');
|
||||
}
|
||||
|
||||
if (!$signed && $unsigned != 'unsigned') {
|
||||
throw new OAuthException("The request wasn't signed");
|
||||
}
|
||||
|
||||
if ($token == NULL && $type == 'access token') {
|
||||
throw new OAuthException('Missing access token');
|
||||
}
|
||||
}
|
||||
catch (OAuthException $e) {
|
||||
drupal_add_http_header('Status', '401 Unauthorized: ' . $e->getMessage());
|
||||
drupal_add_http_header('WWW-Authenticate', sprintf('OAuth realm="%s"', url('', array('absolute' => TRUE))));
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu callback for when something has been authorized - used in both client and provider flow
|
||||
*
|
||||
* @param $csid Should contain the id of the consumer when used in the client flow
|
||||
*/
|
||||
function oauth_common_page_authorized($csid = NULL) {
|
||||
// If we have an oauth_token we're acting as a consumer and just got authorized
|
||||
if (!empty($_GET['oauth_token'])) {
|
||||
//TODO: Add documentation on how to use the callback url with
|
||||
$consumer = $csid ? DrupalOAuthConsumer::loadById($csid, FALSE) : FALSE;
|
||||
if ($consumer) {
|
||||
$request_token = DrupalOAuthToken::loadByKey($_GET['oauth_token'], $consumer, OAUTH_COMMON_TOKEN_TYPE_REQUEST);
|
||||
}
|
||||
else {
|
||||
// Backwards compatibility with 6.x-3.0-beta3
|
||||
$request_token = DrupalOAuthToken::load($_GET['oauth_token'], FALSE);
|
||||
$consumer = $request_token ? $request_token->consumer : FALSE;
|
||||
}
|
||||
if (!empty($request_token)) {
|
||||
$client = new DrupalOAuthClient($consumer, $request_token);
|
||||
|
||||
$verifier = isset($_GET['oauth_verifier']) ? $_GET['oauth_verifier'] : NULL;
|
||||
|
||||
$access_token = $client->getAccessToken(NULL, array('verifier' => $verifier));
|
||||
if ($access_token) {
|
||||
// We recieved a new token - save it
|
||||
if (!$access_token->in_database) {
|
||||
$access_token->write();
|
||||
}
|
||||
$request_token->delete();
|
||||
module_invoke_all('oauth_common_authorized', $consumer, $access_token, $request_token);
|
||||
}
|
||||
}
|
||||
}
|
||||
return t('The application has been authorized');
|
||||
}
|
||||
|
||||
/**
|
||||
* Form for granting access to the consumer
|
||||
*/
|
||||
function oauth_common_form_authorize() {
|
||||
module_load_include('inc', 'oauth_common');
|
||||
$req = DrupalOAuthRequest::from_request();
|
||||
$context = oauth_common_context_from_request($req);
|
||||
$auth_ops = $context->authorization_options;
|
||||
|
||||
if (!$context) {
|
||||
drupal_set_message(t("Can't find OAuth context, check the site's settings."), 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
$token = $req->get_parameter('oauth_token');
|
||||
$callback = $req->get_parameter('oauth_callback');
|
||||
$token = DrupalOAuthToken::loadByKey($token, FALSE, OAUTH_COMMON_TOKEN_TYPE_REQUEST);
|
||||
|
||||
// Check that we have a valid token
|
||||
if (!$token) {
|
||||
drupal_set_message(t('Please include a valid OAuth token in your request.'), 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
$consumer = $token->consumer;
|
||||
|
||||
// Redirect to the right form, or present an error.
|
||||
global $user;
|
||||
if ($user->uid) {
|
||||
// There's some strange bug in the ?destination=... handling
|
||||
// This is not exactly beautiful, but it gets the work done
|
||||
// TODO: Find out why!
|
||||
if (drupal_substr($_SERVER['REQUEST_URI'], 0, 2) == '//') {
|
||||
header('Location: ' . drupal_substr($_SERVER['REQUEST_URI'], 1), TRUE, 302);
|
||||
}
|
||||
|
||||
if (!(user_access('oauth authorize any consumers') || user_access('oauth authorize consumers in ' . $consumer->context))) {
|
||||
drupal_set_message(t('You are not authorized to allow external services access to this system.'), 'error');
|
||||
return drupal_access_denied();
|
||||
}
|
||||
|
||||
if (!empty($auth_ops['automatic_authorization'])
|
||||
&& $auth_ops['automatic_authorization']
|
||||
&& !empty($consumer->callback_url)) {
|
||||
// Authorize the request token
|
||||
$token->uid = $user->uid;
|
||||
$token->authorized = 1;
|
||||
$token->services = $context->authorization_options['default_authorization_levels'];
|
||||
$token->write(TRUE);
|
||||
|
||||
// Pick the callback url apart and add the token parameter
|
||||
$callback = parse_url($consumer->callback_url);
|
||||
$query = array();
|
||||
parse_str($callback['query'], $query);
|
||||
$query['oauth_token'] = $token->key;
|
||||
$callback['query'] = http_build_query($query, 'idx_', '&');
|
||||
|
||||
// Return to the consumer site
|
||||
header('Location: ' . _oauth_common_glue_url($callback), TRUE, 302);
|
||||
exit;
|
||||
}
|
||||
|
||||
$tvars = array(
|
||||
'@user' => $user->name,
|
||||
'@appname' => $consumer->name,
|
||||
'@sitename' => variable_get('site_name', ''),
|
||||
);
|
||||
|
||||
$title = !empty($context->title) ? $context->title : 'Authorize @appname';
|
||||
drupal_set_title(t($title, $tvars), PASS_THROUGH);
|
||||
|
||||
$form = array();
|
||||
|
||||
$form['token'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $token,
|
||||
);
|
||||
|
||||
$message = !empty($auth_ops['message']) ? $auth_ops['message'] :
|
||||
'The application @appname wants to access @sitename on your behalf, check the permissions that you would like the application to have.';
|
||||
$form['message'] = array(
|
||||
'#type' => 'item',
|
||||
'#markup' => t($message, $tvars),
|
||||
);
|
||||
|
||||
$message = !empty($auth_ops['warning']) ? $auth_ops['warning'] :
|
||||
'If you don\'t know what @appname is, or don\'t want to give it access to your content, just click here and we\'ll take you away from this page without granting @appname any access to @sitename.';
|
||||
$form['warning'] = array(
|
||||
'#type' => 'item',
|
||||
'#markup' => l(t($message, $tvars), 'oauth/authorization/deny/' . $token->key),
|
||||
'#attributes' => array(
|
||||
'class' => array('abort-authorization'),
|
||||
),
|
||||
);
|
||||
|
||||
$disable_selection = !empty($auth_ops['disable_auth_level_selection'])
|
||||
&& !empty($auth_ops['default_authorization_levels'])
|
||||
&& $auth_ops['disable_auth_level_selection'];
|
||||
|
||||
if (!$disable_selection) {
|
||||
$authorization_title = !empty($auth_ops['authorization_title']) ? $auth_ops['authorization_title'] :
|
||||
'Permissions';
|
||||
$form['authorization'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t($authorization_title, $tvars),
|
||||
);
|
||||
|
||||
$form['authorization']['levels'] = array(
|
||||
'#tree' => TRUE,
|
||||
);
|
||||
foreach ($context->authorization_levels as $name => $level) {
|
||||
$auth_level_opt = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t($level['title'], $tvars),
|
||||
'#description' => t($level['description'], $tvars),
|
||||
'#value' => $level['default'],
|
||||
);
|
||||
$form['authorization']['levels'][$name] = $auth_level_opt;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$form['authorization']['levels'] = array(
|
||||
'#tree' => TRUE,
|
||||
);
|
||||
foreach ($auth_ops['default_authorization_levels'] as $level) {
|
||||
$form['authorization']['levels'][$level] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $level,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$deny_title = !empty($auth_ops['deny_access_title']) ? $auth_ops['deny_access_title'] :
|
||||
'Deny access';
|
||||
$form['deny'] = array(
|
||||
'#type' => 'item',
|
||||
'#markup' => l(t($deny_title), 'oauth/authorization/deny/' . $token->key),
|
||||
'#attributes' => array(
|
||||
'class' => array('deny-access'),
|
||||
),
|
||||
);
|
||||
|
||||
$grant_title = !empty($auth_ops['grant_access_title']) ? $auth_ops['grant_access_title'] :
|
||||
'Grant access';
|
||||
$form['actions'] = array('#type' => 'actions');
|
||||
$form['actions']['confirm'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t($grant_title),
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
else {
|
||||
$query = $_GET;
|
||||
unset($query['q']); // why are there so few q's?
|
||||
drupal_goto('user/login', array('query' => array(
|
||||
'destination' => url('oauth/authorize', array(
|
||||
'query' => $query,
|
||||
)),
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validation of the form for granting access to the consumer
|
||||
*/
|
||||
function oauth_common_form_authorize_validate($form, &$form_state) {
|
||||
$values = $form_state['values'];
|
||||
$got_permission = FALSE;
|
||||
|
||||
$consumer = $values['token']->consumer;
|
||||
$context = oauth_common_context_load($consumer->context);
|
||||
|
||||
if (!$context) {
|
||||
form_set_error('confirm', t("Can't find OAuth context."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$context->authorization_options['disable_auth_level_selection']) {
|
||||
foreach ($context->authorization_levels as $name => $level) {
|
||||
if ($values['levels'][$name]) {
|
||||
$got_permission = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$got_permission) {
|
||||
form_set_error('confirm', t("You haven't given the application access to anything. Click on 'Deny access' or just close this window if you don't want to authorize it."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Form submit handler that grants access to the consumer
|
||||
*/
|
||||
function oauth_common_form_authorize_submit(&$form, &$form_state) {
|
||||
global $user;
|
||||
$values = $form_state['values'];
|
||||
|
||||
// Save the list of all services that the user allowed the
|
||||
// consumer to do
|
||||
$token = $values['token'];
|
||||
$token->uid = $user->uid;
|
||||
$token->authorized = 1;
|
||||
$consumer = $token->consumer;
|
||||
$context = oauth_common_context_load($consumer->context);
|
||||
|
||||
if (!$context) {
|
||||
drupal_set_message(t("Can't find OAuth context, check the site's settings."), 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
// Add services
|
||||
if (!empty($values['full_access'])) { // TODO: Full access should be a configurable auth level
|
||||
$token->services = array('*');
|
||||
}
|
||||
elseif (!empty($values['levels'])) {
|
||||
$token->services = array_keys(array_filter($values['levels']));
|
||||
}
|
||||
else {
|
||||
$token->services = array();
|
||||
}
|
||||
|
||||
$token->write(TRUE);
|
||||
|
||||
if (!empty($consumer->callback_url) && $consumer->callback_url !== 'oob') {
|
||||
// Pick the callback url apart and add the token parameter
|
||||
$callback = parse_url($consumer->callback_url);
|
||||
$query = array();
|
||||
if (!empty($callback['query'])) {
|
||||
parse_str($callback['query'], $query);
|
||||
}
|
||||
$query['oauth_token'] = $token->key;
|
||||
$callback['query'] = http_build_query($query, 'idx_', '&');
|
||||
|
||||
// Return to the consumer site
|
||||
header('Location: ' . _oauth_common_glue_url($callback), TRUE, 302);
|
||||
exit;
|
||||
}
|
||||
else {
|
||||
drupal_goto('oauth/authorized');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs the url to which to return someone who has asked for access to a consumer
|
||||
*/
|
||||
function _oauth_common_glue_url($parsed) {
|
||||
$uri = isset($parsed['scheme']) ? $parsed['scheme'] . '://' : '';
|
||||
$uri .= isset($parsed['user']) ? $parsed['user'] . (isset($parsed['pass']) ? ':' . $parsed['pass'] : '') . '@' : '';
|
||||
$uri .= isset($parsed['host']) ? $parsed['host'] : '';
|
||||
$uri .= isset($parsed['port']) ? ':' . $parsed['port'] : '';
|
||||
|
||||
if (isset($parsed['path'])) {
|
||||
$uri .= (substr($parsed['path'], 0, 1) == '/') ?
|
||||
$parsed['path'] :
|
||||
((!empty($uri) ? '/' : '' ) . $parsed['path']);
|
||||
}
|
||||
|
||||
$uri .= isset($parsed['query']) ? '?' . $parsed['query'] : '';
|
||||
|
||||
return $uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a request token from the request.
|
||||
*/
|
||||
function oauth_common_callback_request_token() {
|
||||
try {
|
||||
$req = DrupalOAuthRequest::from_request();
|
||||
$context = oauth_common_context_from_request($req);
|
||||
if (!$context) {
|
||||
throw new OAuthException('No OAuth context found');
|
||||
}
|
||||
$server = new DrupalOAuthServer($context);
|
||||
print $server->fetch_request_token($req);
|
||||
}
|
||||
catch (OAuthException $e) {
|
||||
drupal_add_http_header('Status', '401 Unauthorized: ' . $e->getMessage());
|
||||
drupal_add_http_header('WWW-Authenticate', sprintf('OAuth realm="%s"', url('', array('absolute' => TRUE))));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a access token for the request
|
||||
*/
|
||||
function oauth_common_callback_access_token() {
|
||||
try {
|
||||
$req = DrupalOAuthRequest::from_request();
|
||||
$context = oauth_common_context_from_request($req);
|
||||
if (!$context) {
|
||||
throw new OAuthException('No OAuth context found');
|
||||
}
|
||||
$server = new DrupalOAuthServer($context);
|
||||
$access_token = $server->fetch_access_token($req);
|
||||
|
||||
// Set the expiry time based on context settings or get parameter
|
||||
$expires = !empty($context->authorization_options['access_token_lifetime']) ? REQUEST_TIME + $context->authorization_options['access_token_lifetime'] : 0;
|
||||
if ($_GET['expires'] && intval($_GET['expires'])) {
|
||||
$hint = intval($_GET['expires']);
|
||||
// Only accept more restrictive expiry times
|
||||
if ($expires == 0 || $hint < $expires) {
|
||||
$expires = $hint;
|
||||
}
|
||||
}
|
||||
|
||||
// Store the expiry time if the access token should expire
|
||||
if ($expires) {
|
||||
$access_token->expires = $expires;
|
||||
$access_token->write(TRUE);
|
||||
}
|
||||
|
||||
print $access_token;
|
||||
}
|
||||
catch (OAuthException $e) {
|
||||
drupal_add_http_header('Status', '401 Unauthorized: ' . $e->getMessage());
|
||||
drupal_add_http_header('WWW-Authenticate', sprintf('OAuth realm="%s"', url('', array('absolute' => TRUE))));
|
||||
}
|
||||
}
|
14
sites/all/modules/oauth/oauth_common_providerui.info
Normal file
14
sites/all/modules/oauth/oauth_common_providerui.info
Normal file
@@ -0,0 +1,14 @@
|
||||
name = OAuth Provider UI
|
||||
description = Provides a UI for when OAuth is acting as a provider.
|
||||
package = "OAuth"
|
||||
|
||||
dependencies[] = oauth_common
|
||||
|
||||
core = 7.x
|
||||
|
||||
; Information added by drupal.org packaging script on 2012-02-08
|
||||
version = "7.x-3.0"
|
||||
core = "7.x"
|
||||
project = "oauth"
|
||||
datestamp = "1328724350"
|
||||
|
168
sites/all/modules/oauth/oauth_common_providerui.module
Normal file
168
sites/all/modules/oauth/oauth_common_providerui.module
Normal file
@@ -0,0 +1,168 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Implements hook_menu().
|
||||
*/
|
||||
function oauth_common_providerui_menu() {
|
||||
$menu = array();
|
||||
$admin_base = array(
|
||||
'access arguments' => array('administer oauth'),
|
||||
'file' => 'oauth_common.admin.inc',
|
||||
);
|
||||
|
||||
$menu['admin/config/services/oauth/contexts'] = array(
|
||||
'title' => 'Contexts',
|
||||
'description' => 'The different context for authorization',
|
||||
'page callback' => 'oauth_common_list_context',
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'weight' => 1,
|
||||
) + $admin_base;
|
||||
|
||||
////////////
|
||||
// Context administration
|
||||
////////////
|
||||
|
||||
$menu['admin/config/services/oauth/add'] = array(
|
||||
'title' => 'Add context',
|
||||
'page callback' => 'oauth_common_add_context',
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'weight' => 10,
|
||||
) + $admin_base;
|
||||
|
||||
$menu['admin/config/services/oauth/%oauth_common_context/edit'] = array(
|
||||
'title' => 'Edit context',
|
||||
'page callback' => 'oauth_common_edit_context',
|
||||
'page arguments' => array(4),
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'weight' => 10,
|
||||
) + $admin_base;
|
||||
|
||||
$menu['admin/config/services/oauth/%oauth_common_context/export'] = array(
|
||||
'title' => 'Export context',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('oauth_common_export_context', 4),
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'weight' => 20,
|
||||
) + $admin_base;
|
||||
|
||||
$menu['admin/config/services/oauth/%oauth_common_context/delete'] = array(
|
||||
'title' => 'Delete context',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('oauth_common_delete_confirm_context', 4),
|
||||
'type' => MENU_CALLBACK,
|
||||
) + $admin_base;
|
||||
|
||||
$menu['admin/config/services/oauth/%oauth_common_context/disable'] = array(
|
||||
'page callback' => 'oauth_common_context',
|
||||
'page arguments' => array(3),
|
||||
'type' => MENU_CALLBACK,
|
||||
) + $admin_base;
|
||||
|
||||
$menu['admin/config/services/oauth/%oauth_common_context/enable'] = array(
|
||||
'page callback' => 'oauth_common_enable_context',
|
||||
'page arguments' => array(3),
|
||||
'type' => MENU_CALLBACK,
|
||||
) + $admin_base;
|
||||
|
||||
////////////
|
||||
// Authorization administration
|
||||
////////////
|
||||
|
||||
$menu['user/%user/oauth'] = array(
|
||||
'title' => 'Authorization',
|
||||
'page callback' => 'oauth_common_page_user_authorizations',
|
||||
'page arguments' => array(1, TRUE),
|
||||
'access callback' => '_oauth_common_user_access',
|
||||
'access arguments' => array(1),
|
||||
'file' => 'oauth_common.authorizations.inc',
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
);
|
||||
|
||||
$menu['user/%user/oauth/authorizations'] = array(
|
||||
'title' => 'Authorizations',
|
||||
'page callback' => 'oauth_common_page_user_authorizations',
|
||||
'page arguments' => array(1, TRUE),
|
||||
'access callback' => '_oauth_common_user_access',
|
||||
'access arguments' => array(1),
|
||||
'file' => 'oauth_common.authorizations.inc',
|
||||
'type' => MENU_DEFAULT_LOCAL_TASK,
|
||||
);
|
||||
|
||||
$menu['user/%user/oauth/authorizations/%oauth_common_provider_token'] = array(
|
||||
'title' => 'Edit authorization',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('oauth_common_form_authorization', 4),
|
||||
'access callback' => '_oauth_common_user_access',
|
||||
'access arguments' => array(1, 'oauth authorize any consumers'),
|
||||
'file' => 'oauth_common.authorizations.inc',
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
);
|
||||
|
||||
$menu['user/%user/oauth/authorizations/%oauth_common_provider_token/delete'] = array(
|
||||
'title' => 'Delete authorization',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('oauth_common_form_authorization_delete', 1, 4),
|
||||
// We always want to allow the user to delete a authorization, that
|
||||
// shouldn't be a permission that can be rescinded.
|
||||
'access callback' => 'user_edit_access',
|
||||
'access arguments' => array(1),
|
||||
'file' => 'oauth_common.authorizations.inc',
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
);
|
||||
|
||||
////////////
|
||||
// Consumer administration
|
||||
////////////
|
||||
|
||||
$menu['user/%user/oauth/consumers'] = array(
|
||||
'title' => 'Consumers',
|
||||
'page callback' => 'oauth_common_page_user_consumers',
|
||||
'page arguments' => array(1),
|
||||
'access callback' => '_oauth_common_user_access',
|
||||
'access arguments' => array(1),
|
||||
'file' => 'oauth_common.consumers.inc',
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
);
|
||||
|
||||
$menu['user/%user/oauth/consumer/add'] = array(
|
||||
'title' => 'Add consumer',
|
||||
'page callback' => 'oauth_common_add_consumer',
|
||||
'page arguments' => array(1),
|
||||
'access callback' => 'oauth_common_can_create_consumers',
|
||||
'file' => 'oauth_common.consumers.inc',
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'weight' => 10,
|
||||
);
|
||||
|
||||
$menu['user/%user/oauth/consumer/%oauth_common_consumer'] = array(
|
||||
'title' => 'Edit consumer',
|
||||
'page callback' => 'oauth_common_edit_consumer',
|
||||
'page arguments' => array(4),
|
||||
'access callback' => 'oauth_common_can_edit_consumer',
|
||||
'access arguments' => array(4),
|
||||
'file' => 'oauth_common.consumers.inc',
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
);
|
||||
|
||||
$menu['user/%user/oauth/consumer/%oauth_common_consumer/delete'] = array(
|
||||
'title' => 'Delete consumer',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('oauth_common_form_consumer_delete', 4),
|
||||
'access callback' => 'oauth_common_can_edit_consumer',
|
||||
'access arguments' => array(4),
|
||||
'file' => 'oauth_common.consumers.inc',
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
);
|
||||
|
||||
$menu['user/%user/oauth/consumer/%oauth_common_consumer/add-authorization'] = array(
|
||||
'title' => 'Add authorization',
|
||||
'page callback' => 'oauth_common_authorization_add',
|
||||
'page arguments' => array(4),
|
||||
'access callback' => 'oauth_common_can_authorize_consumer',
|
||||
'access arguments' => array(4),
|
||||
'file' => 'oauth_common.authorizations.inc',
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
);
|
||||
|
||||
return $menu;
|
||||
}
|
Reference in New Issue
Block a user