Repository: agiliq/Django-Socialauth Branch: master Commit: b0b1acb180dd Files: 79 Total size: 284.4 KB Directory structure: gitextract_g8s7pbgi/ ├── .gitignore ├── AUTHORS ├── LICENSE ├── README.rst ├── example_project/ │ ├── LICENSE │ ├── __init__.py │ ├── commentor/ │ │ ├── __init__.py │ │ ├── models.py │ │ ├── templates/ │ │ │ └── commentor/ │ │ │ └── index.html │ │ ├── tests.py │ │ ├── urls.py │ │ └── views.py │ ├── example/ │ │ ├── README │ │ ├── __init__.py │ │ ├── admin.py │ │ ├── models.py │ │ ├── templates/ │ │ │ ├── comments/ │ │ │ │ └── example/ │ │ │ │ ├── form.html │ │ │ │ └── list.html │ │ │ └── example/ │ │ │ ├── post_detail.html │ │ │ └── post_list.html │ │ ├── tests.py │ │ ├── urls.py │ │ └── views.py │ ├── example_comments/ │ │ ├── __init__.py │ │ ├── models.py │ │ ├── tests.py │ │ └── views.py │ ├── localsettings.example.py │ ├── manage.py │ ├── media/ │ │ ├── css/ │ │ │ ├── openid.css │ │ │ └── socialauth.css │ │ └── js/ │ │ └── openid-jquery.js │ ├── settings.py │ └── urls.py ├── ez_setup.py ├── openid_consumer/ │ ├── __init__.py │ ├── locale/ │ │ ├── en/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── django.mo │ │ │ └── django.po │ │ └── sl/ │ │ └── LC_MESSAGES/ │ │ ├── django.mo │ │ └── django.po │ ├── middleware.py │ ├── models.py │ ├── templates/ │ │ └── openid_consumer/ │ │ ├── failure.html │ │ ├── openid_consumer_base.html │ │ └── signin.html │ ├── util.py │ └── views.py ├── requirements.txt ├── setup.py └── socialauth/ ├── __init__.py ├── admin.py ├── auth_backends.py ├── context_processors.py ├── forms.py ├── lib/ │ ├── __init__.py │ ├── foursquare.py │ ├── github.py │ ├── linkedin.py │ ├── oauth1.py │ ├── oauth2.py │ ├── oauthgoogle.py │ ├── oauthtwitter.py │ ├── oauthtwitter2.py │ ├── oauthyahoo.py │ └── twitter.py ├── models.py ├── templates/ │ ├── openid/ │ │ └── index.html │ └── socialauth/ │ ├── base.html │ ├── editprofile.html │ ├── login_page.html │ ├── signin_complete.html │ ├── socialauth_base.html │ └── xd_receiver.htm ├── templatetags/ │ ├── __init__.py │ └── socialauth_tags.py ├── test_data.py.example ├── tests.py ├── urls.py └── views.py ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ *.log *.pyc *~ build/ localsettings.py .svn/ .svn/* data.db *.wpr facebook.py ================================================ FILE: AUTHORS ================================================ AUTHORS Shabda Raaj Lakshman Prasad Ashok Raavi CONTRIBUTORS oliland Benny Daon Peter Sidebotham clay Ken Sternberg Oliver Kingshott twidi Aitzol Naberan cyrildoussin zay2 Phui Hock David Michon Justin Wong Rodrigo Chacon Didier Rano David Michon Josu Azpillaga Hel Javed Khan Roman Vorushin ================================================ FILE: LICENSE ================================================ This work is dual licensed under GPLv3 and BSD license. BSD ------- Copyright (c) 2009-2010, Agiliq Solutions All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of the nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. GPLv3 ----------- Copyright (c) 2010, Agiliq Solutions All rights reserved. GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. 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 them 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 prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. 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. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey 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; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If 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 convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU 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 that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. 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. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 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. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. 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 state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program 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, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU 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. But first, please read . ================================================ FILE: README.rst ================================================ WARNING: This app is not maintained anymore ------------------------------------------------- This repo is here for archive purposes, but unmaintained. There are much better social auth libraries. * https://github.com/omab/django-social-auth/ * https://github.com/pennersr/django-allauth What it does. -------------- #. Allow logging in via various providers. #. Import contacts from various third party sites, to find out which of your friends already use our service. Logging In ---------- This is a application to enable authentication via various third party sites. In particular it allows logging in via #. Twitter #. Gmail #. Facebook #. Yahoo(Essentially openid) #. OpenId #. Github #. Foursquare Libs you need to install See requirements.txt use `pip install -r requirements.txt` to install all dependencies at once Note that you will probably require git and mercurial installed for pip to fetch the requirements. The API Keys are available from * http://www.facebook.com/developers/createapp.php * https://developer.yahoo.com/dashboard/createKey.html * https://www.google.com/accounts/ManageDomains * http://twitter.com/oauth_clients * https://github.com/settings/applications/new * https://developer.foursquare.com/overview/auth.html How it works. -------------- * **Openid**: Users need to provide their openid providers. Talk to the providers and login. * **Yahoo**: Yahoo is an openid provider. Talk to Yahoo endpoints. (Endpoint: http://yahoo.com) * **Google**: Google is a provider. Talk to them. (Endpoint: https://www.google.com/accounts/o8/id) * **Facebook**: Facebook connect provides authentication framework. * **Twitter**: We use Twitter Oauth for authentication. In theory, Oauth shouldn't be used for authentication. (It is an autorisation framework, not an authentication one), In practice it works pretty well. Once you have an access_token, and a name, essentially authenticated. * **Github**:We use Github Oauth for authentication. As like Twitter, it works pretty well. * **Foursquare**:We use Oauth2.0 for authenticating via foursquare. References ---------- #. http://openid.net/developers/ #. http://developer.yahoo.com/openid/ #. http://code.google.com/apis/accounts/docs/OpenID.html #. http://apiwiki.twitter.com/OAuth-FAQ #. http://developers.facebook.com/connect.php #. http://develop.github.com/p/oauth.html #. https://developer.foursquare.com/overview/auth.html Limitations ------------ As with all APIs, we are limited by the amount of data which the API provider provides us. For example, both Yahoo and Google provide extremely limited data about the autheticated subscriber. Twitter and Facebook provide a lot of details, but not the email. Different Openid providers are free to provide [different amounts of data](http://openid.net/specs/openid-simple-registration-extension-1_0.html). How it works. -------------- #. For all providers(except Facebook) there are two urls and views. (start and done) #. Start sets up the required tokens, and redirects and hands off to the correct provider. #. Provider handles authentication on their ends, and hands off to Us, providing authorization tokens. #. In done, we check if the user with these details already exists, if yes, we log them in. Otherwise we create a new user, and log them in. For all of these, we use standard django authenication system, with custom auth_backends, hence all existing views, and decorators as login_required will work as expected. Urls ----- * /login/ Login page. Has all the login options * /openid_login/ AND /openid_login/done/ * /yahoo_login/ AND /yahoo_login/done/ * /gmail_login/ AND /gmail_login/done/ * /twitter_login/ AND /twitter_login/done/ * /facebook_login/done/ We dont have a start url here, as the starting tokens are set in a popup. * /github_login/ AND /github_login/done/ * /foursquare_login/ AND /foursquare_login/done/ Implementation --------------- #. Install required libraries. #. Get tokens and populate in localsettings.py #. Set the token callback urls correctly at Twitter, Facebook, Github and Foursquare. #. Set the authentication_backends to the providers you are using. ================================================ FILE: example_project/LICENSE ================================================ This work is dual licensed under (new) Copyright (c) 2009, Shabda Raaj and Contributors All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (To keep the older one around if in some remote chance you prefer this.) Copyright (c) 2009 by Shabda Raaj and Contributors * Usware Technologies * www.uswaretech.com "Building Amazing Webapps" All Rights Reserved Modified ATTRIBUTION ASSURANCE LICENSE (adapted from the original BSD license) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the conditions below are met. These conditions require a modest attribution to Author (the "Shabda Raaj"), who hopes that its promotional value may help justify the thousands of dollars in otherwise billable time invested in writing this and other freely available, open-source software. 1. Redistributions of source code, in whole or part and with or without modification (the "Code"), must display this GPG-signed text in verifiable form. 2. Redistributions of the Code in binary form must be accompanied by this GPG-signed text in any documentation and, each time the resulting executable program or a program dependent thereon is launched, a modest display (e.g., splash screen or banner text) of the Shabda Raaj's attribution information, which includes: (a) Name ("Shabda Raaj"), (b) Identification ("Usware Technologies"), and (c) URL ("http://uswaretech.com"). 3. Neither the name nor any trademark of the Shabda Raaj may be used to endorse or promote products derived from this software without specific prior written permission. 4. Users are entirely responsible, to the exclusion of the Shabda Raaj and any other persons, for compliance with (1) regulations set by owners or administrators of employed equipment, (2) licensing terms of any other software, and (3) local regulations regarding use, including those regarding import, export, and use of encryption software. THIS FREE SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR ANY CONTRIBUTOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, EFFECTS OF UNAUTHORIZED OR MALICIOUS NETWORK ACCESS; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. This work builds upon the work of many others. No ownership of their work is claimed or implied. They retain the ownership of their work, included here for convenience. On being bought to my notice, mentions would be gratefully provided. ================================================ FILE: example_project/__init__.py ================================================ ================================================ FILE: example_project/commentor/__init__.py ================================================ ================================================ FILE: example_project/commentor/models.py ================================================ from django.db import models class Comment(models.Model): comment = models.TextField() ================================================ FILE: example_project/commentor/templates/commentor/index.html ================================================ {% extends 'base.html' %} {% block main_content %} Enter Comment {% if user.is_authenticated %}

You are currently logged in as {{ user.username }}

{{ form }}
Logout {% else %} Login {% endif %} {% for comment in comments %}

{{ comment.comment }}

{% endfor %} {% endblock %} ================================================ FILE: example_project/commentor/tests.py ================================================ """ This file demonstrates two different styles of tests (one doctest and one unittest). These will both pass when you run "manage.py test". Replace these with more appropriate tests for your application. """ from django.test import TestCase class SimpleTest(TestCase): def test_basic_addition(self): """ Tests that 1 + 1 always equals 2. """ self.failUnlessEqual(1 + 1, 2) __test__ = {"doctest": """ Another way to test that 1 + 1 is equal to 2. >>> 1 + 1 == 2 True """} ================================================ FILE: example_project/commentor/urls.py ================================================ ================================================ FILE: example_project/commentor/views.py ================================================ from django.contrib.auth.decorators import login_required from django.http import HttpResponseRedirect from commentor.models import Comment from django.template import RequestContext from django import forms from django.shortcuts import render_to_response @login_required def leave_comment(request): form = CommentForm() if request.POST: form = CommentForm(data = request.POST) if form.is_valid(): form.save() return HttpResponseRedirect('.') payload = {'form':form, 'comments':Comment.objects.all()} return render_to_response('commentor/index.html', payload, RequestContext(request)) class CommentForm(forms.ModelForm): class Meta: model = Comment ================================================ FILE: example_project/example/README ================================================ This is an example app using socialauth as authentication backend We use modified django comments app to allow users to login via socialauth and post comments To use this app in your project: 1. Copy example and example_comments dirs to your project dir 2. Add 'example', 'example_comments', to your INSTALLED_APPS 3. Add (r'^comments/post/', 'example_comments.views.post_comment'), (r'comments/', include('django.contrib.comments.urls')), to your urlpatterns 4. Add COMMENTS_APP = 'example_comments' to your settings ================================================ FILE: example_project/example/__init__.py ================================================ ================================================ FILE: example_project/example/admin.py ================================================ from models import Post from django.contrib import admin admin.site.register(Post) ================================================ FILE: example_project/example/models.py ================================================ from django.db import models from django.contrib.auth.models import User # Create your models here. class Post(models.Model): author = models.ForeignKey(User) date = models.DateTimeField() title = models.CharField(max_length=100) post = models.TextField() def __unicode__(self): return self.title ================================================ FILE: example_project/example/templates/comments/example/form.html ================================================ {% load comments i18n %}
{% csrf_token %} {% if next %}{% endif %} {% for field in form %} {% if field.is_hidden %} {{ field }} {% else %} {% if field.errors %}{{ field.errors }}{% endif %} {% endif %} {% endfor %}

================================================ FILE: example_project/example/templates/comments/example/list.html ================================================
{% for comment in comment_list %}

{{ comment.comment }}

on {{ comment.submit_date|date }} at {{ comment.submit_date|time }} by {{ comment.name }}
{% endfor %}
================================================ FILE: example_project/example/templates/example/post_detail.html ================================================ {% load comments socialauth_tags %}

{{ post }}

{{ post.post }}


posted by {{ post.author }} on {{ post.date|date }} at {{ post.date|time }}

Comments:
{% render_comment_list for post %}
{% if request.user.username %} Comment as {% get_calculated_username request.user %} (Not {% get_calculated_username request.user %}?) {% render_comment_form for post %} {% else %} Login to comment {% endif %} ================================================ FILE: example_project/example/templates/example/post_list.html ================================================ {% load comments %} {{ latest }} {% if object_list %} {% for post in object_list %}

{{ post }}

{{ post.post }}

{% get_comment_count for post as comment_count %} {{ post.author }} wrote {{ post.post|wordcount }} words on {{ post.date|date }} at {{ post.date|time }}
{{ comment_count }} comments {% endfor %} {% else %} Add a new blog post {% endif %} ================================================ FILE: example_project/example/tests.py ================================================ """ This file demonstrates two different styles of tests (one doctest and one unittest). These will both pass when you run "manage.py test". Replace these with more appropriate tests for your application. """ from django.test import TestCase class SimpleTest(TestCase): def test_basic_addition(self): """ Tests that 1 + 1 always equals 2. """ self.failUnlessEqual(1 + 1, 2) __test__ = {"doctest": """ Another way to test that 1 + 1 is equal to 2. >>> 1 + 1 == 2 True """} ================================================ FILE: example_project/example/urls.py ================================================ from django.conf.urls.defaults import * from models import Post urlpatterns = patterns('', url(r'^(?P\d+)$', 'example.views.post_detail'), url(r'^$', 'django.views.generic.list_detail.object_list', { 'queryset' : Post.objects.all() } ), ) ================================================ FILE: example_project/example/views.py ================================================ # Create your views here. from django.shortcuts import render_to_response, get_object_or_404 from django.http import HttpResponseRedirect from django.contrib.comments.models import Comment from models import Post from django.template import RequestContext def comment_posted(request): comment_id = request.GET['c'] post_id = Comment.objects.get(id=comment_id).content_object.id return HttpResponseRedirect('/blog/'+str(post_id)) def post_detail(request, post_id): post = get_object_or_404(Post, pk=post_id) return render_to_response('example/post_detail.html', {'post': post}, context_instance=RequestContext(request)) ================================================ FILE: example_project/example_comments/__init__.py ================================================ from django import forms from django.utils.translation import ugettext_lazy as _ from django.contrib.comments.forms import CommentDetailsForm class CommentForm(CommentDetailsForm): name = forms.CharField(widget=forms.HiddenInput, required=False) email = forms.EmailField(widget=forms.HiddenInput, required=False) url = forms.URLField(widget=forms.HiddenInput, required=False) def get_form(): return CommentForm ================================================ FILE: example_project/example_comments/models.py ================================================ from django.db import models # Create your models here. ================================================ FILE: example_project/example_comments/tests.py ================================================ """ This file demonstrates two different styles of tests (one doctest and one unittest). These will both pass when you run "manage.py test". Replace these with more appropriate tests for your application. """ from django.test import TestCase class SimpleTest(TestCase): def test_basic_addition(self): """ Tests that 1 + 1 always equals 2. """ self.failUnlessEqual(1 + 1, 2) __test__ = {"doctest": """ Another way to test that 1 + 1 is equal to 2. >>> 1 + 1 == 2 True """} ================================================ FILE: example_project/example_comments/views.py ================================================ # Create your views here. from django.contrib.auth.decorators import login_required from django.contrib.comments.views.comments import post_comment as old_post_comment @login_required def post_comment(request): return old_post_comment(request) ================================================ FILE: example_project/localsettings.example.py ================================================ OPENID_REDIRECT_NEXT = '/accounts/openid/done/' OPENID_SREG = {"requred": "nickname, email, fullname", "optional":"postcode, country", "policy_url": ""} #example should be something more like the real thing, i think OPENID_AX = [{"type_uri": "http://axschema.org/contact/email", "count": 1, "required": True, "alias": "email"}, {"type_uri": "http://axschema.org/schema/fullname", "count":1 , "required": False, "alias": "fname"}] OPENID_AX_PROVIDER_MAP = {'Google': {'email': 'http://axschema.org/contact/email', 'firstname': 'http://axschema.org/namePerson/first', 'lastname': 'http://axschema.org/namePerson/last'}, 'Default': {'email': 'http://axschema.org/contact/email', 'fullname': 'http://axschema.org/namePerson', 'nickname': 'http://axschema.org/namePerson/friendly'} } TWITTER_CONSUMER_KEY = '' TWITTER_CONSUMER_SECRET = '' FACEBOOK_APP_ID = '' FACEBOOK_API_KEY = '' FACEBOOK_SECRET_KEY = '' LINKEDIN_CONSUMER_KEY = '' LINKEDIN_CONSUMER_SECRET = '' GITHUB_CLIENT_ID = '' GITHUB_CLIENT_SECRET = '' FOURSQUARE_CONSUMER_KEY = '' FOURSQUARE_CONSUMER_SECRET = '' FOURSQUARE_REGISTERED_REDIRECT_URI = '' ## if any of this information is desired for your app FACEBOOK_EXTENDED_PERMISSIONS = ( #'publish_stream', #'create_event', #'rsvp_event', #'sms', #'offline_access', #'email', #'read_stream', #'user_about_me', #'user_activites', #'user_birthday', #'user_education_history', #'user_events', #'user_groups', #'user_hometown', #'user_interests', #'user_likes', #'user_location', #'user_notes', #'user_online_presence', #'user_photo_video_tags', #'user_photos', #'user_relationships', #'user_religion_politics', #'user_status', #'user_videos', #'user_website', #'user_work_history', #'read_friendlists', #'read_requests', #'friend_about_me', #'friend_activites', #'friend_birthday', #'friend_education_history', #'friend_events', #'friend_groups', #'friend_hometown', #'friend_interests', #'friend_likes', #'friend_location', #'friend_notes', #'friend_online_presence', #'friend_photo_video_tags', #'friend_photos', #'friend_relationships', #'friend_religion_politics', #'friend_status', #'friend_videos', #'friend_website', #'friend_work_history', ) AUTHENTICATION_BACKENDS = ( 'django.contrib.auth.backends.ModelBackend', 'socialauth.auth_backends.OpenIdBackend', 'socialauth.auth_backends.TwitterBackend', 'socialauth.auth_backends.FacebookBackend', 'socialauth.auth_backends.LinkedInBackend', 'socialauth.auth_backends.GithubBackend', 'socialauth.auth_backends.FoursquareBackend', ) ================================================ FILE: example_project/manage.py ================================================ #!/usr/bin/env python from django.core.management import execute_manager try: import settings # Assumed to be in the same directory. except ImportError: import sys sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__) sys.exit(1) if __name__ == "__main__": execute_manager(settings) ================================================ FILE: example_project/media/css/openid.css ================================================ #openid_form { width: 400px; } #openid_form legend { font-weight: bold; } #openid_choice { display: none; } #openid_input_area { clear: both; padding: 10px; } #openid_btns, #openid_btns br { clear: both; } #openid_highlight { padding: 3px; background-color: #FFFCC9; float: left; } .openid_large_btn { width: 100px; height: 60px; border: 1px solid #DDD; margin: 3px; float: left; } .openid_small_btn { width: 24px; height: 24px; border: 1px solid #DDD; margin: 3px; float: left; } a.openid_large_btn:focus { outline: none; } a.openid_large_btn:focus{ -moz-outline-style: none; } .openid_selected { border: 4px solid #DDD; } ================================================ FILE: example_project/media/css/socialauth.css ================================================ body { margin-left: auto; margin-right: auto; width: 950px; } #openid_choice{ margin-top:10px; display: none; } #openid_input_area{ padding-top:10px; clear: both; } #openid_username{ margin-right:5px; } #openid_btns, #openid_btns br{ clear: both; } #openid_btns #facebook{ height: 60px; width: 110px; float: left; margin-top: 3px; display: table-cell; text-align: center; vertical-align: middle; border: 1px solid #ccc; } #openid_highlight{ padding: 3px; background-color: #FFFCC9; float: left; } .openid_large_btn{ width: 100px; height: 60px; border: 1px solid #DDD; margin: 3px; float: left; } .openid_small_btn{ width: 24px; height: 24px; border: 1px solid #DDD; margin: 3px; float: left; } a.openid_large_btn:focus{ outline: none; } a.openid_large_btn:focus{ -moz-outline-style: none; } .openid_selected{ border: 4px solid #DDD; } .linkedin{ background: #FFF url(/site_media/images/linkedin.jpg) no-repeat center center; } .yahoo{ background: #FFF url(/site_media/images/yahoo.gif) no-repeat center center; } .google{ background: #FFF url(/site_media/images/google.png) no-repeat center center; } .openid{ background: #FFF url(/site_media/images/openid.png) no-repeat center center; } .twitter{ background: #FFF url(/site_media/images/twitter.png) no-repeat center center; } ================================================ FILE: example_project/media/js/openid-jquery.js ================================================ /* Simple OpenID Plugin http://code.google.com/p/openid-selector/ This code is licenced under the New BSD License. */ var providers_large = { google: { name: 'Google', url: 'https://www.google.com/accounts/o8/id' }, yahoo: { name: 'Yahoo', url: 'https://me.yahoo.com/' }, aol: { name: 'AOL', label: 'Enter your AOL screenname.', url: 'http://openid.aol.com/{username}/' }, openid: { name: 'OpenID', label: 'Enter your OpenID.', url: null } }; var providers_small = { myopenid: { name: 'MyOpenID', label: 'Enter your MyOpenID username.', url: 'http://{username}.myopenid.com/' }, livejournal: { name: 'LiveJournal', label: 'Enter your Livejournal username.', url: 'http://{username}.livejournal.com/' }, flickr: { name: 'Flickr', label: 'Enter your Flickr username.', url: 'http://flickr.com/photos/{username}/' }, technorati: { name: 'Technorati', label: 'Enter your Technorati username.', url: 'http://technorati.com/people/technorati/{username}/' }, wordpress: { name: 'Wordpress', label: 'Enter your Wordpress.com username.', url: 'http://{username}.wordpress.com/' }, blogger: { name: 'Blogger', label: 'Your Blogger account', url: 'http://{username}.blogspot.com/' }, verisign: { name: 'Verisign', label: 'Your Verisign username', url: 'http://{username}.pip.verisignlabs.com/' }, vidoop: { name: 'Vidoop', label: 'Your Vidoop username', url: 'http://{username}.myvidoop.com/' }, verisign: { name: 'Verisign', label: 'Your Verisign username', url: 'http://{username}.pip.verisignlabs.com/' }, claimid: { name: 'ClaimID', label: 'Your ClaimID username', url: 'http://claimid.com/{username}' } }; var providers = $.extend({}, providers_large, providers_small); var openid = { cookie_expires: 6*30, // 6 months. cookie_name: 'openid_provider', cookie_path: '/', img_path: '/site_media/images/', input_id: null, provider_url: null, init: function(input_id) { var openid_btns = $('#openid_btns'); this.input_id = input_id; $('#openid_choice').show(); $('#openid_input_area').empty(); // add box for each provider for (id in providers_large) { openid_btns.append(this.getBoxHTML(providers_large[id], 'large', '.gif')); } if (providers_small) { openid_btns.append('
'); for (id in providers_small) { openid_btns.append(this.getBoxHTML(providers_small[id], 'small', '.ico')); } } $('#openid_form').submit(this.submit); var box_id = this.readCookie(); if (box_id) { this.signin(box_id, true); } }, getBoxHTML: function(provider, box_size, image_ext) { var box_id = provider["name"].toLowerCase(); return ''; }, /* Provider image click */ signin: function(box_id, onload) { var provider = providers[box_id]; if (! provider) { return; } this.highlight(box_id); this.setCookie(box_id); // prompt user for input? if (provider['label']) { this.useInputBox(provider); this.provider_url = provider['url']; } else { this.setOpenIdUrl(provider['url']); if (! onload) { $('#openid_form').submit(); } } }, /* Sign-in button click */ submit: function() { var url = openid.provider_url; if (url) { url = url.replace('{username}', $('#openid_username').val()); openid.setOpenIdUrl(url); } return true; }, setOpenIdUrl: function (url) { var hidden = $('#'+this.input_id); if (hidden.length > 0) { hidden.value = url; } else { $('#openid_form').append(''); } }, highlight: function (box_id) { // remove previous highlight. var highlight = $('#openid_highlight'); if (highlight) { highlight.replaceWith($('#openid_highlight a')[0]); } // add new highlight. $('.'+box_id).wrap('
'); }, setCookie: function (value) { var date = new Date(); date.setTime(date.getTime()+(this.cookie_expires*24*60*60*1000)); var expires = "; expires="+date.toGMTString(); document.cookie = this.cookie_name+"="+value+expires+"; path=" + this.cookie_path; }, readCookie: function () { var nameEQ = this.cookie_name + "="; var ca = document.cookie.split(';'); for(var i=0;i < ca.length;i++) { var c = ca[i]; while (c.charAt(0)==' ') c = c.substring(1,c.length); if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length); } return null; }, useInputBox: function (provider) { var input_area = $('#openid_input_area'); var html = ''; var id = 'openid_username'; var value = ''; var label = provider['label']; var style = ''; if (label) { html = '

' + label + '

'; } if (provider['name'] == 'OpenID') { id = this.input_id; value = 'http://'; style = 'background:#FFF url('+this.img_path+'openid-inputicon.gif) no-repeat scroll 0 50%; padding-left:18px;'; } html += '' + ''; input_area.empty(); input_area.append(html); $('#'+id).focus(); } }; ================================================ FILE: example_project/settings.py ================================================ # Django settings for socialauthdemo project. DEBUG = True TEMPLATE_DEBUG = DEBUG ADMINS = ( # ('Your Name', 'your_email@domain.com'), ) MANAGERS = ADMINS DATABASE_ENGINE = 'sqlite3' # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'. DATABASE_NAME = 'data.db' # Or path to database file if using sqlite3. DATABASE_USER = '' # Not used with sqlite3. DATABASE_PASSWORD = '' # Not used with sqlite3. DATABASE_HOST = '' # Set to empty string for localhost. Not used with sqlite3. DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3. # Local time zone for this installation. Choices can be found here: # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name # although not all choices may be available on all operating systems. # If running in a Windows environment this must be set to the same as your # system time zone. TIME_ZONE = 'America/Chicago' # Language code for this installation. All choices can be found here: # http://www.i18nguy.com/unicode/language-identifiers.html LANGUAGE_CODE = 'en-us' SITE_ID = 1 # If you set this to False, Django will make some optimizations so as not # to load the internationalization machinery. USE_I18N = True # Absolute path to the directory that holds media. # Example: "/home/media/media.lawrence.com/" MEDIA_ROOT = '' # URL that handles the media served from MEDIA_ROOT. Make sure to use a # trailing slash if there is a path component (optional in other cases). # Examples: "http://media.lawrence.com", "http://example.com/media/" MEDIA_URL = '' # URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a # trailing slash. # Examples: "http://foo.com/media/", "/media/". ADMIN_MEDIA_PREFIX = '/media/' # Make this unique, and don't share it with anybody. SECRET_KEY = 'tnrm*t2b80nu51mgz3kc2%_v6bv8e)8rup$03m)dp7xb0)m8t9' # List of callables that know how to import templates from various sources. TEMPLATE_LOADERS = ( 'django.template.loaders.filesystem.load_template_source', 'django.template.loaders.app_directories.load_template_source', # 'django.template.loaders.eggs.load_template_source', ) MIDDLEWARE_CLASSES = ( 'django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'openid_consumer.middleware.OpenIDMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', #'socialauth.middleware.FacebookConnectMiddleware' ) TEMPLATE_CONTEXT_PROCESSORS = ( "socialauth.context_processors.facebook_api_key", 'django.core.context_processors.media', "django.contrib.auth.context_processors.auth", "django.core.context_processors.request", ) ROOT_URLCONF = 'urls' TEMPLATE_DIRS = ( # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". # Always use forward slashes, even on Windows. # Don't forget to use absolute paths, not relative paths. ) INSTALLED_APPS = ( 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.sites', 'django.contrib.admin', 'django.contrib.comments', 'socialauth', 'openid_consumer', 'commentor', 'example', 'example_comments', ) COMMENTS_APP = 'example_comments' LOGIN_REDIRECT_URL = '/' LOGOUT_REDIRECT_URL = '/' try: from localsettings import * except ImportError: pass import os MEDIA_ROOT = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'media')) MEDIA_URL = '/site_media/' SITE_NAME = 'foobar' ================================================ FILE: example_project/urls.py ================================================ from django.conf.urls.defaults import * from commentor.views import leave_comment from django.contrib import admin admin.autodiscover() urlpatterns = patterns('', (r'^blog/', include('example.urls')), (r'^accounts/', include('socialauth.urls')), (r'^admin/', admin.site.urls), #(r'^$', leave_comment), (r'^$', 'socialauth.views.signin_complete'), (r'^comments/post/', 'example_comments.views.post_comment'), (r'comments/', include('django.contrib.comments.urls')), ) from django.conf import settings if settings.DEBUG: urlpatterns += patterns('', # This is for the CSS and static files: (r'^site_media/(?P.*)$', 'django.views.static.serve', {'document_root': settings.MEDIA_ROOT}), ) ================================================ FILE: ez_setup.py ================================================ #!python """Bootstrap setuptools installation If you want to use setuptools in your package's setup.py, just include this file in the same directory with it, and add this to the top of your setup.py:: from ez_setup import use_setuptools use_setuptools() If you want to require a specific version of setuptools, set a download mirror, or use an alternate download directory, you can do so by supplying the appropriate options to ``use_setuptools()``. This file can also be run as a script to install or upgrade setuptools. """ import sys DEFAULT_VERSION = "0.6c9" DEFAULT_URL = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3] md5_data = { 'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca', 'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb', 'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b', 'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a', 'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618', 'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac', 'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5', 'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4', 'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c', 'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b', 'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27', 'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277', 'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa', 'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e', 'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e', 'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f', 'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2', 'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc', 'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167', 'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64', 'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d', 'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20', 'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab', 'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53', 'setuptools-0.6c7-py2.3.egg': '209fdf9adc3a615e5115b725658e13e2', 'setuptools-0.6c7-py2.4.egg': '5a8f954807d46a0fb67cf1f26c55a82e', 'setuptools-0.6c7-py2.5.egg': '45d2ad28f9750e7434111fde831e8372', 'setuptools-0.6c8-py2.3.egg': '50759d29b349db8cfd807ba8303f1902', 'setuptools-0.6c8-py2.4.egg': 'cba38d74f7d483c06e9daa6070cce6de', 'setuptools-0.6c8-py2.5.egg': '1721747ee329dc150590a58b3e1ac95b', 'setuptools-0.6c9-py2.3.egg': 'a83c4020414807b496e4cfbe08507c03', 'setuptools-0.6c9-py2.4.egg': '260a2be2e5388d66bdaee06abec6342a', 'setuptools-0.6c9-py2.5.egg': 'fe67c3e5a17b12c0e7c541b7ea43a8e6', 'setuptools-0.6c9-py2.6.egg': 'ca37b1ff16fa2ede6e19383e7b59245a', } import sys, os try: from hashlib import md5 except ImportError: from md5 import md5 def _validate_md5(egg_name, data): if egg_name in md5_data: digest = md5(data).hexdigest() if digest != md5_data[egg_name]: print >>sys.stderr, ( "md5 validation of %s failed! (Possible download problem?)" % egg_name ) sys.exit(2) return data def use_setuptools( version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, download_delay=15 ): """Automatically find/download setuptools and make it available on sys.path `version` should be a valid setuptools version number that is available as an egg for download under the `download_base` URL (which should end with a '/'). `to_dir` is the directory where setuptools will be downloaded, if it is not already available. If `download_delay` is specified, it should be the number of seconds that will be paused before initiating a download, should one be required. If an older version of setuptools is installed, this routine will print a message to ``sys.stderr`` and raise SystemExit in an attempt to abort the calling script. """ was_imported = 'pkg_resources' in sys.modules or 'setuptools' in sys.modules def do_download(): egg = download_setuptools(version, download_base, to_dir, download_delay) sys.path.insert(0, egg) import setuptools; setuptools.bootstrap_install_from = egg try: import pkg_resources except ImportError: return do_download() try: pkg_resources.require("setuptools>="+version); return except pkg_resources.VersionConflict, e: if was_imported: print >>sys.stderr, ( "The required version of setuptools (>=%s) is not available, and\n" "can't be installed while this script is running. Please install\n" " a more recent version first, using 'easy_install -U setuptools'." "\n\n(Currently using %r)" ) % (version, e.args[0]) sys.exit(2) else: del pkg_resources, sys.modules['pkg_resources'] # reload ok return do_download() except pkg_resources.DistributionNotFound: return do_download() def download_setuptools( version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, delay = 15 ): """Download setuptools from a specified location and return its filename `version` should be a valid setuptools version number that is available as an egg for download under the `download_base` URL (which should end with a '/'). `to_dir` is the directory where the egg will be downloaded. `delay` is the number of seconds to pause before an actual download attempt. """ import urllib2, shutil egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3]) url = download_base + egg_name saveto = os.path.join(to_dir, egg_name) src = dst = None if not os.path.exists(saveto): # Avoid repeated downloads try: from distutils import log if delay: log.warn(""" --------------------------------------------------------------------------- This script requires setuptools version %s to run (even to display help). I will attempt to download it for you (from %s), but you may need to enable firewall access for this script first. I will start the download in %d seconds. (Note: if this machine does not have network access, please obtain the file %s and place it in this directory before rerunning this script.) ---------------------------------------------------------------------------""", version, download_base, delay, url ); from time import sleep; sleep(delay) log.warn("Downloading %s", url) src = urllib2.urlopen(url) # Read/write all in one block, so we don't create a corrupt file # if the download is interrupted. data = _validate_md5(egg_name, src.read()) dst = open(saveto,"wb"); dst.write(data) finally: if src: src.close() if dst: dst.close() return os.path.realpath(saveto) def main(argv, version=DEFAULT_VERSION): """Install or upgrade setuptools and EasyInstall""" try: import setuptools except ImportError: egg = None try: egg = download_setuptools(version, delay=0) sys.path.insert(0,egg) from setuptools.command.easy_install import main return main(list(argv)+[egg]) # we're done here finally: if egg and os.path.exists(egg): os.unlink(egg) else: if setuptools.__version__ == '0.0.1': print >>sys.stderr, ( "You have an obsolete version of setuptools installed. Please\n" "remove it from your system entirely before rerunning this script." ) sys.exit(2) req = "setuptools>="+version import pkg_resources try: pkg_resources.require(req) except pkg_resources.VersionConflict: try: from setuptools.command.easy_install import main except ImportError: from easy_install import main main(list(argv)+[download_setuptools(delay=0)]) sys.exit(0) # try to force an exit else: if argv: from setuptools.command.easy_install import main main(argv) else: print "Setuptools version",version,"or greater has been installed." print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)' def update_md5(filenames): """Update our built-in md5 registry""" import re for name in filenames: base = os.path.basename(name) f = open(name,'rb') md5_data[base] = md5(f.read()).hexdigest() f.close() data = [" %r: %r,\n" % it for it in md5_data.items()] data.sort() repl = "".join(data) import inspect srcfile = inspect.getsourcefile(sys.modules[__name__]) f = open(srcfile, 'rb'); src = f.read(); f.close() match = re.search("\nmd5_data = {\n([^}]+)}", src) if not match: print >>sys.stderr, "Internal error!" sys.exit(2) src = src[:match.start(1)] + repl + src[match.end(1):] f = open(srcfile,'w') f.write(src) f.close() if __name__=='__main__': if len(sys.argv)>2 and sys.argv[1]=='--md5update': update_md5(sys.argv[2:]) else: main(sys.argv[1:]) ================================================ FILE: openid_consumer/__init__.py ================================================ ================================================ FILE: openid_consumer/locale/en/LC_MESSAGES/django.po ================================================ # English language file for django-openid-consumer package # Copyright (C) 2008 Matjaž Črnko # This file is distributed under the same license as the django-openid-consumer package. # Matjaž Črnko , 2008. #, fuzzy msgid "" msgstr "" "Project-Id-Version: 0.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2008-08-07 13:23+0200\n" "PO-Revision-Date: 2008-8-1 21:02+0100\n" "Last-Translator: Matjaž Črnko \n" "Language-Team: none\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: views.py:91 msgid "i-names are not supported" msgstr "i-names are not supported" #: views.py:97 msgid "The OpenID was invalid" msgstr "The OpenID was invalid" #: views.py:128 msgid "The request was cancelled" msgstr "The request was cancelled" #: views.py:132 msgid "Setup needed" msgstr "Setup needed" #~ msgid "OpenID failed" #~ msgstr "OpenID failed" #~ msgid "Sign in with your OpenID" #~ msgstr "Sign in with your OpenID" #~ msgid "Sign in" #~ msgstr "Sign in" ================================================ FILE: openid_consumer/locale/sl/LC_MESSAGES/django.po ================================================ # Slovenski prevod django-openid-consumer paketa # Copyright (C) 2008 Matjaž Črnko # This file is distributed under the same license as the django-openid-consumer package. # Matjaž Črnko , 2008. #, fuzzy msgid "" msgstr "" "Project-Id-Version: 0.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2008-08-07 13:23+0200\n" "PO-Revision-Date: 2008-8-1 21:02+0100\n" "Last-Translator: Matjaž Črnko \n" "Language-Team: none\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: views.py:91 msgid "i-names are not supported" msgstr "i-name ni podprt" #: views.py:97 msgid "The OpenID was invalid" msgstr "OpenID je neveljaven" #: views.py:128 msgid "The request was cancelled" msgstr "Povezava je bila prekinjena" #: views.py:132 msgid "Setup needed" msgstr "Postavitev potrebna" #~ msgid "OpenID failed" #~ msgstr "Napaka pri prijavi z OpenID" #~ msgid "Sign in with your OpenID" #~ msgstr "Prijavite se z OpenID" #~ msgid "Sign in" #~ msgstr "Prijava" ================================================ FILE: openid_consumer/middleware.py ================================================ class OpenIDMiddleware(object): """ Populate request.openid and request.openids with their openid. This comes either from their cookie or from their session, depending on the presence of OPENID_USE_SESSIONS. """ def process_request(self, request): request.openids = request.session.get('openids', []) if request.openids: request.openid = request.openids[-1] # Last authenticated OpenID else: request.openid = None ================================================ FILE: openid_consumer/models.py ================================================ from django.db import models class Nonce(models.Model): server_url = models.URLField() timestamp = models.IntegerField() salt = models.CharField( max_length=50 ) def __unicode__(self): return "Nonce: %s" % self.nonce class Association(models.Model): server_url = models.TextField(max_length=2047) handle = models.CharField(max_length=255) secret = models.TextField(max_length=255) # Stored base64 encoded issued = models.IntegerField() lifetime = models.IntegerField() assoc_type = models.TextField(max_length=64) def __unicode__(self): return "Association: %s, %s" % (self.server_url, self.handle) ================================================ FILE: openid_consumer/templates/openid_consumer/failure.html ================================================ {% extends 'openid_consumer/openid_consumer_base.html' %} {% load i18n %} {% block title %} {{ block.super }} - {% trans "OpenID failed" %} {% endblock %} {% block main_content %}

{% trans "OpenID failed" %}:

{{ message|escape }}





{% endblock %} {# {% load i18n %} {% trans "OpenID failed" %}

{% trans "OpenID failed" %}:

{{ message|escape }}

#} ================================================ FILE: openid_consumer/templates/openid_consumer/openid_consumer_base.html ================================================ {% extends 'socialauth/base.html' %} ================================================ FILE: openid_consumer/templates/openid_consumer/signin.html ================================================ {% extends 'openid_consumer/openid_consumer_base.html' %} {% load i18n %} {% block title %} {{ block.super }} - Sign in with your OpenID {% endblock %} {% block main_content %}

{% trans "Sign in with your OpenID" %}





{% endblock %} {# {% trans "Sign in with your OpenID" %} #} ================================================ FILE: openid_consumer/util.py ================================================ import openid if openid.__version__ < '2.1.0': from openid.sreg import SRegResponse else: from openid.extensions.sreg import SRegResponse from openid.extensions.ax import FetchResponse as AXFetchResponse try: from openid.extensions.pape import Response as PapeResponse except ImportError: from openid.extensions import pape as openid_pape PapeResponse = openid_pape.Response from openid.store import nonce as oid_nonce from openid.store.interface import OpenIDStore from openid.association import Association as OIDAssociation from openid.yadis import xri import time, base64, hashlib from django.conf import settings from models import Association, Nonce class OpenID: def __init__(self, openid, issued, attrs=None, sreg=None, pape=None, ax=None): self.openid = openid self.issued = issued self.attrs = attrs or {} self.sreg = sreg or {} self.pape = pape or {} self.ax = ax or {} self.is_iname = (xri.identifierScheme(openid) == 'XRI') def __repr__(self): return '' % self.openid def __str__(self): return self.openid class DjangoOpenIDStore(OpenIDStore): def __init__(self): self.max_nonce_age = 6 * 60 * 60 # Six hours def storeAssociation(self, server_url, association): assoc = Association( server_url = server_url, handle = association.handle, secret = base64.encodestring(association.secret), issued = association.issued, lifetime = association.issued, assoc_type = association.assoc_type ) assoc.save() def getAssociation(self, server_url, handle=None): assocs = [] if handle is not None: assocs = Association.objects.filter( server_url = server_url, handle = handle ) else: assocs = Association.objects.filter( server_url = server_url ) if not assocs: return None associations = [] for assoc in assocs: association = OIDAssociation( assoc.handle, base64.decodestring(assoc.secret), assoc.issued, assoc.lifetime, assoc.assoc_type ) if association.getExpiresIn() == 0: self.removeAssociation(server_url, assoc.handle) else: associations.append((association.issued, association)) if not associations: return None return associations[-1][1] def removeAssociation(self, server_url, handle): assocs = list(Association.objects.filter( server_url = server_url, handle = handle )) assocs_exist = len(assocs) > 0 for assoc in assocs: assoc.delete() return assocs_exist def storeNonce(self, nonce): nonce, created = Nonce.objects.get_or_create( nonce = nonce, defaults={'expires': int(time.time())} ) def useNonce(self, server_url, timestamp, salt): if abs(timestamp - time.time()) > oid_nonce.SKEW: return False try: nonce = Nonce(server_url=server_url, timestamp=timestamp, salt=salt) nonce.save() except: raise else: return 1 def getAuthKey(self): # Use first AUTH_KEY_LEN characters of md5 hash of SECRET_KEY return hashlib.md5(settings.SECRET_KEY).hexdigest()[:self.AUTH_KEY_LEN] def from_openid_response(openid_response): issued = int(time.time()) openid = OpenID(openid_response.identity_url, issued, openid_response.signed_fields) if getattr(settings, 'OPENID_PAPE', False): openid.pape = PapeResponse.fromSuccessResponse(openid_response) if getattr(settings, 'OPENID_SREG', False): openid.sreg = SRegResponse.fromSuccessResponse(openid_response) if getattr(settings, 'OPENID_AX', False): openid.ax = AXFetchResponse.fromSuccessResponse(openid_response) return openid ================================================ FILE: openid_consumer/views.py ================================================ from django.http import HttpResponse, HttpResponseRedirect from django.shortcuts import render_to_response as render from django.template import RequestContext from django.conf import settings from django.utils.translation import ugettext_lazy as _ import re, time, urllib import openid if openid.__version__ < '2.0.0': raise ImportError, 'You need python-openid 2.0.0 or newer' elif openid.__version__ < '2.1.0': from openid.sreg import SRegRequest else: from openid.extensions.sreg import SRegRequest try: from openid.extensions.pape import Request as PapeRequest except ImportError: from openid.extensions import pape as openid_pape PapeRequest = openid_pape.Request from openid.extensions.ax import FetchRequest as AXFetchRequest from openid.extensions.ax import AttrInfo from openid.consumer.consumer import Consumer, \ SUCCESS, CANCEL, FAILURE, SETUP_NEEDED from openid.consumer.discover import DiscoveryFailure from openid.yadis import xri from util import OpenID, DjangoOpenIDStore, from_openid_response from middleware import OpenIDMiddleware from django.utils.html import escape def get_url_host(request): if request.is_secure(): protocol = 'https' else: protocol = 'http' host = escape(request.get_host()) return '%s://%s' % (protocol, host) def get_full_url(request): return get_url_host(request) + request.get_full_path() next_url_re = re.compile('^/[-\w/]+$') def is_valid_next_url(next): # When we allow this: # /openid/?next=/welcome/ # For security reasons we want to restrict the next= bit to being a local # path, not a complete URL. return bool(next_url_re.match(next)) def begin(request, redirect_to=None, on_failure=None, user_url=None, template_name='openid_consumer/signin.html'): on_failure = on_failure or default_on_failure trust_root = getattr( settings, 'OPENID_TRUST_ROOT', get_url_host(request) + '/' ) # foo derbis. redirect_to = redirect_to or getattr( settings, 'OPENID_REDIRECT_TO', # If not explicitly set, assume current URL with complete/ appended get_full_url(request).split('?')[0] + 'complete/' ) # In case they were lazy... if not ( redirect_to.startswith('http://') or redirect_to.startswith('https://')): redirect_to = get_url_host(request) + redirect_to if request.GET.get('next') and is_valid_next_url(request.GET['next']): if '?' in redirect_to: join = '&' else: join = '?' redirect_to += join + urllib.urlencode({ 'next': request.GET['next'] }) if not user_url: user_url = request.REQUEST.get('openid_url', None) if not user_url: request_path = request.path if request.GET.get('next'): request_path += '?' + urllib.urlencode({ 'next': request.GET['next'] }) return render(template_name, { 'action': request_path, }, RequestContext(request)) if xri.identifierScheme(user_url) == 'XRI' and getattr( settings, 'OPENID_DISALLOW_INAMES', False ): return on_failure(request, _('i-names are not supported')) consumer = Consumer(request.session, DjangoOpenIDStore()) try: auth_request = consumer.begin(user_url) except DiscoveryFailure: return on_failure(request, _('The OpenID was invalid')) sreg = getattr(settings, 'OPENID_SREG', False) if sreg: s = SRegRequest() for sarg in sreg: if sarg.lower().lstrip() == "policy_url": s.policy_url = sreg[sarg] else: for v in sreg[sarg].split(','): s.requestField(field_name=v.lower().lstrip(), required=(sarg.lower().lstrip() == "required")) auth_request.addExtension(s) pape = getattr(settings, 'OPENID_PAPE', False) if pape: if openid.__version__ <= '2.0.0' and openid.__version__ >= '2.1.0': raise (ImportError, 'For pape extension you need python-openid 2.1.0 or newer') p = PapeRequest() for parg in pape: if parg.lower().strip() == 'policy_list': for v in pape[parg].split(','): p.addPolicyURI(v) elif parg.lower().strip() == 'max_auth_age': p.max_auth_age = pape[parg] auth_request.addExtension(p) OPENID_AX_PROVIDER_MAP = getattr(settings, 'OPENID_AX_PROVIDER_MAP', {}) openid_provider = ('Google' if 'google' in request.session.get('openid_provider', '') else 'Default') ax = OPENID_AX_PROVIDER_MAP.get(openid_provider) if ax: axr = AXFetchRequest() for attr_name, attr_url in ax.items(): # axr.add(AttrInfo(i['type_uri'], # i['count'], i['required'], # i['alias'])) # setting all as required attrs axr.add(AttrInfo(attr_url, required=True)) auth_request.addExtension(axr) redirect_url = auth_request.redirectURL(trust_root, redirect_to) return HttpResponseRedirect(redirect_url) def complete(request, on_success=None, on_failure=None, failure_template='openid_consumer/failure.html'): on_success = on_success or default_on_success on_failure = on_failure or default_on_failure consumer = Consumer(request.session, DjangoOpenIDStore()) #dummydebug #for r in request.GET.items(): # print r # JanRain library raises a warning if passed unicode objects as the keys, # so we convert to bytestrings before passing to the library query_dict = dict([ (k.encode('utf8'), v.encode('utf8')) for k, v in request.REQUEST.items() ]) url = get_url_host(request) + request.path openid_response = consumer.complete(query_dict, url) if openid_response.status == SUCCESS: return on_success(request, openid_response.identity_url, openid_response) elif openid_response.status == CANCEL: return on_failure(request, _('The request was cancelled'), failure_template) elif openid_response.status == FAILURE: return on_failure(request, openid_response.message, failure_template) elif openid_response.status == SETUP_NEEDED: return on_failure(request, _('Setup needed'), failure_template) else: assert False, "Bad openid status: %s" % openid_response.status def default_on_success(request, identity_url, openid_response): if 'openids' not in request.session.keys(): request.session['openids'] = [] # Eliminate any duplicates request.session['openids'] = [ o for o in request.session['openids'] if o.openid != identity_url ] request.session['openids'].append(from_openid_response(openid_response)) # Set up request.openids and request.openid, reusing middleware logic OpenIDMiddleware().process_request(request) next = request.GET.get('next', '').strip() if not next or not is_valid_next_url(next): next = getattr(settings, 'OPENID_REDIRECT_NEXT', '/') return HttpResponseRedirect(next) def default_on_failure(request, message, template_name='openid_consumer/failure.html'): return render(template_name, { 'message': message }, RequestContext(request)) def signout(request): request.session['openids'] = [] next = request.GET.get('next', '/') if not is_valid_next_url(next): next = '/' return HttpResponseRedirect(next) ================================================ FILE: requirements.txt ================================================ python-openid>=2.0.0 oauth>=1.0.0 facebook-sdk hg+https://pylinkedin.googlecode.com/hg/pylinkedin>=0.1 ================================================ FILE: setup.py ================================================ import ez_setup ez_setup.use_setuptools() from setuptools import setup, find_packages setup( name = "django-socialauth", version = "0.1.2c", packages = ['socialauth', 'socialauth/lib', 'socialauth/templatetags', 'openid_consumer'], package_data = { 'socialauth': [ 'templates/*.html', 'templates/socialauth/*.html', 'templates/socialauth/*.htm', 'templates/openid/*.html'], 'openid_consumer': ['locale/*/LC_MESSAGES/*.po'] }, zip_safe = False, author = "Agiliq Solutions", author_email = "info@uswaretech.com", description = "Allows logging via Facebook, Yahoo, Gmail, Twitter and Openid ", url = "http://agiliq.com/", ) ================================================ FILE: socialauth/__init__.py ================================================ ================================================ FILE: socialauth/admin.py ================================================ from socialauth.models import AuthMeta, OpenidProfile, TwitterUserProfile, \ FacebookUserProfile, LinkedInUserProfile, GithubUserProfile, FoursquareUserProfile from django.contrib import admin admin.site.register(AuthMeta) admin.site.register(OpenidProfile) admin.site.register(TwitterUserProfile) admin.site.register(FacebookUserProfile) admin.site.register(LinkedInUserProfile) admin.site.register(GithubUserProfile) admin.site.register(FoursquareUserProfile) ================================================ FILE: socialauth/auth_backends.py ================================================ from django.contrib.auth.models import User from django.contrib.sites.models import Site from django.core.urlresolvers import reverse from django.conf import settings import facebook import urllib from socialauth.lib import oauthtwitter2 as oauthtwitter from socialauth.models import OpenidProfile as UserAssociation, \ TwitterUserProfile, FacebookUserProfile, LinkedInUserProfile, AuthMeta, GithubUserProfile, FoursquareUserProfile from socialauth.lib.linkedin import * import random from cgi import parse_qs TWITTER_CONSUMER_KEY = getattr(settings, 'TWITTER_CONSUMER_KEY', '') TWITTER_CONSUMER_SECRET = getattr(settings, 'TWITTER_CONSUMER_SECRET', '') FACEBOOK_APP_ID = getattr(settings, 'FACEBOOK_APP_ID', '') FACEBOOK_API_KEY = getattr(settings, 'FACEBOOK_API_KEY', '') FACEBOOK_SECRET_KEY = getattr(settings, 'FACEBOOK_SECRET_KEY', '') # Linkedin LINKEDIN_CONSUMER_KEY = getattr(settings, 'LINKEDIN_CONSUMER_KEY', '') LINKEDIN_CONSUMER_SECRET = getattr(settings, 'LINKEDIN_CONSUMER_SECRET', '') # OpenId setting map OPENID_AX_PROVIDER_MAP = getattr(settings, 'OPENID_AX_PROVIDER_MAP', {}) class OpenIdBackend: supports_object_permissions = False supports_anonymous_user = False supports_inactive_user = False def authenticate(self, openid_key, request, provider, user=None): try: assoc = UserAssociation.objects.get(openid_key=openid_key) return assoc.user except UserAssociation.DoesNotExist: #fetch if openid provider provides any simple registration fields nickname = None email = None firstname = None lastname = None if request.openid and request.openid.sreg: email = request.openid.sreg.get('email') nickname = request.openid.sreg.get('nickname') firstname, lastname = (request.openid.sreg .get('fullname', ' ') .split(' ', 1)) elif request.openid and request.openid.ax: email = \ (request.openid.ax .getSingle('http://axschema.org/contact/email')) if 'google' in provider: ax_schema = OPENID_AX_PROVIDER_MAP['Google'] firstname = (request.openid.ax .getSingle(ax_schema['firstname'])) lastname = (request.openid.ax .getSingle(ax_schema['lastname'])) nickname = email.split('@')[0] else: ax_schema = OPENID_AX_PROVIDER_MAP['Default'] try: #should be replaced by correct schema nickname = (request.openid.ax .getSingle(ax_schema['nickname'])) (firstname, lastname) = (request.openid.ax .getSingle(ax_schema['fullname']) .split(' ', 1)) except: pass if nickname is None : nickname = ''.join( [random .choice('abcdefghijklmnopqrstuvwxyz') for i in xrange(10)]) name_count = (User.objects .filter(username__startswith=nickname) .count()) if name_count: username = '%s%d' % (nickname, name_count + 1) else: username = '%s' % (nickname) if email is None : valid_username = False email = "%s@socialauth" % (username) else: valid_username = True if not user: user = User.objects.create_user(username, email) user.first_name = firstname user.last_name = lastname user.save() #create openid association assoc = UserAssociation() assoc.openid_key = openid_key assoc.user = user if email: assoc.email = email if nickname: assoc.nickname = nickname if valid_username: assoc.is_username_valid = True assoc.save() #Create AuthMeta # auth_meta = # AuthMeta(user=user, # provider=provider, # provider_model='OpenidProfile', # provider_id=assoc.pk) auth_meta = AuthMeta(user=user, provider=provider) auth_meta.save() return user def GooglesAX(self,openid_response): email = (openid_response.ax .getSingle('http://axschema.org/contact/email')) firstname = (openid_response.ax .getSingle('http://axschema.org/namePerson/first')) lastname = (openid_response.ax .getSingle('http://axschema.org/namePerson/last')) # country = # (openid_response.ax # .getSingle('http://axschema.org/contact/country/home')) # language = # openid_response.ax.getSingle('http://axschema.org/pref/language') return locals() def get_user(self, user_id): try: user = User.objects.get(pk=user_id) return user except User.DoesNotExist: return None class LinkedInBackend: """LinkedInBackend for authentication""" def authenticate(self, linkedin_access_token, user=None): linkedin = LinkedIn(LINKEDIN_CONSUMER_KEY, LINKEDIN_CONSUMER_SECRET) # get their profile profile = (ProfileApi(linkedin) .getMyProfile(access_token=linkedin_access_token)) try: user_profile = (LinkedInUserProfile.objects .get(linkedin_uid=profile.id)) user = user_profile.user return user except LinkedInUserProfile.DoesNotExist: # Create a new user username = 'LI:%s' % profile.id if not user: user = User(username=username) user.first_name, user.last_name = (profile.firstname , profile.lastname) user.email = '%s@socialauth' % (username) user.save() userprofile = LinkedInUserProfile(user=user, linkedin_uid=profile.id) userprofile.save() auth_meta = AuthMeta(user=user, provider='LinkedIn').save() return user def get_user(self, user_id): try: return User.objects.get(pk=user_id) except: return None class TwitterBackend: """TwitterBackend for authentication""" def authenticate(self, twitter_access_token, user=None): ''' authenticates the token by requesting user information from twitter ''' # twitter = oauthtwitter.OAuthApi(TWITTER_CONSUMER_KEY, # TWITTER_CONSUMER_SECRET, # twitter_access_token) twitter = oauthtwitter.TwitterOAuthClient( settings.TWITTER_CONSUMER_KEY, settings.TWITTER_CONSUMER_SECRET) try: userinfo = twitter.get_user_info(twitter_access_token) except: # If we cannot get the user information, # user cannot be authenticated raise screen_name = userinfo.screen_name twitter_id = userinfo.id try: user_profile = (TwitterUserProfile.objects .get(screen_name=screen_name)) # Update Twitter Profile user_profile.url = userinfo.url user_profile.location = userinfo.location user_profile.description = userinfo.description user_profile.profile_image_url = userinfo.profile_image_url user_profile.save() user = user_profile.user return user except TwitterUserProfile.DoesNotExist: # Create new user if not user: same_name_count = (User.objects .filter(username__startswith=screen_name) .count()) if same_name_count: username = '%s%s' % (screen_name, same_name_count + 1) else: username = screen_name user = User(username=username) name_data = userinfo.name.split() try: first_name, last_name = (name_data[0], ' '.join(name_data[1:])) except: first_name, last_name = screen_name, '' user.first_name, user.last_name = first_name, last_name #user.email = screen_name + "@socialauth" #user.email = '%s@example.twitter.com'%(userinfo.screen_name) user.save() user_profile = TwitterUserProfile(user=user, screen_name=screen_name) user_profile.access_token = str(twitter_access_token) user_profile.url = userinfo.url user_profile.location = userinfo.location user_profile.description = userinfo.description user_profile.profile_image_url = userinfo.profile_image_url user_profile.save() auth_meta = AuthMeta(user=user, provider='Twitter').save() return user def get_user(self, user_id): try: return User.objects.get(pk=user_id) except: return None class FacebookBackend: def authenticate(self, request, user=None): cookie = facebook.get_user_from_cookie(request.COOKIES, FACEBOOK_APP_ID, FACEBOOK_SECRET_KEY) if cookie: uid = cookie['uid'] access_token = cookie['access_token'] else: # if cookie does not exist # assume logging in normal way params = {} params["client_id"] = FACEBOOK_APP_ID params["client_secret"] = FACEBOOK_SECRET_KEY params["redirect_uri"] = '%s://%s%s' % ( 'https' if request.is_secure() else 'http', Site.objects.get_current().domain, reverse("socialauth_facebook_login_done")) params["code"] = request.GET.get('code', '') url = ("https://graph.facebook.com/oauth/access_token?" + urllib.urlencode(params)) from cgi import parse_qs userdata = urllib.urlopen(url).read() res_parse_qs = parse_qs(userdata) # Could be a bot query if not res_parse_qs.has_key('access_token'): return None access_token = res_parse_qs['access_token'][-1] graph = facebook.GraphAPI(access_token) uid = graph.get_object('me')['id'] try: fb_user = FacebookUserProfile.objects.get(facebook_uid=uid) return fb_user.user except FacebookUserProfile.DoesNotExist: # create new FacebookUserProfile graph = facebook.GraphAPI(access_token) fb_data = graph.get_object("me") if not fb_data: return None username = uid if not user: user = User.objects.create(username=username) user.first_name = fb_data['first_name'] user.last_name = fb_data['last_name'] user.save() fb_profile = FacebookUserProfile(facebook_uid=uid, user=user) fb_profile.save() auth_meta = AuthMeta(user=user, provider='Facebook').save() return user def get_user(self, user_id): try: return User.objects.get(pk=user_id) except: return None class GithubBackend: def authenticate(self, github_access_token, user=None): response_qs = parse_qs(github_access_token) github_access_token = response_qs['access_token'][0] try: github_user = GithubUserProfile.objects.get(access_token=github_access_token) return github_user.user except GithubUserProfile.DoesNotExist: github_user_count = GithubUserProfile.objects.all().count() username = "GithubUser:" + str(github_user_count+1) user = User(username=username) user.save() github_user = GithubUserProfile(user=user, access_token=github_access_token) github_user.save() AuthMeta(user=user, provider='Github').save() return github_user.user def get_user(self, user_id): try: return User.objects.get(pk=user_id) except: return None class FoursquareBackend: def authenticate(self, foursquare_access_token): try: foursquare_profile = FoursquareUserProfile.objects.get(access_token=foursquare_access_token) return foursquare_profile.user except FoursquareUserProfile.DoesNotExist: foursquare_user_count = FoursquareUserProfile.objects.all().count() username = "FoursquareUser:" + str(foursquare_user_count+1) user = User(username=username) user.save() foursquare_user = FoursquareUserProfile(user=user, access_token=foursquare_access_token) foursquare_user.save() AuthMeta(user=user, provider='Foursquare').save() return foursquare_user.user def get_user(self, user_id): try: return User.objects.get(pk=user_id) except: return None ================================================ FILE: socialauth/context_processors.py ================================================ from django.conf import settings def facebook_api_key(request): FACEBOOK_APP_ID = getattr(settings, 'FACEBOOK_APP_ID', '') FACEBOOK_API_KEY = getattr(settings, 'FACEBOOK_API_KEY', '') FACEBOOK_EXTENDED_PERMISSIONS = getattr(settings, 'FACEBOOK_EXTENDED_PERMISSIONS', '') if FACEBOOK_APP_ID: return {'FACEBOOK_APP_ID': FACEBOOK_APP_ID, 'FACEBOOK_API_KEY': FACEBOOK_API_KEY, 'login_button_perms': ','.join(FACEBOOK_EXTENDED_PERMISSIONS)} else: return {} ================================================ FILE: socialauth/forms.py ================================================ from django import forms from django.conf import settings from socialauth.models import AuthMeta ALLOW_MULTIPLE_USERNAME_EDITS = getattr(settings, 'ALLOW_MULTIPLE_USERNAME_EDITS', False) class EditProfileForm(forms.Form): email = forms.EmailField() password = forms.CharField(max_length=100, widget=forms.PasswordInput, required=False, help_text='If you give a password, you can \ login via a login form as well.') password2 = forms.CharField(max_length=100, widget=forms.PasswordInput, required=False, label='Repeat password') first_name = forms.CharField(max_length=100, required=False) last_name = forms.CharField(max_length=100, required=False) def __init__(self, user=None, *args, **kwargs): super(EditProfileForm, self).__init__(*args, **kwargs) self.user = user if self.user: self.initial = {'email': user.email, \ 'first_name':user.first_name, 'last_name':user.last_name} def clean(self): cleaned_data = self.cleaned_data if 'password' in cleaned_data or 'password2' in cleaned_data: if 'password' in cleaned_data and 'password2' in cleaned_data: if cleaned_data['password'] != cleaned_data['password2']: raise forms.ValidationError('The passwords do not match.') else: raise forms.ValidationError('Either ener both or None of the\ password fields') return cleaned_data def save(self): user = self.user user.email = self.cleaned_data['email'] user.first_name = self.cleaned_data['first_name'] user.last_name = self.cleaned_data['last_name'] if ('password' in self.cleaned_data and 'password2' in self.cleaned_data): user.set_password(self.cleaned_data['password']) user.save() try: authmeta = user.authmeta authmeta.is_email_filled = True authmeta.is_profile_modified = True authmeta.save() except AuthMeta.DoesNotExist: pass return user ================================================ FILE: socialauth/lib/__init__.py ================================================ ================================================ FILE: socialauth/lib/foursquare.py ================================================ from oauth import oauth from django.conf import settings import httplib FOURSQUARE_AUTHENTICATION_URL = 'https://foursquare.com/oauth2/authenticate' FOURSQUARE_ACCESS_TOKEN_URL = 'https://foursquare.com/oauth2/access_token' FOURSQUARE_CONSUMER_KEY = getattr(settings, 'FOURSQUARE_CONSUMER_KEY') FOURSQUARE_CONSUMER_SECRET = getattr(settings, 'FOURSQUARE_CONSUMER_SECRET') REGISTERED_REDIRECT_URI = getattr(settings, 'FOURSQUARE_REGISTERED_REDIRECT_URI') def get_http_connection(): return httplib.HTTPSConnection('foursquare.com') def get_response_body(oauth_request): http_conn = get_http_connection() http_conn.request('GET', oauth_request.to_url()) response = http_conn.getresponse().read() return response class FourSquareClient(object): def __init__(self): self.consumer = oauth.OAuthConsumer(FOURSQUARE_CONSUMER_KEY, FOURSQUARE_CONSUMER_SECRET) def get_authentication_url(self): parameters = {} parameters['client_id'] = FOURSQUARE_CONSUMER_KEY parameters['response_type'] = 'code' parameters['redirect_uri'] = REGISTERED_REDIRECT_URI oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, http_url=FOURSQUARE_AUTHENTICATION_URL, parameters=parameters) return oauth_request.to_url() def get_access_token(self, foursquare_code): parameters = {} parameters['client_id'] = FOURSQUARE_CONSUMER_KEY parameters['client_secret'] = FOURSQUARE_CONSUMER_SECRET parameters['grant_type'] = 'authorization_code' parameters['redirect_uri'] = REGISTERED_REDIRECT_URI parameters['code'] = foursquare_code oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, http_url=FOURSQUARE_ACCESS_TOKEN_URL, parameters=parameters) access_token_response = get_response_body(oauth_request) return access_token_response ================================================ FILE: socialauth/lib/github.py ================================================ from django.conf import settings from oauth.oauth import OAuthConsumer, OAuthRequest import httplib GITHUB_CLIENT_ID = getattr(settings, 'GITHUB_CLIENT_ID') GITHUB_CLIENT_SECRET = getattr(settings, 'GITHUB_CLIENT_SECRET') GITHUB_AUTHORIZE_URL = 'https://github.com/login/oauth/authorize' GITHUB_ACCESS_TOKEN_URL = 'https://github.com/login/oauth/access_token' def get_response_from_url(to_url): conn = httplib.HTTPSConnection('github.com') conn.request('GET', to_url) return conn.getresponse().read() class GithubClient(object): def __init__(self): self.consumer = OAuthConsumer(GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET) def get_authorize_url(self): parameters = {'client_id':GITHUB_CLIENT_ID} oauth_request = OAuthRequest.from_consumer_and_token(self.consumer, http_url=GITHUB_AUTHORIZE_URL, parameters=parameters) return oauth_request.to_url() def get_access_token(self, code): parameters = {} parameters['client_id'] = GITHUB_CLIENT_ID parameters['client_secret'] = GITHUB_CLIENT_SECRET parameters['code'] = code oauth_request = OAuthRequest.from_consumer_and_token(self.consumer, http_url=GITHUB_ACCESS_TOKEN_URL, parameters=parameters) access_token = get_response_from_url(oauth_request.to_url()) return access_token ================================================ FILE: socialauth/lib/linkedin.py ================================================ """ LinkedIn OAuth Api Access Version: 0.01 License: MIT Author: Max Lynch Website: http://mendotasoft.com, http://maxlynch.com Date Release: 11/23/2009 Enjoy! """ import hashlib import urllib2 import httplib import time from xml.dom.minidom import parseString import oauth.oauth as oauth class LinkedIn(): LI_SERVER = "api.linkedin.com" LI_API_URL = "https://api.linkedin.com" REQUEST_TOKEN_URL = LI_API_URL + "/uas/oauth/requestToken" AUTHORIZE_URL = LI_API_URL + "/uas/oauth/authorize" ACCESS_TOKEN_URL = LI_API_URL + "/uas/oauth/accessToken" def __init__(self, api_key, secret_key): self.api_key = api_key self.secret_key = secret_key self.connection = httplib.HTTPSConnection(self.LI_SERVER) self.consumer = oauth.OAuthConsumer(api_key, secret_key) self.sig_method = oauth.OAuthSignatureMethod_HMAC_SHA1() self.status_api = StatusApi(self) self.connections_api = ConnectionsApi(self) def getRequestToken(self, callback): """ Get a request token from linkedin """ oauth_consumer_key = self.api_key oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, callback=callback, http_url = self.REQUEST_TOKEN_URL) oauth_request.sign_request(self.sig_method, self.consumer, None) self.connection.request(oauth_request.http_method, self.REQUEST_TOKEN_URL, headers = oauth_request.to_header()) response = self.connection.getresponse().read() token = oauth.OAuthToken.from_string(response) return token def getAuthorizeUrl(self, token): """ Get the URL that we can redirect the user to for authorization of our application. """ oauth_request = oauth.OAuthRequest.from_token_and_callback(token=token, http_url = self.AUTHORIZE_URL) return oauth_request.to_url() def getAccessToken(self, token, verifier): """ Using the verifier we obtained through the user's authorization of our application, get an access code. Note: token is the request token returned from the call to getRequestToken @return an OAuthToken object with the access token. Use it like this: token.key -> Key token.secret -> Secret Key """ token.verifier = verifier oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, token=token, verifier=verifier, http_url=self.ACCESS_TOKEN_URL) oauth_request.sign_request(self.sig_method, self.consumer, token) # self.connection.request(oauth_request.http_method, self.ACCESS_TOKEN_URL, headers=oauth_request.to_header()) self.connection.request(oauth_request.http_method, oauth_request.to_url()) response = self.connection.getresponse().read() return oauth.OAuthToken.from_string(response) """ More functionality coming soon... """ class LinkedInApi(): def __init__(self, linkedin): self.linkedin = linkedin def doApiRequest(self, url, access_token): oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.linkedin.consumer, token=access_token, http_url=url) oauth_request.sign_request(self.linkedin.sig_method, self.linkedin.consumer, access_token) self.linkedin.connection.request(oauth_request.http_method, url, headers=oauth_request.to_header()) return self.linkedin.connection.getresponse().read() class StatusApi(LinkedInApi): STATUS_SELF_URL = LinkedIn.LI_API_URL + "/v1/people/~:(current-status)" def __init__(self, linkedin): LinkedInApi.__init__(self, linkedin) def getMyStatus(self, access_token): return self.doApiRequest(self.STATUS_SELF_URL, access_token) class ProfileApi(LinkedInApi): """ Get a LinkedIn Profile """ PROFILE_SELF = LinkedIn.LI_API_URL + r"/v1/people/~:(id,first-name,last-name,headline,industry,picture-url,site-standard-profile-request)" def __init__(self, linkedin): LinkedInApi.__init__(self, linkedin) def getMyProfile(self, access_token): xml = self.doApiRequest(self.PROFILE_SELF, access_token) dom = parseString(xml) personDom = dom.getElementsByTagName('person') p = personDom[0] fn=ln=picurl=headline=company=industry=profurl="" try: id = p.getElementsByTagName('id')[0].firstChild.nodeValue fn = p.getElementsByTagName('first-name')[0].firstChild.nodeValue ln = p.getElementsByTagName('last-name')[0].firstChild.nodeValue picurl = p.getElementsByTagName('picture-url')[0].firstChild.nodeValue headline = p.getElementsByTagName('headline')[0].firstChild.nodeValue if ' at ' in headline: company = headline.split(' at ')[1] industry = p.getElementsByTagName('industry')[0].firstChild.nodeValue #location = p.getElementsByTagName('industry')[0].firstChild.nodeValue profurl = p.getElementsByTagName('url')[0].firstChild.nodeValue except: pass person = Person() person.id = id person.firstname = fn person.lastname = ln person.headline = headline person.company = company person.industry = industry person.picture_url = picurl person.profile_url = profurl return person class ConnectionsApi(LinkedInApi): """ How to get all of a user's connections: Note: This should happen after the linkedin redirect. verifier is passed by LinkedIn back to your redirect page li = LinkedIn(LINKEDIN_CONSUMER_KEY, LINKEDIN_CONSUMER_SECRET) tokenObj = oauth.OAuthToken(requestTokenKey, requestTokenSecret) access_token = li.getAccessToken(tokenObj, verifier) connections = li.connections_api.getMyConnections(access_token) for c in connections: # Access c.firstname, c.lastname, etc. """ CONNECTIONS_SELF = LinkedIn.LI_API_URL + "/v1/people/~/connections" def __init__(self, linkedin): LinkedInApi.__init__(self, linkedin) def getMyConnections(self, access_token): xml = self.doApiRequest(self.CONNECTIONS_SELF, access_token) dom = parseString(xml) peopleDom = dom.getElementsByTagName('person') people = [] for p in peopleDom: try: fn = p.getElementsByTagName('first-name')[0].firstChild.nodeValue ln = p.getElementsByTagName('last-name')[0].firstChild.nodeValue headline = p.getElementsByTagName('headline')[0].firstChild.nodeValue company = headline.split(' at ')[1] industry = p.getElementsByTagName('industry')[0].firstChild.nodeValue #location = p.getElementsByTagName('industry')[0].firstChild.nodeValue person = Person() person.firstname = fn person.lastname = ln person.headline = headline person.company = company person.industry = industry people.append(person) except: continue return people class Person(): id = "" firstname = "" lastname = "" headline = "" company = "" location = None industry = "" picture_url = "" profile_url = "" def __str__(self): return "%s %s working at %s" % (self.firstname, self.lastname, self.company) class Location(): name = "" country = "" ================================================ FILE: socialauth/lib/oauth1.py ================================================ """ The MIT License Copyright (c) 2007 Leah Culver Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ import cgi import urllib import time import random import urlparse import hmac import binascii VERSION = '1.0' # Hi Blaine! HTTP_METHOD = 'GET' SIGNATURE_METHOD = 'PLAINTEXT' class OAuthError(RuntimeError): """Generic exception class.""" def __init__(self, message='OAuth error occured.'): self.message = message def build_authenticate_header(realm=''): """Optional WWW-Authenticate header (401 error)""" return {'WWW-Authenticate': 'OAuth realm="%s"' % realm} def escape(s): """Escape a URL including any /.""" return urllib.quote(s, safe='~') def _utf8_str(s): """Convert unicode to utf-8.""" if isinstance(s, unicode): return s.encode("utf-8") else: return str(s) def generate_timestamp(): """Get seconds since epoch (UTC).""" return int(time.time()) def generate_nonce(length=8): """Generate pseudorandom number.""" return ''.join([str(random.randint(0, 9)) for i in range(length)]) def generate_verifier(length=8): """Generate pseudorandom number.""" return ''.join([str(random.randint(0, 9)) for i in range(length)]) class OAuthConsumer(object): """Consumer of OAuth authentication. OAuthConsumer is a data type that represents the identity of the Consumer via its shared secret with the Service Provider. """ key = None secret = None def __init__(self, key, secret): self.key = key self.secret = secret class OAuthToken(object): """OAuthToken is a data type that represents an End User via either an access or request token. key -- the token secret -- the token secret """ key = None secret = None callback = None callback_confirmed = None verifier = None def __init__(self, key, secret): self.key = key self.secret = secret def set_callback(self, callback): self.callback = callback self.callback_confirmed = 'true' def set_verifier(self, verifier=None): if verifier is not None: self.verifier = verifier else: self.verifier = generate_verifier() def get_callback_url(self): if self.callback and self.verifier: # Append the oauth_verifier. parts = urlparse.urlparse(self.callback) scheme, netloc, path, params, query, fragment = parts[:6] if query: query = '%s&oauth_verifier=%s' % (query, self.verifier) else: query = 'oauth_verifier=%s' % self.verifier return urlparse.urlunparse((scheme, netloc, path, params, query, fragment)) return self.callback def to_string(self): data = { 'oauth_token': self.key, 'oauth_token_secret': self.secret, } if self.callback_confirmed is not None: data['oauth_callback_confirmed'] = self.callback_confirmed return urllib.urlencode(data) def from_string(s): """ Returns a token from something like: oauth_token_secret=xxx&oauth_token=xxx """ params = cgi.parse_qs(s, keep_blank_values=False) key = params['oauth_token'][0] secret = params['oauth_token_secret'][0] token = OAuthToken(key, secret) try: token.callback_confirmed = params['oauth_callback_confirmed'][0] except KeyError: pass # 1.0, no callback confirmed. return token from_string = staticmethod(from_string) def __str__(self): return self.to_string() class OAuthRequest(object): """OAuthRequest represents the request and can be serialized. OAuth parameters: - oauth_consumer_key - oauth_token - oauth_signature_method - oauth_signature - oauth_timestamp - oauth_nonce - oauth_version - oauth_verifier ... any additional parameters, as defined by the Service Provider. """ parameters = None # OAuth parameters. http_method = HTTP_METHOD http_url = None version = VERSION def __init__(self, http_method=HTTP_METHOD, http_url=None, parameters=None): self.http_method = http_method self.http_url = http_url self.parameters = parameters or {} def set_parameter(self, parameter, value): self.parameters[parameter] = value def get_parameter(self, parameter): try: return self.parameters[parameter] except: raise OAuthError('Parameter not found: %s' % parameter) def _get_timestamp_nonce(self): return self.get_parameter('oauth_timestamp'), self.get_parameter( 'oauth_nonce') def get_nonoauth_parameters(self): """Get any non-OAuth parameters.""" parameters = {} for k, v in self.parameters.iteritems(): # Ignore oauth parameters. if k.find('oauth_') < 0: parameters[k] = v return parameters def to_header(self, realm=''): """Serialize as a header for an HTTPAuth request.""" auth_header = 'OAuth realm="%s"' % realm # Add the oauth parameters. if self.parameters: for k, v in self.parameters.iteritems(): if k[:6] == 'oauth_': auth_header += ', %s="%s"' % (k, escape(str(v))) return {'Authorization': auth_header} def to_postdata(self): """Serialize as post data for a POST request.""" return '&'.join(['%s=%s' % (escape(str(k)), escape(str(v))) \ for k, v in self.parameters.iteritems()]) def to_url(self): """Serialize as a URL for a GET request.""" return '%s?%s' % (self.get_normalized_http_url(), self.to_postdata()) def get_normalized_parameters(self): """Return a string that contains the parameters that must be signed.""" params = self.parameters try: # Exclude the signature if it exists. del params['oauth_signature'] except: pass # Escape key values before sorting. key_values = [(escape(_utf8_str(k)), escape(_utf8_str(v))) \ for k,v in params.items()] # Sort lexicographically, first after key, then after value. key_values.sort() # Combine key value pairs into a string. return '&'.join(['%s=%s' % (k, v) for k, v in key_values]) def get_normalized_http_method(self): """Uppercases the http method.""" return self.http_method.upper() def get_normalized_http_url(self): """Parses the URL and rebuilds it to be scheme://host/path.""" parts = urlparse.urlparse(self.http_url) scheme, netloc, path = parts[:3] # Exclude default port numbers. if scheme == 'http' and netloc[-3:] == ':80': netloc = netloc[:-3] elif scheme == 'https' and netloc[-4:] == ':443': netloc = netloc[:-4] return '%s://%s%s' % (scheme, netloc, path) def sign_request(self, signature_method, consumer, token): """Set the signature parameter to the result of build_signature.""" # Set the signature method. self.set_parameter('oauth_signature_method', signature_method.get_name()) # Set the signature. self.set_parameter('oauth_signature', self.build_signature(signature_method, consumer, token)) def build_signature(self, signature_method, consumer, token): """Calls the build signature method within the signature method.""" return signature_method.build_signature(self, consumer, token) def from_request(http_method, http_url, headers=None, parameters=None, query_string=None): """Combines multiple parameter sources.""" if parameters is None: parameters = {} # Headers if headers and 'Authorization' in headers: auth_header = headers['Authorization'] # Check that the authorization header is OAuth. if auth_header[:6] == 'OAuth ': auth_header = auth_header[6:] try: # Get the parameters from the header. header_params = OAuthRequest._split_header(auth_header) parameters.update(header_params) except: raise OAuthError('Unable to parse OAuth parameters from ' 'Authorization header.') # GET or POST query string. if query_string: query_params = OAuthRequest._split_url_string(query_string) parameters.update(query_params) # URL parameters. param_str = urlparse.urlparse(http_url)[4] # query url_params = OAuthRequest._split_url_string(param_str) parameters.update(url_params) if parameters: return OAuthRequest(http_method, http_url, parameters) return None from_request = staticmethod(from_request) def from_consumer_and_token(oauth_consumer, token=None, callback=None, verifier=None, http_method=HTTP_METHOD, http_url=None, parameters=None): if not parameters: parameters = {} defaults = { 'oauth_consumer_key': oauth_consumer.key, 'oauth_timestamp': generate_timestamp(), 'oauth_nonce': generate_nonce(), 'oauth_version': OAuthRequest.version, } defaults.update(parameters) parameters = defaults if token: parameters['oauth_token'] = token.key if token.callback: parameters['oauth_callback'] = token.callback # 1.0a support for verifier. if verifier: parameters['oauth_verifier'] = verifier elif callback: # 1.0a support for callback in the request token request. parameters['oauth_callback'] = callback return OAuthRequest(http_method, http_url, parameters) from_consumer_and_token = staticmethod(from_consumer_and_token) def from_token_and_callback(token, callback=None, http_method=HTTP_METHOD, http_url=None, parameters=None): if not parameters: parameters = {} parameters['oauth_token'] = token.key if callback: parameters['oauth_callback'] = callback return OAuthRequest(http_method, http_url, parameters) from_token_and_callback = staticmethod(from_token_and_callback) def _split_header(header): """Turn Authorization: header into parameters.""" params = {} parts = header.split(',') for param in parts: # Ignore realm parameter. if param.find('realm') > -1: continue # Remove whitespace. param = param.strip() # Split key-value. param_parts = param.split('=', 1) # Remove quotes and unescape the value. params[param_parts[0]] = urllib.unquote(param_parts[1].strip('\"')) return params _split_header = staticmethod(_split_header) def _split_url_string(param_str): """Turn URL string into parameters.""" parameters = cgi.parse_qs(param_str, keep_blank_values=False) for k, v in parameters.iteritems(): parameters[k] = urllib.unquote(v[0]) return parameters _split_url_string = staticmethod(_split_url_string) class OAuthServer(object): """A worker to check the validity of a request against a data store.""" timestamp_threshold = 300 # In seconds, five minutes. version = VERSION signature_methods = None data_store = None def __init__(self, data_store=None, signature_methods=None): self.data_store = data_store self.signature_methods = signature_methods or {} def set_data_store(self, data_store): self.data_store = data_store def get_data_store(self): return self.data_store def add_signature_method(self, signature_method): self.signature_methods[signature_method.get_name()] = signature_method return self.signature_methods def fetch_request_token(self, oauth_request): """Processes a request_token request and returns the request token on success. """ try: # Get the request token for authorization. token = self._get_token(oauth_request, 'request') except OAuthError: # No token required for the initial token request. version = self._get_version(oauth_request) consumer = self._get_consumer(oauth_request) try: callback = self.get_callback(oauth_request) except OAuthError: callback = None # 1.0, no callback specified. self._check_signature(oauth_request, consumer, None) # Fetch a new token. token = self.data_store.fetch_request_token(consumer, callback) return token def fetch_access_token(self, oauth_request): """Processes an access_token request and returns the access token on success. """ version = self._get_version(oauth_request) consumer = self._get_consumer(oauth_request) try: verifier = self._get_verifier(oauth_request) except OAuthError: verifier = None # Get the request token. token = self._get_token(oauth_request, 'request') self._check_signature(oauth_request, consumer, token) new_token = self.data_store.fetch_access_token(consumer, token, verifier) return new_token def verify_request(self, oauth_request): """Verifies an api call and checks all the parameters.""" # -> consumer and token version = self._get_version(oauth_request) consumer = self._get_consumer(oauth_request) # Get the access token. token = self._get_token(oauth_request, 'access') self._check_signature(oauth_request, consumer, token) parameters = oauth_request.get_nonoauth_parameters() return consumer, token, parameters def authorize_token(self, token, user): """Authorize a request token.""" return self.data_store.authorize_request_token(token, user) def get_callback(self, oauth_request): """Get the callback URL.""" return oauth_request.get_parameter('oauth_callback') def build_authenticate_header(self, realm=''): """Optional support for the authenticate header.""" return {'WWW-Authenticate': 'OAuth realm="%s"' % realm} def _get_version(self, oauth_request): """Verify the correct version request for this server.""" try: version = oauth_request.get_parameter('oauth_version') except: version = VERSION if version and version != self.version: raise OAuthError('OAuth version %s not supported.' % str(version)) return version def _get_signature_method(self, oauth_request): """Figure out the signature with some defaults.""" try: signature_method = oauth_request.get_parameter( 'oauth_signature_method') except: signature_method = SIGNATURE_METHOD try: # Get the signature method object. signature_method = self.signature_methods[signature_method] except: signature_method_names = ', '.join(self.signature_methods.keys()) raise OAuthError('Signature method %s not supported try one of the ' 'following: %s' % (signature_method, signature_method_names)) return signature_method def _get_consumer(self, oauth_request): consumer_key = oauth_request.get_parameter('oauth_consumer_key') consumer = self.data_store.lookup_consumer(consumer_key) if not consumer: raise OAuthError('Invalid consumer.') return consumer def _get_token(self, oauth_request, token_type='access'): """Try to find the token for the provided request token key.""" token_field = oauth_request.get_parameter('oauth_token') token = self.data_store.lookup_token(token_type, token_field) if not token: raise OAuthError('Invalid %s token: %s' % (token_type, token_field)) return token def _get_verifier(self, oauth_request): return oauth_request.get_parameter('oauth_verifier') def _check_signature(self, oauth_request, consumer, token): timestamp, nonce = oauth_request._get_timestamp_nonce() self._check_timestamp(timestamp) self._check_nonce(consumer, token, nonce) signature_method = self._get_signature_method(oauth_request) try: signature = oauth_request.get_parameter('oauth_signature') except: raise OAuthError('Missing signature.') # Validate the signature. valid_sig = signature_method.check_signature(oauth_request, consumer, token, signature) if not valid_sig: key, base = signature_method.build_signature_base_string( oauth_request, consumer, token) raise OAuthError('Invalid signature. Expected signature base ' 'string: %s' % base) built = signature_method.build_signature(oauth_request, consumer, token) def _check_timestamp(self, timestamp): """Verify that timestamp is recentish.""" timestamp = int(timestamp) now = int(time.time()) lapsed = abs(now - timestamp) if lapsed > self.timestamp_threshold: raise OAuthError('Expired timestamp: given %d and now %s has a ' 'greater difference than threshold %d' % (timestamp, now, self.timestamp_threshold)) def _check_nonce(self, consumer, token, nonce): """Verify that the nonce is uniqueish.""" nonce = self.data_store.lookup_nonce(consumer, token, nonce) if nonce: raise OAuthError('Nonce already used: %s' % str(nonce)) class OAuthClient(object): """OAuthClient is a worker to attempt to execute a request.""" consumer = None token = None def __init__(self, oauth_consumer, oauth_token): self.consumer = oauth_consumer self.token = oauth_token def get_consumer(self): return self.consumer def get_token(self): return self.token def fetch_request_token(self, oauth_request): """-> OAuthToken.""" raise NotImplementedError def fetch_access_token(self, oauth_request): """-> OAuthToken.""" raise NotImplementedError def access_resource(self, oauth_request): """-> Some protected resource.""" raise NotImplementedError class OAuthDataStore(object): """A database abstraction used to lookup consumers and tokens.""" def lookup_consumer(self, key): """-> OAuthConsumer.""" raise NotImplementedError def lookup_token(self, oauth_consumer, token_type, token_token): """-> OAuthToken.""" raise NotImplementedError def lookup_nonce(self, oauth_consumer, oauth_token, nonce): """-> OAuthToken.""" raise NotImplementedError def fetch_request_token(self, oauth_consumer, oauth_callback): """-> OAuthToken.""" raise NotImplementedError def fetch_access_token(self, oauth_consumer, oauth_token, oauth_verifier): """-> OAuthToken.""" raise NotImplementedError def authorize_request_token(self, oauth_token, user): """-> OAuthToken.""" raise NotImplementedError class OAuthSignatureMethod(object): """A strategy class that implements a signature method.""" def get_name(self): """-> str.""" raise NotImplementedError def build_signature_base_string(self, oauth_request, oauth_consumer, oauth_token): """-> str key, str raw.""" raise NotImplementedError def build_signature(self, oauth_request, oauth_consumer, oauth_token): """-> str.""" raise NotImplementedError def check_signature(self, oauth_request, consumer, token, signature): built = self.build_signature(oauth_request, consumer, token) return built == signature class OAuthSignatureMethod_HMAC_SHA1(OAuthSignatureMethod): def get_name(self): return 'HMAC-SHA1' def build_signature_base_string(self, oauth_request, consumer, token): sig = ( escape(oauth_request.get_normalized_http_method()), escape(oauth_request.get_normalized_http_url()), escape(oauth_request.get_normalized_parameters()), ) key = '%s&' % escape(consumer.secret) if token: key += escape(token.secret) raw = '&'.join(sig) return key, raw def build_signature(self, oauth_request, consumer, token): """Builds the base signature string.""" key, raw = self.build_signature_base_string(oauth_request, consumer, token) # HMAC object. try: import hashlib # 2.5 hashed = hmac.new(key, raw, hashlib.sha1) except: import sha # Deprecated hashed = hmac.new(key, raw, sha) # Calculate the digest base 64. return binascii.b2a_base64(hashed.digest())[:-1] class OAuthSignatureMethod_PLAINTEXT(OAuthSignatureMethod): def get_name(self): return 'PLAINTEXT' def build_signature_base_string(self, oauth_request, consumer, token): """Concatenates the consumer key and secret.""" sig = '%s&' % escape(consumer.secret) if token: sig = sig + escape(token.secret) return sig, sig def build_signature(self, oauth_request, consumer, token): key, raw = self.build_signature_base_string(oauth_request, consumer, token) return key ================================================ FILE: socialauth/lib/oauth2.py ================================================ """ The MIT License Copyright (c) 2007 Leah Culver Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ import cgi import urllib import time import random import urlparse import hmac import binascii VERSION = '1.0' # Hi Blaine! HTTP_METHOD = 'GET' SIGNATURE_METHOD = 'PLAINTEXT' class OAuthError(RuntimeError): """Generic exception class.""" def __init__(self, message='OAuth error occured.'): self.message = message def build_authenticate_header(realm=''): """Optional WWW-Authenticate header (401 error)""" return {'WWW-Authenticate': 'OAuth realm="%s"' % realm} def escape(s): """Escape a URL including any /.""" return urllib.quote(s, safe='~') def _utf8_str(s): """Convert unicode to utf-8.""" if isinstance(s, unicode): return s.encode("utf-8") else: return str(s) def generate_timestamp(): """Get seconds since epoch (UTC).""" return int(time.time()) def generate_nonce(length=8): """Generate pseudorandom number.""" return ''.join([str(random.randint(0, 9)) for i in range(length)]) class OAuthConsumer(object): """Consumer of OAuth authentication. OAuthConsumer is a data type that represents the identity of the Consumer via its shared secret with the Service Provider. """ key = None secret = None def __init__(self, key, secret): self.key = key self.secret = secret class OAuthToken(object): """OAuthToken is a data type that represents an End User via either an access or request token. key -- the token secret -- the token secret """ key = None secret = None def __init__(self, key, secret): self.key = key self.secret = secret def to_string(self): return urllib.urlencode({'oauth_token': self.key, 'oauth_token_secret': self.secret}) def from_string(s): """ Returns a token from something like: oauth_token_secret=xxx&oauth_token=xxx """ params = cgi.parse_qs(s, keep_blank_values=False) key = params['oauth_token'][0] secret = params['oauth_token_secret'][0] return OAuthToken(key, secret) from_string = staticmethod(from_string) def __str__(self): return self.to_string() class OAuthRequest(object): """OAuthRequest represents the request and can be serialized. OAuth parameters: - oauth_consumer_key - oauth_token - oauth_signature_method - oauth_signature - oauth_timestamp - oauth_nonce - oauth_version ... any additional parameters, as defined by the Service Provider. """ parameters = None # OAuth parameters. http_method = HTTP_METHOD http_url = None version = VERSION def __init__(self, http_method=HTTP_METHOD, http_url=None, parameters=None): self.http_method = http_method self.http_url = http_url self.parameters = parameters or {} def set_parameter(self, parameter, value): self.parameters[parameter] = value def get_parameter(self, parameter): try: return self.parameters[parameter] except: raise OAuthError('Parameter not found: %s' % parameter) def _get_timestamp_nonce(self): return self.get_parameter('oauth_timestamp'), self.get_parameter( 'oauth_nonce') def get_nonoauth_parameters(self): """Get any non-OAuth parameters.""" parameters = {} for k, v in self.parameters.iteritems(): # Ignore oauth parameters. if k.find('oauth_') < 0: parameters[k] = v return parameters def to_header(self, realm=''): """Serialize as a header for an HTTPAuth request.""" auth_header = 'OAuth realm="%s"' % realm # Add the oauth parameters. if self.parameters: for k, v in self.parameters.iteritems(): if k[:6] == 'oauth_': auth_header += ', %s="%s"' % (k, escape(str(v))) return {'Authorization': auth_header} def to_postdata(self): """Serialize as post data for a POST request.""" return '&'.join(['%s=%s' % (escape(str(k)), escape(str(v))) \ for k, v in self.parameters.iteritems()]) def to_url(self): """Serialize as a URL for a GET request.""" return '%s?%s' % (self.get_normalized_http_url(), self.to_postdata()) def get_normalized_parameters(self): """Return a string that contains the parameters that must be signed.""" params = self.parameters try: # Exclude the signature if it exists. del params['oauth_signature'] except: pass # Escape key values before sorting. key_values = [(escape(_utf8_str(k)), escape(_utf8_str(v))) \ for k,v in params.items()] # Sort lexicographically, first after key, then after value. key_values.sort() # Combine key value pairs into a string. return '&'.join(['%s=%s' % (k, v) for k, v in key_values]) def get_normalized_http_method(self): """Uppercases the http method.""" return self.http_method.upper() def get_normalized_http_url(self): """Parses the URL and rebuilds it to be scheme://host/path.""" parts = urlparse.urlparse(self.http_url) scheme, netloc, path = parts[:3] # Exclude default port numbers. if scheme == 'http' and netloc[-3:] == ':80': netloc = netloc[:-3] elif scheme == 'https' and netloc[-4:] == ':443': netloc = netloc[:-4] return '%s://%s%s' % (scheme, netloc, path) def sign_request(self, signature_method, consumer, token): """Set the signature parameter to the result of build_signature.""" # Set the signature method. self.set_parameter('oauth_signature_method', signature_method.get_name()) # Set the signature. self.set_parameter('oauth_signature', self.build_signature(signature_method, consumer, token)) def build_signature(self, signature_method, consumer, token): """Calls the build signature method within the signature method.""" return signature_method.build_signature(self, consumer, token) def from_request(http_method, http_url, headers=None, parameters=None, query_string=None): """Combines multiple parameter sources.""" if parameters is None: parameters = {} # Headers if headers and 'Authorization' in headers: auth_header = headers['Authorization'] # Check that the authorization header is OAuth. if auth_header.index('OAuth') > -1: auth_header = auth_header.lstrip('OAuth ') try: # Get the parameters from the header. header_params = OAuthRequest._split_header(auth_header) parameters.update(header_params) except: raise OAuthError('Unable to parse OAuth parameters from ' 'Authorization header.') # GET or POST query string. if query_string: query_params = OAuthRequest._split_url_string(query_string) parameters.update(query_params) # URL parameters. param_str = urlparse.urlparse(http_url)[4] # query url_params = OAuthRequest._split_url_string(param_str) parameters.update(url_params) if parameters: return OAuthRequest(http_method, http_url, parameters) return None from_request = staticmethod(from_request) def from_consumer_and_token(oauth_consumer, token=None, http_method=HTTP_METHOD, http_url=None, parameters=None): if not parameters: parameters = {} defaults = { 'oauth_consumer_key': oauth_consumer.key, 'oauth_timestamp': generate_timestamp(), 'oauth_nonce': generate_nonce(), 'oauth_version': OAuthRequest.version, } defaults.update(parameters) parameters = defaults if token: parameters['oauth_token'] = token.key return OAuthRequest(http_method, http_url, parameters) from_consumer_and_token = staticmethod(from_consumer_and_token) def from_token_and_callback(token, callback=None, http_method=HTTP_METHOD, http_url=None, parameters=None): if not parameters: parameters = {} parameters['oauth_token'] = token.key if callback: parameters['oauth_callback'] = callback return OAuthRequest(http_method, http_url, parameters) from_token_and_callback = staticmethod(from_token_and_callback) def _split_header(header): """Turn Authorization: header into parameters.""" params = {} parts = header.split(',') for param in parts: # Ignore realm parameter. if param.find('realm') > -1: continue # Remove whitespace. param = param.strip() # Split key-value. param_parts = param.split('=', 1) # Remove quotes and unescape the value. params[param_parts[0]] = urllib.unquote(param_parts[1].strip('\"')) return params _split_header = staticmethod(_split_header) def _split_url_string(param_str): """Turn URL string into parameters.""" parameters = cgi.parse_qs(param_str, keep_blank_values=False) for k, v in parameters.iteritems(): parameters[k] = urllib.unquote(v[0]) return parameters _split_url_string = staticmethod(_split_url_string) class OAuthServer(object): """A worker to check the validity of a request against a data store.""" timestamp_threshold = 300 # In seconds, five minutes. version = VERSION signature_methods = None data_store = None def __init__(self, data_store=None, signature_methods=None): self.data_store = data_store self.signature_methods = signature_methods or {} def set_data_store(self, data_store): self.data_store = data_store def get_data_store(self): return self.data_store def add_signature_method(self, signature_method): self.signature_methods[signature_method.get_name()] = signature_method return self.signature_methods def fetch_request_token(self, oauth_request): """Processes a request_token request and returns the request token on success. """ try: # Get the request token for authorization. token = self._get_token(oauth_request, 'request') except OAuthError: # No token required for the initial token request. version = self._get_version(oauth_request) consumer = self._get_consumer(oauth_request) self._check_signature(oauth_request, consumer, None) # Fetch a new token. token = self.data_store.fetch_request_token(consumer) return token def fetch_access_token(self, oauth_request): """Processes an access_token request and returns the access token on success. """ version = self._get_version(oauth_request) consumer = self._get_consumer(oauth_request) # Get the request token. token = self._get_token(oauth_request, 'request') self._check_signature(oauth_request, consumer, token) new_token = self.data_store.fetch_access_token(consumer, token) return new_token def verify_request(self, oauth_request): """Verifies an api call and checks all the parameters.""" # -> consumer and token version = self._get_version(oauth_request) consumer = self._get_consumer(oauth_request) # Get the access token. token = self._get_token(oauth_request, 'access') self._check_signature(oauth_request, consumer, token) parameters = oauth_request.get_nonoauth_parameters() return consumer, token, parameters def authorize_token(self, token, user): """Authorize a request token.""" return self.data_store.authorize_request_token(token, user) def get_callback(self, oauth_request): """Get the callback URL.""" return oauth_request.get_parameter('oauth_callback') def build_authenticate_header(self, realm=''): """Optional support for the authenticate header.""" return {'WWW-Authenticate': 'OAuth realm="%s"' % realm} def _get_version(self, oauth_request): """Verify the correct version request for this server.""" try: version = oauth_request.get_parameter('oauth_version') except: version = VERSION if version and version != self.version: raise OAuthError('OAuth version %s not supported.' % str(version)) return version def _get_signature_method(self, oauth_request): """Figure out the signature with some defaults.""" try: signature_method = oauth_request.get_parameter( 'oauth_signature_method') except: signature_method = SIGNATURE_METHOD try: # Get the signature method object. signature_method = self.signature_methods[signature_method] except: signature_method_names = ', '.join(self.signature_methods.keys()) raise OAuthError('Signature method %s not supported try one of the ' 'following: %s' % (signature_method, signature_method_names)) return signature_method def _get_consumer(self, oauth_request): consumer_key = oauth_request.get_parameter('oauth_consumer_key') consumer = self.data_store.lookup_consumer(consumer_key) if not consumer: raise OAuthError('Invalid consumer.') return consumer def _get_token(self, oauth_request, token_type='access'): """Try to find the token for the provided request token key.""" token_field = oauth_request.get_parameter('oauth_token') token = self.data_store.lookup_token(token_type, token_field) if not token: raise OAuthError('Invalid %s token: %s' % (token_type, token_field)) return token def _check_signature(self, oauth_request, consumer, token): timestamp, nonce = oauth_request._get_timestamp_nonce() self._check_timestamp(timestamp) self._check_nonce(consumer, token, nonce) signature_method = self._get_signature_method(oauth_request) try: signature = oauth_request.get_parameter('oauth_signature') except: raise OAuthError('Missing signature.') # Validate the signature. valid_sig = signature_method.check_signature(oauth_request, consumer, token, signature) if not valid_sig: key, base = signature_method.build_signature_base_string( oauth_request, consumer, token) raise OAuthError('Invalid signature. Expected signature base ' 'string: %s' % base) built = signature_method.build_signature(oauth_request, consumer, token) def _check_timestamp(self, timestamp): """Verify that timestamp is recentish.""" timestamp = int(timestamp) now = int(time.time()) lapsed = now - timestamp if lapsed > self.timestamp_threshold: raise OAuthError('Expired timestamp: given %d and now %s has a ' 'greater difference than threshold %d' % (timestamp, now, self.timestamp_threshold)) def _check_nonce(self, consumer, token, nonce): """Verify that the nonce is uniqueish.""" nonce = self.data_store.lookup_nonce(consumer, token, nonce) if nonce: raise OAuthError('Nonce already used: %s' % str(nonce)) class OAuthClient(object): """OAuthClient is a worker to attempt to execute a request.""" consumer = None token = None def __init__(self, oauth_consumer, oauth_token): self.consumer = oauth_consumer self.token = oauth_token def get_consumer(self): return self.consumer def get_token(self): return self.token def fetch_request_token(self, oauth_request): """-> OAuthToken.""" raise NotImplementedError def fetch_access_token(self, oauth_request): """-> OAuthToken.""" raise NotImplementedError def access_resource(self, oauth_request): """-> Some protected resource.""" raise NotImplementedError class OAuthDataStore(object): """A database abstraction used to lookup consumers and tokens.""" def lookup_consumer(self, key): """-> OAuthConsumer.""" raise NotImplementedError def lookup_token(self, oauth_consumer, token_type, token_token): """-> OAuthToken.""" raise NotImplementedError def lookup_nonce(self, oauth_consumer, oauth_token, nonce): """-> OAuthToken.""" raise NotImplementedError def fetch_request_token(self, oauth_consumer): """-> OAuthToken.""" raise NotImplementedError def fetch_access_token(self, oauth_consumer, oauth_token): """-> OAuthToken.""" raise NotImplementedError def authorize_request_token(self, oauth_token, user): """-> OAuthToken.""" raise NotImplementedError class OAuthSignatureMethod(object): """A strategy class that implements a signature method.""" def get_name(self): """-> str.""" raise NotImplementedError def build_signature_base_string(self, oauth_request, oauth_consumer, oauth_token): """-> str key, str raw.""" raise NotImplementedError def build_signature(self, oauth_request, oauth_consumer, oauth_token): """-> str.""" raise NotImplementedError def check_signature(self, oauth_request, consumer, token, signature): built = self.build_signature(oauth_request, consumer, token) return built == signature class OAuthSignatureMethod_HMAC_SHA1(OAuthSignatureMethod): def get_name(self): return 'HMAC-SHA1' def build_signature_base_string(self, oauth_request, consumer, token): sig = ( escape(oauth_request.get_normalized_http_method()), escape(oauth_request.get_normalized_http_url()), escape(oauth_request.get_normalized_parameters()), ) key = '%s&' % escape(consumer.secret) if token: key += escape(token.secret) raw = '&'.join(sig) return key, raw def build_signature(self, oauth_request, consumer, token): """Builds the base signature string.""" key, raw = self.build_signature_base_string(oauth_request, consumer, token) # HMAC object. try: import hashlib # 2.5 hashed = hmac.new(key, raw, hashlib.sha1) except: import sha # Deprecated hashed = hmac.new(key, raw, sha) # Calculate the digest base 64. return binascii.b2a_base64(hashed.digest())[:-1] class OAuthSignatureMethod_PLAINTEXT(OAuthSignatureMethod): def get_name(self): return 'PLAINTEXT' def build_signature_base_string(self, oauth_request, consumer, token): """Concatenates the consumer key and secret.""" sig = '%s&' % escape(consumer.secret) if token: sig = sig + escape(token.secret) return sig, sig def build_signature(self, oauth_request, consumer, token): key, raw = self.build_signature_base_string(oauth_request, consumer, token) return key ================================================ FILE: socialauth/lib/oauthgoogle.py ================================================ import httplib import urllib2 import urllib import time import oauth2 as oauth from django.conf import settings REQUEST_TOKEN_URL = 'https://www.google.com/accounts/OAuthGetRequestToken' ACCESS_TOKEN_URL = 'https://www.google.com/accounts/OAuthGetAccessToken' AUTHORIZATION_URL = 'https://www.google.com/accounts/OAuthAuthorizeToken' class GoogleOAuthClient(oauth.OAuthClient): def __init__(self, consumer_key, consumer_secret, request_token_url=REQUEST_TOKEN_URL, access_token_url=ACCESS_TOKEN_URL, authorization_url=AUTHORIZATION_URL): self.consumer_key = consumer_key self.consumer_secret = consumer_secret self.request_token_url = request_token_url self.access_token_url = access_token_url self.authorization_url = authorization_url self.consumer = oauth.OAuthConsumer(consumer_key, consumer_secret) self.signature_method = oauth.OAuthSignatureMethod_HMAC_SHA1() def fetch_request_token(self, **kwargs): if not 'scope' in kwargs: kwargs['scope'] = 'http://www.google.com/m8/feeds' oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, http_url=self.request_token_url, parameters=kwargs) oauth_request.sign_request(self.signature_method, self.consumer, None) params = oauth_request.parameters data = urllib.urlencode(params) full_url='%s?%s'%(self.request_token_url, data) response = urllib2.urlopen(full_url) return oauth.OAuthToken.from_string(response.read()) def authorize_token_url(self, token, callback_url=None,): if not callback_url: callback_url = CALLBACK_URL oauth_request = oauth.OAuthRequest.from_token_and_callback(token=token, callback=callback_url, http_url=self.authorization_url,) params = oauth_request.parameters data = urllib.urlencode(params) full_url='%s?%s'%(self.authorization_url, data) return full_url # response = urllib2.urlopen(full_url) # return oauth.OAuthToken.from_string(response.read()) def fetch_access_token(self, token): oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, token=token, http_url=self.access_token_url) oauth_request.sign_request(self.signature_method, self.consumer, token) params = oauth_request.parameters data = urllib.urlencode(params) full_url='%s?%s'%(self.access_token_url, data) response = urllib2.urlopen(full_url) return oauth.OAuthToken.from_string(response.read()) def access_resource(self, url, token, **kwargs): oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, token=token, http_url=url, parameters=kwargs) oauth_request.sign_request(self.signature_method, self.consumer, token) params = oauth_request.parameters data = urllib.urlencode(params) full_url='%s?%s'%(url, data) #print full_url response = urllib2.urlopen(full_url) return response def run_example(): # setup print '** OAuth Python Library Example **' client = GoogleOAuthClient(CONSUMER_KEY, CONSUMER_SECRET) pause() # get request token print '* Obtain a request token ...' pause() print 'REQUEST (via headers)' print 'parameters: %s' % str(oauth_request.parameters) pause() token = client.fetch_request_token(oauth_request) print 'GOT' print 'key: %s' % str(token.key) print 'secret: %s' % str(token.secret) pause() print '* Authorize the request token ...' pause() print 'REQUEST (via url query string)' print 'parameters: %s' % str(oauth_request.parameters) pause() # this will actually occur only on some callback url = client.authorize_token(oauth_request, get_url_only=True) print 'GOT' print url pause() # get access token print '* Obtain an access token ...' pause() print 'REQUEST (via headers)' print 'parameters: %s' % str(oauth_request.parameters) pause() token = client.fetch_access_token(oauth_request) print 'GOT' print 'key: %s' % str(token.key) print 'secret: %s' % str(token.secret) pause() # access some protected resources print '* Access protected resources ...' pause() parameters = {'file': 'vacation.jpg', 'size': 'original', 'oauth_callback': CALLBACK_URL} # resource specific params print 'REQUEST (via post body)' print 'parameters: %s' % str(oauth_request.parameters) pause() params = client.access_resource(oauth_request) print 'GOT' print 'non-oauth parameters: %s' % params pause() def pause(): print '' time.sleep(1) if __name__ == '__main__': run_example() print 'Done.' ================================================ FILE: socialauth/lib/oauthtwitter.py ================================================ #!/usr/bin/env python # # Copyright under GPLv3 '''A class the inherits everything from python-twitter and allows oauth based access Requires: python-twitter simplejson oauth ''' __author__ = "Hameedullah Khan " __version__ = "0.1" from twitter import Api, User try: import json as simplejson except: from django.utils import simplejson from oauth import oauth # Taken from oauth implementation at: http://github.com/harperreed/twitteroauth-python/tree/master REQUEST_TOKEN_URL = 'https://twitter.com/oauth/request_token' ACCESS_TOKEN_URL = 'https://twitter.com/oauth/access_token' AUTHORIZATION_URL = 'http://twitter.com/oauth/authorize' SIGNIN_URL = 'http://twitter.com/oauth/authenticate' class OAuthApi(Api): def __init__(self, consumer_key, consumer_secret, access_token=None): if access_token: Api.__init__(self,access_token.key, access_token.secret) else: Api.__init__(self) self._Consumer = oauth.OAuthConsumer(consumer_key, consumer_secret) self._signature_method = oauth.OAuthSignatureMethod_HMAC_SHA1() self._access_token = access_token def _GetOpener(self): opener = self._urllib.build_opener() return opener def _FetchUrl(self, url, post_data=None, parameters=None, no_cache=None): '''Fetch a URL, optionally caching for a specified time. Args: url: The URL to retrieve post_data: A dict of (str, unicode) key/value pairs. If set, POST will be used. parameters: A dict whose key/value pairs should encoded and added to the query string. [OPTIONAL] no_cache: If true, overrides the cache on the current request Returns: A string containing the body of the response. ''' # Build the extra parameters dict extra_params = {} try: if self._default_params: extra_params.update(self._default_params) except AttributeError: pass if parameters: extra_params.update(parameters) # Add key/value parameters to the query string of the url #url = self._BuildUrl(url, extra_params=extra_params) if post_data: http_method = "POST" extra_params.update(post_data) else: http_method = "GET" req = self._makeOAuthRequest(url, parameters=extra_params, http_method=http_method) self._signRequest(req, self._signature_method) # Get a url opener that can handle Oauth basic auth opener = self._GetOpener() #encoded_post_data = self._EncodePostData(post_data) if post_data: encoded_post_data = req.to_postdata() url = req.get_normalized_http_url() else: url = req.to_url() encoded_post_data = "" no_cache=True # Open and return the URL immediately if we're not going to cache # OR we are posting data if encoded_post_data or no_cache: if encoded_post_data: url_data = opener.open(url, encoded_post_data).read() else: url_data = opener.open(url).read() opener.close() else: # Unique keys are a combination of the url and the username if self._username: key = self._username + ':' + url else: key = url # See if it has been cached before last_cached = self._cache.GetCachedTime(key) # If the cached version is outdated then fetch another and store it if not last_cached or time.time() >= last_cached + self._cache_timeout: url_data = opener.open(url).read() opener.close() self._cache.Set(key, url_data) else: url_data = self._cache.Get(key) # Always return the latest version return url_data def _makeOAuthRequest(self, url, token=None, parameters=None, http_method="GET"): '''Make a OAuth request from url and parameters Args: url: The Url to use for creating OAuth Request parameters: The URL parameters http_method: The HTTP method to use Returns: A OAauthRequest object ''' if not token: token = self._access_token request = oauth.OAuthRequest.from_consumer_and_token( self._Consumer, token=token, http_url=url, parameters=parameters, http_method=http_method) return request def _signRequest(self, req, signature_method=oauth.OAuthSignatureMethod_HMAC_SHA1()): '''Sign a request Reminder: Created this function so incase if I need to add anything to request before signing Args: req: The OAuth request created via _makeOAuthRequest signate_method: The oauth signature method to use ''' req.sign_request(signature_method, self._Consumer, self._access_token) def getAuthorizationURL(self, token, url=AUTHORIZATION_URL, callback_url = None): '''Create a signed authorization URL Returns: A signed OAuthRequest authorization URL ''' if callback_url: parameters= {'oauth_callback':callback_url} else: parameters= {} req = self._makeOAuthRequest(url, token=token, parameters=parameters) self._signRequest(req) return req.to_url() def getSigninURL(self, token, url=SIGNIN_URL, callback_url = None): '''Create a signed Sign-in URL Returns: A signed OAuthRequest Sign-in URL ''' signin_url = self.getAuthorizationURL(token, url, callback_url) return signin_url def getAccessToken(self, url=ACCESS_TOKEN_URL): token = self._FetchUrl(url, no_cache=True) return oauth.OAuthToken.from_string(token) def getRequestToken(self, url=REQUEST_TOKEN_URL): '''Get a Request Token from Twitter Returns: A OAuthToken object containing a request token ''' resp = self._FetchUrl(url, no_cache=True) token = oauth.OAuthToken.from_string(resp) return token def GetUserInfo(self, url='https://twitter.com/account/verify_credentials.json'): '''Get user information from twitter Returns: Returns the twitter.User object ''' json = self._FetchUrl(url) data = simplejson.loads(json) self._CheckForTwitterError(data) return User.NewFromJsonDict(data) ================================================ FILE: socialauth/lib/oauthtwitter2.py ================================================ import time from urllib2 import Request, urlopen import oauth1 as oauth from twitter import User from django.utils import simplejson as json TWITTER_URL = 'twitter.com' REQUEST_TOKEN_URL = 'https://api.twitter.com/oauth/request_token' ACCESS_TOKEN_URL = 'https://api.twitter.com/oauth/access_token' AUTHORIZATION_URL = 'https://api.twitter.com/oauth/authorize' TWITTER_CREDENTIALS_URL = 'https://api.twitter.com/1/account/verify_credentials.json' def oauth_response(req): response = urlopen(Request(req.http_url, headers=req.to_header())) return response.read() class TwitterOAuthClient(oauth.OAuthClient): def __init__(self, consumer_key, consumer_secret, request_token_url=REQUEST_TOKEN_URL, access_token_url=ACCESS_TOKEN_URL, authorization_url=AUTHORIZATION_URL): self.consumer_secret = consumer_secret self.consumer_key = consumer_key self.consumer = oauth.OAuthConsumer(consumer_key, consumer_secret) self.signature_method = oauth.OAuthSignatureMethod_HMAC_SHA1() self.request_token_url = request_token_url self.access_token_url = access_token_url self.authorization_url = authorization_url def fetch_request_token(self, callback): oauth_request = oauth.OAuthRequest.from_consumer_and_token( self.consumer, http_url=self.request_token_url, callback=callback ) oauth_request.sign_request(self.signature_method, self.consumer, None) return oauth.OAuthToken.from_string(oauth_response(oauth_request)) def authorize_token_url(self, token, callback_url=None): oauth_request = oauth.OAuthRequest.from_consumer_and_token( self.consumer, token=token, http_url=self.authorization_url, callback=callback_url ) oauth_request.sign_request(self.signature_method, self.consumer, token) return oauth_request.to_url() def fetch_access_token(self, token, verifier): oauth_request = oauth.OAuthRequest.from_consumer_and_token( self.consumer, token=token, verifier=verifier, http_url=self.access_token_url ) oauth_request.http_method = 'POST' oauth_request.sign_request(self.signature_method, self.consumer, token) return oauth.OAuthToken.from_string(oauth_response(oauth_request)) def get_user_info(self, token): try: oauth_request = oauth.OAuthRequest.from_consumer_and_token( self.consumer, token=token, http_url=TWITTER_CREDENTIALS_URL ) oauth_request.sign_request(self.signature_method, self.consumer, token) return User.NewFromJsonDict(json.loads(oauth_response(oauth_request))) except: pass return None def access_resource(self, oauth_request): # via post body # -> some protected resources headers = {'Content-Type': 'application/x-www-form-urlencoded'} connection = get_connection() body = oauth_request.to_postdata() connection.request('POST', RESOURCE_URL, body=body, headers=headers) response = connection.getresponse().read() connection.close() return response def run_example(): # setup print '** OAuth Python Library Example **' consumer_key = raw_input('Twitter Consumer Key:').strip() consumer_secret = raw_input('Twitter Consumer Secret:').strip() callback = 'http://example.com/newaccounts/login/done/' client = TwitterOAuthClient(consumer_key, consumer_secret) pause() # get request token print '* Obtain a request token ...' pause() token = client.fetch_request_token(callback) print 'GOT' print 'key: %s' % str(token.key) print 'secret: %s' % str(token.secret) pause() print '* Authorize the request token ...' pause() # this will actually occur only on some callback url = client.authorize_token_url(token) print 'GOT' print url pause() # get access token print '* Obtain an access token ...' pause() access_token = client.fetch_access_token(token) print 'GOT' print 'key: %s' % str(access_token.key) print 'secret: %s' % str(access_token.secret) pause() # access some protected resources print '* Access protected resources ...' pause() parameters = { 'file': 'vacation.jpg', 'size': 'original', 'oauth_callback': callback, } # resource specific params oauth_request = oauth.OAuthRequest.from_consumer_and_token( client.consumer, token=token, http_method='POST', http_url=RESOURCE_URL, parameters=parameters ) oauth_request.sign_request(client.signature_method, client.consumer, token) print 'REQUEST (via post body)' print 'parameters: %s' % str(oauth_request.parameters) pause() params = client.access_resource(oauth_request) print 'GOT' print 'non-oauth parameters: %s' % params pause() def pause(): print '' time.sleep(1) if __name__ == '__main__': run_example() print 'Done.' ================================================ FILE: socialauth/lib/oauthyahoo.py ================================================ import httplib import urllib2 import urllib import time import oauth.oauth as oauth from django.conf import settings REQUEST_TOKEN_URL = 'https://api.login.yahoo.com/oauth/v2/get_request_token' ACCESS_TOKEN_URL = 'https://api.login.yahoo.com/oauth/v2/get_token' AUTHORIZATION_URL = 'https://api.login.yahoo.com/oauth/v2/request_auth' class YahooOAuthClient(oauth.OAuthClient): def __init__(self, consumer_key, consumer_secret, request_token_url=REQUEST_TOKEN_URL, access_token_url=ACCESS_TOKEN_URL, authorization_url=AUTHORIZATION_URL): self.consumer_key = consumer_key self.consumer_secret = consumer_secret self.request_token_url = request_token_url self.access_token_url = access_token_url self.authorization_url = authorization_url self.consumer = oauth.OAuthConsumer(consumer_key, consumer_secret) self.signature_method = oauth.OAuthSignatureMethod_HMAC_SHA1() def fetch_request_token(self, **kwargs): oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, http_url=self.request_token_url, parameters=kwargs) oauth_request.sign_request(self.signature_method, self.consumer, None) params = oauth_request.parameters data = urllib.urlencode(params) full_url='%s?%s'%(self.request_token_url, data) response = urllib2.urlopen(full_url) return oauth.OAuthToken.from_string(response.read()) def authorize_token_url(self, token, callback_url=None): oauth_request = oauth.OAuthRequest.from_token_and_callback(token=token, callback=callback_url, http_url=self.authorization_url) params = oauth_request.parameters data = urllib.urlencode(params) full_url='%s?%s'%(self.authorization_url, data) return full_url response = urllib2.urlopen(full_url) return oauth.OAuthToken.from_string(response.read()) def fetch_access_token(self, token, **kwargs): oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, token=token, http_url=self.access_token_url, parameters = kwargs) oauth_request.sign_request(self.signature_method, self.consumer, token) params = oauth_request.parameters data = urllib.urlencode(params) full_url='%s?%s'%(self.access_token_url, data) response = urllib2.urlopen(full_url) return response #return oauth.OAuthToken.from_string(response.read()) def access_resource(self, url, token, **kwargs): oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, token=token, http_url=url, parameters = kwargs) oauth_request.sign_request(self.signature_method, self.consumer, token) params = oauth_request.parameters data = urllib.urlencode(params) full_url='%s?%s'%(url, data) response = urllib2.urlopen(full_url) return response def run_example(): # setup print '** OAuth Python Library Example **' client = GoogleOAuthClient(CONSUMER_KEY, CONSUMER_SECRET) pause() # get request token print '* Obtain a request token ...' pause() print 'REQUEST (via headers)' print 'parameters: %s' % str(oauth_request.parameters) pause() token = client.fetch_request_token(oauth_request) print 'GOT' print 'key: %s' % str(token.key) print 'secret: %s' % str(token.secret) pause() print '* Authorize the request token ...' pause() print 'REQUEST (via url query string)' print 'parameters: %s' % str(oauth_request.parameters) pause() # this will actually occur only on some callback url = client.authorize_token(oauth_request, get_url_only=True) print 'GOT' print url pause() # get access token print '* Obtain an access token ...' pause() print 'REQUEST (via headers)' print 'parameters: %s' % str(oauth_request.parameters) pause() token = client.fetch_access_token(oauth_request) print 'GOT' print 'key: %s' % str(token.key) print 'secret: %s' % str(token.secret) pause() # access some protected resources print '* Access protected resources ...' pause() parameters = {'file': 'vacation.jpg', 'size': 'original', 'oauth_callback': CALLBACK_URL} # resource specific params print 'REQUEST (via post body)' print 'parameters: %s' % str(oauth_request.parameters) pause() params = client.access_resource(oauth_request) print 'GOT' print 'non-oauth parameters: %s' % params pause() def pause(): print '' time.sleep(1) if __name__ == '__main__': run_example() print 'Done.' ================================================ FILE: socialauth/lib/twitter.py ================================================ #!/usr/bin/python2.4 # # Copyright 2007 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. '''A library that provides a python interface to the Twitter API''' __author__ = 'dewitt@google.com' __version__ = '0.6-devel' import base64 import calendar import os import rfc822 try: import json as simplejson except: from django.utils import simplejson import sys import tempfile import textwrap import time import urllib import urllib2 import urlparse try: from hashlib import md5 except ImportError: from md5 import md5 CHARACTER_LIMIT = 140 class TwitterError(Exception): '''Base class for Twitter errors''' @property def message(self): '''Returns the first argument used to construct this error.''' return self.args[0] class Status(object): '''A class representing the Status structure used by the twitter API. The Status structure exposes the following properties: status.created_at status.created_at_in_seconds # read only status.favorited status.in_reply_to_screen_name status.in_reply_to_user_id status.in_reply_to_status_id status.truncated status.source status.id status.text status.relative_created_at # read only status.user ''' def __init__(self, created_at=None, favorited=None, id=None, text=None, user=None, in_reply_to_screen_name=None, in_reply_to_user_id=None, in_reply_to_status_id=None, truncated=None, source=None, now=None): '''An object to hold a Twitter status message. This class is normally instantiated by the twitter.Api class and returned in a sequence. Note: Dates are posted in the form "Sat Jan 27 04:17:38 +0000 2007" Args: created_at: The time this status message was posted favorited: Whether this is a favorite of the authenticated user id: The unique id of this status message text: The text of this status message relative_created_at: A human readable string representing the posting time user: A twitter.User instance representing the person posting the message now: The current time, if the client choses to set it. Defaults to the wall clock time. ''' self.created_at = created_at self.favorited = favorited self.id = id self.text = text self.user = user self.now = now self.in_reply_to_screen_name = in_reply_to_screen_name self.in_reply_to_user_id = in_reply_to_user_id self.in_reply_to_status_id = in_reply_to_status_id self.truncated = truncated self.source = source def GetCreatedAt(self): '''Get the time this status message was posted. Returns: The time this status message was posted ''' return self._created_at def SetCreatedAt(self, created_at): '''Set the time this status message was posted. Args: created_at: The time this status message was created ''' self._created_at = created_at created_at = property(GetCreatedAt, SetCreatedAt, doc='The time this status message was posted.') def GetCreatedAtInSeconds(self): '''Get the time this status message was posted, in seconds since the epoch. Returns: The time this status message was posted, in seconds since the epoch. ''' return calendar.timegm(rfc822.parsedate(self.created_at)) created_at_in_seconds = property(GetCreatedAtInSeconds, doc="The time this status message was " "posted, in seconds since the epoch") def GetFavorited(self): '''Get the favorited setting of this status message. Returns: True if this status message is favorited; False otherwise ''' return self._favorited def SetFavorited(self, favorited): '''Set the favorited state of this status message. Args: favorited: boolean True/False favorited state of this status message ''' self._favorited = favorited favorited = property(GetFavorited, SetFavorited, doc='The favorited state of this status message.') def GetId(self): '''Get the unique id of this status message. Returns: The unique id of this status message ''' return self._id def SetId(self, id): '''Set the unique id of this status message. Args: id: The unique id of this status message ''' self._id = id id = property(GetId, SetId, doc='The unique id of this status message.') def GetInReplyToScreenName(self): return self._in_reply_to_screen_name def SetInReplyToScreenName(self, in_reply_to_screen_name): self._in_reply_to_screen_name = in_reply_to_screen_name in_reply_to_screen_name = property(GetInReplyToScreenName, SetInReplyToScreenName, doc='') def GetInReplyToUserId(self): return self._in_reply_to_user_id def SetInReplyToUserId(self, in_reply_to_user_id): self._in_reply_to_user_id = in_reply_to_user_id in_reply_to_user_id = property(GetInReplyToUserId, SetInReplyToUserId, doc='') def GetInReplyToStatusId(self): return self._in_reply_to_status_id def SetInReplyToStatusId(self, in_reply_to_status_id): self._in_reply_to_status_id = in_reply_to_status_id in_reply_to_status_id = property(GetInReplyToStatusId, SetInReplyToStatusId, doc='') def GetTruncated(self): return self._truncated def SetTruncated(self, truncated): self._truncated = truncated truncated = property(GetTruncated, SetTruncated, doc='') def GetSource(self): return self._source def SetSource(self, source): self._source = source source = property(GetSource, SetSource, doc='') def GetText(self): '''Get the text of this status message. Returns: The text of this status message. ''' return self._text def SetText(self, text): '''Set the text of this status message. Args: text: The text of this status message ''' self._text = text text = property(GetText, SetText, doc='The text of this status message') def GetRelativeCreatedAt(self): '''Get a human redable string representing the posting time Returns: A human readable string representing the posting time ''' fudge = 1.25 delta = long(self.now) - long(self.created_at_in_seconds) if delta < (1 * fudge): return 'about a second ago' elif delta < (60 * (1/fudge)): return 'about %d seconds ago' % (delta) elif delta < (60 * fudge): return 'about a minute ago' elif delta < (60 * 60 * (1/fudge)): return 'about %d minutes ago' % (delta / 60) elif delta < (60 * 60 * fudge): return 'about an hour ago' elif delta < (60 * 60 * 24 * (1/fudge)): return 'about %d hours ago' % (delta / (60 * 60)) elif delta < (60 * 60 * 24 * fudge): return 'about a day ago' else: return 'about %d days ago' % (delta / (60 * 60 * 24)) relative_created_at = property(GetRelativeCreatedAt, doc='Get a human readable string representing' 'the posting time') def GetUser(self): '''Get a twitter.User reprenting the entity posting this status message. Returns: A twitter.User reprenting the entity posting this status message ''' return self._user def SetUser(self, user): '''Set a twitter.User reprenting the entity posting this status message. Args: user: A twitter.User reprenting the entity posting this status message ''' self._user = user user = property(GetUser, SetUser, doc='A twitter.User reprenting the entity posting this ' 'status message') def GetNow(self): '''Get the wallclock time for this status message. Used to calculate relative_created_at. Defaults to the time the object was instantiated. Returns: Whatever the status instance believes the current time to be, in seconds since the epoch. ''' if self._now is None: self._now = time.time() return self._now def SetNow(self, now): '''Set the wallclock time for this status message. Used to calculate relative_created_at. Defaults to the time the object was instantiated. Args: now: The wallclock time for this instance. ''' self._now = now now = property(GetNow, SetNow, doc='The wallclock time for this status instance.') def __ne__(self, other): return not self.__eq__(other) def __eq__(self, other): try: return other and \ self.created_at == other.created_at and \ self.id == other.id and \ self.text == other.text and \ self.user == other.user and \ self.in_reply_to_screen_name == other.in_reply_to_screen_name and \ self.in_reply_to_user_id == other.in_reply_to_user_id and \ self.in_reply_to_status_id == other.in_reply_to_status_id and \ self.truncated == other.truncated and \ self.favorited == other.favorited and \ self.source == other.source except AttributeError: return False def __str__(self): '''A string representation of this twitter.Status instance. The return value is the same as the JSON string representation. Returns: A string representation of this twitter.Status instance. ''' return self.AsJsonString() def AsJsonString(self): '''A JSON string representation of this twitter.Status instance. Returns: A JSON string representation of this twitter.Status instance ''' return simplejson.dumps(self.AsDict(), sort_keys=True) def AsDict(self): '''A dict representation of this twitter.Status instance. The return value uses the same key names as the JSON representation. Return: A dict representing this twitter.Status instance ''' data = {} if self.created_at: data['created_at'] = self.created_at if self.favorited: data['favorited'] = self.favorited if self.id: data['id'] = self.id if self.text: data['text'] = self.text if self.user: data['user'] = self.user.AsDict() if self.in_reply_to_screen_name: data['in_reply_to_screen_name'] = self.in_reply_to_screen_name if self.in_reply_to_user_id: data['in_reply_to_user_id'] = self.in_reply_to_user_id if self.in_reply_to_status_id: data['in_reply_to_status_id'] = self.in_reply_to_status_id if self.truncated is not None: data['truncated'] = self.truncated if self.favorited is not None: data['favorited'] = self.favorited if self.source: data['source'] = self.source return data @staticmethod def NewFromJsonDict(data): '''Create a new instance based on a JSON dict. Args: data: A JSON dict, as converted from the JSON in the twitter API Returns: A twitter.Status instance ''' if 'user' in data: user = User.NewFromJsonDict(data['user']) else: user = None return Status(created_at=data.get('created_at', None), favorited=data.get('favorited', None), id=data.get('id', None), text=data.get('text', None), in_reply_to_screen_name=data.get('in_reply_to_screen_name', None), in_reply_to_user_id=data.get('in_reply_to_user_id', None), in_reply_to_status_id=data.get('in_reply_to_status_id', None), truncated=data.get('truncated', None), source=data.get('source', None), user=user) class User(object): '''A class representing the User structure used by the twitter API. The User structure exposes the following properties: user.id user.name user.screen_name user.location user.description user.profile_image_url user.profile_background_tile user.profile_background_image_url user.profile_sidebar_fill_color user.profile_background_color user.profile_link_color user.profile_text_color user.protected user.utc_offset user.time_zone user.url user.status user.statuses_count user.followers_count user.friends_count user.favourites_count ''' def __init__(self, id=None, name=None, screen_name=None, location=None, description=None, profile_image_url=None, profile_background_tile=None, profile_background_image_url=None, profile_sidebar_fill_color=None, profile_background_color=None, profile_link_color=None, profile_text_color=None, protected=None, utc_offset=None, time_zone=None, followers_count=None, friends_count=None, statuses_count=None, favourites_count=None, url=None, status=None): self.id = id self.name = name self.screen_name = screen_name self.location = location self.description = description self.profile_image_url = profile_image_url self.profile_background_tile = profile_background_tile self.profile_background_image_url = profile_background_image_url self.profile_sidebar_fill_color = profile_sidebar_fill_color self.profile_background_color = profile_background_color self.profile_link_color = profile_link_color self.profile_text_color = profile_text_color self.protected = protected self.utc_offset = utc_offset self.time_zone = time_zone self.followers_count = followers_count self.friends_count = friends_count self.statuses_count = statuses_count self.favourites_count = favourites_count self.url = url self.status = status def GetId(self): '''Get the unique id of this user. Returns: The unique id of this user ''' return self._id def SetId(self, id): '''Set the unique id of this user. Args: id: The unique id of this user. ''' self._id = id id = property(GetId, SetId, doc='The unique id of this user.') def GetName(self): '''Get the real name of this user. Returns: The real name of this user ''' return self._name def SetName(self, name): '''Set the real name of this user. Args: name: The real name of this user ''' self._name = name name = property(GetName, SetName, doc='The real name of this user.') def GetScreenName(self): '''Get the short username of this user. Returns: The short username of this user ''' return self._screen_name def SetScreenName(self, screen_name): '''Set the short username of this user. Args: screen_name: the short username of this user ''' self._screen_name = screen_name screen_name = property(GetScreenName, SetScreenName, doc='The short username of this user.') def GetLocation(self): '''Get the geographic location of this user. Returns: The geographic location of this user ''' return self._location def SetLocation(self, location): '''Set the geographic location of this user. Args: location: The geographic location of this user ''' self._location = location location = property(GetLocation, SetLocation, doc='The geographic location of this user.') def GetDescription(self): '''Get the short text description of this user. Returns: The short text description of this user ''' return self._description def SetDescription(self, description): '''Set the short text description of this user. Args: description: The short text description of this user ''' self._description = description description = property(GetDescription, SetDescription, doc='The short text description of this user.') def GetUrl(self): '''Get the homepage url of this user. Returns: The homepage url of this user ''' return self._url def SetUrl(self, url): '''Set the homepage url of this user. Args: url: The homepage url of this user ''' self._url = url url = property(GetUrl, SetUrl, doc='The homepage url of this user.') def GetProfileImageUrl(self): '''Get the url of the thumbnail of this user. Returns: The url of the thumbnail of this user ''' return self._profile_image_url def SetProfileImageUrl(self, profile_image_url): '''Set the url of the thumbnail of this user. Args: profile_image_url: The url of the thumbnail of this user ''' self._profile_image_url = profile_image_url profile_image_url= property(GetProfileImageUrl, SetProfileImageUrl, doc='The url of the thumbnail of this user.') def GetProfileBackgroundTile(self): '''Boolean for whether to tile the profile background image. Returns: True if the background is to be tiled, False if not, None if unset. ''' return self._profile_background_tile def SetProfileBackgroundTile(self, profile_background_tile): '''Set the boolean flag for whether to tile the profile background image. Args: profile_background_tile: Boolean flag for whether to tile or not. ''' self._profile_background_tile = profile_background_tile profile_background_tile = property(GetProfileBackgroundTile, SetProfileBackgroundTile, doc='Boolean for whether to tile the background image.') def GetProfileBackgroundImageUrl(self): return self._profile_background_image_url def SetProfileBackgroundImageUrl(self, profile_background_image_url): self._profile_background_image_url = profile_background_image_url profile_background_image_url = property(GetProfileBackgroundImageUrl, SetProfileBackgroundImageUrl, doc='The url of the profile background of this user.') def GetProfileSidebarFillColor(self): return self._profile_sidebar_fill_color def SetProfileSidebarFillColor(self, profile_sidebar_fill_color): self._profile_sidebar_fill_color = profile_sidebar_fill_color profile_sidebar_fill_color = property(GetProfileSidebarFillColor, SetProfileSidebarFillColor) def GetProfileBackgroundColor(self): return self._profile_background_color def SetProfileBackgroundColor(self, profile_background_color): self._profile_background_color = profile_background_color profile_background_color = property(GetProfileBackgroundColor, SetProfileBackgroundColor) def GetProfileLinkColor(self): return self._profile_link_color def SetProfileLinkColor(self, profile_link_color): self._profile_link_color = profile_link_color profile_link_color = property(GetProfileLinkColor, SetProfileLinkColor) def GetProfileTextColor(self): return self._profile_text_color def SetProfileTextColor(self, profile_text_color): self._profile_text_color = profile_text_color profile_text_color = property(GetProfileTextColor, SetProfileTextColor) def GetProtected(self): return self._protected def SetProtected(self, protected): self._protected = protected protected = property(GetProtected, SetProtected) def GetUtcOffset(self): return self._utc_offset def SetUtcOffset(self, utc_offset): self._utc_offset = utc_offset utc_offset = property(GetUtcOffset, SetUtcOffset) def GetTimeZone(self): '''Returns the current time zone string for the user. Returns: The descriptive time zone string for the user. ''' return self._time_zone def SetTimeZone(self, time_zone): '''Sets the user's time zone string. Args: time_zone: The descriptive time zone to assign for the user. ''' self._time_zone = time_zone time_zone = property(GetTimeZone, SetTimeZone) def GetStatus(self): '''Get the latest twitter.Status of this user. Returns: The latest twitter.Status of this user ''' return self._status def SetStatus(self, status): '''Set the latest twitter.Status of this user. Args: status: The latest twitter.Status of this user ''' self._status = status status = property(GetStatus, SetStatus, doc='The latest twitter.Status of this user.') def GetFriendsCount(self): '''Get the friend count for this user. Returns: The number of users this user has befriended. ''' return self._friends_count def SetFriendsCount(self, count): '''Set the friend count for this user. Args: count: The number of users this user has befriended. ''' self._friends_count = count friends_count = property(GetFriendsCount, SetFriendsCount, doc='The number of friends for this user.') def GetFollowersCount(self): '''Get the follower count for this user. Returns: The number of users following this user. ''' return self._followers_count def SetFollowersCount(self, count): '''Set the follower count for this user. Args: count: The number of users following this user. ''' self._followers_count = count followers_count = property(GetFollowersCount, SetFollowersCount, doc='The number of users following this user.') def GetStatusesCount(self): '''Get the number of status updates for this user. Returns: The number of status updates for this user. ''' return self._statuses_count def SetStatusesCount(self, count): '''Set the status update count for this user. Args: count: The number of updates for this user. ''' self._statuses_count = count statuses_count = property(GetStatusesCount, SetStatusesCount, doc='The number of updates for this user.') def GetFavouritesCount(self): '''Get the number of favourites for this user. Returns: The number of favourites for this user. ''' return self._favourites_count def SetFavouritesCount(self, count): '''Set the favourite count for this user. Args: count: The number of favourites for this user. ''' self._favourites_count = count favourites_count = property(GetFavouritesCount, SetFavouritesCount, doc='The number of favourites for this user.') def __ne__(self, other): return not self.__eq__(other) def __eq__(self, other): try: return other and \ self.id == other.id and \ self.name == other.name and \ self.screen_name == other.screen_name and \ self.location == other.location and \ self.description == other.description and \ self.profile_image_url == other.profile_image_url and \ self.profile_background_tile == other.profile_background_tile and \ self.profile_background_image_url == other.profile_background_image_url and \ self.profile_sidebar_fill_color == other.profile_sidebar_fill_color and \ self.profile_background_color == other.profile_background_color and \ self.profile_link_color == other.profile_link_color and \ self.profile_text_color == other.profile_text_color and \ self.protected == other.protected and \ self.utc_offset == other.utc_offset and \ self.time_zone == other.time_zone and \ self.url == other.url and \ self.statuses_count == other.statuses_count and \ self.followers_count == other.followers_count and \ self.favourites_count == other.favourites_count and \ self.friends_count == other.friends_count and \ self.status == other.status except AttributeError: return False def __str__(self): '''A string representation of this twitter.User instance. The return value is the same as the JSON string representation. Returns: A string representation of this twitter.User instance. ''' return self.AsJsonString() def AsJsonString(self): '''A JSON string representation of this twitter.User instance. Returns: A JSON string representation of this twitter.User instance ''' return simplejson.dumps(self.AsDict(), sort_keys=True) def AsDict(self): '''A dict representation of this twitter.User instance. The return value uses the same key names as the JSON representation. Return: A dict representing this twitter.User instance ''' data = {} if self.id: data['id'] = self.id if self.name: data['name'] = self.name if self.screen_name: data['screen_name'] = self.screen_name if self.location: data['location'] = self.location if self.description: data['description'] = self.description if self.profile_image_url: data['profile_image_url'] = self.profile_image_url if self.profile_background_tile is not None: data['profile_background_tile'] = self.profile_background_tile if self.profile_background_image_url: data['profile_sidebar_fill_color'] = self.profile_background_image_url if self.profile_background_color: data['profile_background_color'] = self.profile_background_color if self.profile_link_color: data['profile_link_color'] = self.profile_link_color if self.profile_text_color: data['profile_text_color'] = self.profile_text_color if self.protected is not None: data['protected'] = self.protected if self.utc_offset: data['utc_offset'] = self.utc_offset if self.time_zone: data['time_zone'] = self.time_zone if self.url: data['url'] = self.url if self.status: data['status'] = self.status.AsDict() if self.friends_count: data['friends_count'] = self.friends_count if self.followers_count: data['followers_count'] = self.followers_count if self.statuses_count: data['statuses_count'] = self.statuses_count if self.favourites_count: data['favourites_count'] = self.favourites_count return data @staticmethod def NewFromJsonDict(data): '''Create a new instance based on a JSON dict. Args: data: A JSON dict, as converted from the JSON in the twitter API Returns: A twitter.User instance ''' if 'status' in data: status = Status.NewFromJsonDict(data['status']) else: status = None return User(id=data.get('id', None), name=data.get('name', None), screen_name=data.get('screen_name', None), location=data.get('location', None), description=data.get('description', None), statuses_count=data.get('statuses_count', None), followers_count=data.get('followers_count', None), favourites_count=data.get('favourites_count', None), friends_count=data.get('friends_count', None), profile_image_url=data.get('profile_image_url', None), profile_background_tile = data.get('profile_background_tile', None), profile_background_image_url = data.get('profile_background_image_url', None), profile_sidebar_fill_color = data.get('profile_sidebar_fill_color', None), profile_background_color = data.get('profile_background_color', None), profile_link_color = data.get('profile_link_color', None), profile_text_color = data.get('profile_text_color', None), protected = data.get('protected', None), utc_offset = data.get('utc_offset', None), time_zone = data.get('time_zone', None), url=data.get('url', None), status=status) class DirectMessage(object): '''A class representing the DirectMessage structure used by the twitter API. The DirectMessage structure exposes the following properties: direct_message.id direct_message.created_at direct_message.created_at_in_seconds # read only direct_message.sender_id direct_message.sender_screen_name direct_message.recipient_id direct_message.recipient_screen_name direct_message.text ''' def __init__(self, id=None, created_at=None, sender_id=None, sender_screen_name=None, recipient_id=None, recipient_screen_name=None, text=None): '''An object to hold a Twitter direct message. This class is normally instantiated by the twitter.Api class and returned in a sequence. Note: Dates are posted in the form "Sat Jan 27 04:17:38 +0000 2007" Args: id: The unique id of this direct message created_at: The time this direct message was posted sender_id: The id of the twitter user that sent this message sender_screen_name: The name of the twitter user that sent this message recipient_id: The id of the twitter that received this message recipient_screen_name: The name of the twitter that received this message text: The text of this direct message ''' self.id = id self.created_at = created_at self.sender_id = sender_id self.sender_screen_name = sender_screen_name self.recipient_id = recipient_id self.recipient_screen_name = recipient_screen_name self.text = text def GetId(self): '''Get the unique id of this direct message. Returns: The unique id of this direct message ''' return self._id def SetId(self, id): '''Set the unique id of this direct message. Args: id: The unique id of this direct message ''' self._id = id id = property(GetId, SetId, doc='The unique id of this direct message.') def GetCreatedAt(self): '''Get the time this direct message was posted. Returns: The time this direct message was posted ''' return self._created_at def SetCreatedAt(self, created_at): '''Set the time this direct message was posted. Args: created_at: The time this direct message was created ''' self._created_at = created_at created_at = property(GetCreatedAt, SetCreatedAt, doc='The time this direct message was posted.') def GetCreatedAtInSeconds(self): '''Get the time this direct message was posted, in seconds since the epoch. Returns: The time this direct message was posted, in seconds since the epoch. ''' return calendar.timegm(rfc822.parsedate(self.created_at)) created_at_in_seconds = property(GetCreatedAtInSeconds, doc="The time this direct message was " "posted, in seconds since the epoch") def GetSenderId(self): '''Get the unique sender id of this direct message. Returns: The unique sender id of this direct message ''' return self._sender_id def SetSenderId(self, sender_id): '''Set the unique sender id of this direct message. Args: sender id: The unique sender id of this direct message ''' self._sender_id = sender_id sender_id = property(GetSenderId, SetSenderId, doc='The unique sender id of this direct message.') def GetSenderScreenName(self): '''Get the unique sender screen name of this direct message. Returns: The unique sender screen name of this direct message ''' return self._sender_screen_name def SetSenderScreenName(self, sender_screen_name): '''Set the unique sender screen name of this direct message. Args: sender_screen_name: The unique sender screen name of this direct message ''' self._sender_screen_name = sender_screen_name sender_screen_name = property(GetSenderScreenName, SetSenderScreenName, doc='The unique sender screen name of this direct message.') def GetRecipientId(self): '''Get the unique recipient id of this direct message. Returns: The unique recipient id of this direct message ''' return self._recipient_id def SetRecipientId(self, recipient_id): '''Set the unique recipient id of this direct message. Args: recipient id: The unique recipient id of this direct message ''' self._recipient_id = recipient_id recipient_id = property(GetRecipientId, SetRecipientId, doc='The unique recipient id of this direct message.') def GetRecipientScreenName(self): '''Get the unique recipient screen name of this direct message. Returns: The unique recipient screen name of this direct message ''' return self._recipient_screen_name def SetRecipientScreenName(self, recipient_screen_name): '''Set the unique recipient screen name of this direct message. Args: recipient_screen_name: The unique recipient screen name of this direct message ''' self._recipient_screen_name = recipient_screen_name recipient_screen_name = property(GetRecipientScreenName, SetRecipientScreenName, doc='The unique recipient screen name of this direct message.') def GetText(self): '''Get the text of this direct message. Returns: The text of this direct message. ''' return self._text def SetText(self, text): '''Set the text of this direct message. Args: text: The text of this direct message ''' self._text = text text = property(GetText, SetText, doc='The text of this direct message') def __ne__(self, other): return not self.__eq__(other) def __eq__(self, other): try: return other and \ self.id == other.id and \ self.created_at == other.created_at and \ self.sender_id == other.sender_id and \ self.sender_screen_name == other.sender_screen_name and \ self.recipient_id == other.recipient_id and \ self.recipient_screen_name == other.recipient_screen_name and \ self.text == other.text except AttributeError: return False def __str__(self): '''A string representation of this twitter.DirectMessage instance. The return value is the same as the JSON string representation. Returns: A string representation of this twitter.DirectMessage instance. ''' return self.AsJsonString() def AsJsonString(self): '''A JSON string representation of this twitter.DirectMessage instance. Returns: A JSON string representation of this twitter.DirectMessage instance ''' return simplejson.dumps(self.AsDict(), sort_keys=True) def AsDict(self): '''A dict representation of this twitter.DirectMessage instance. The return value uses the same key names as the JSON representation. Return: A dict representing this twitter.DirectMessage instance ''' data = {} if self.id: data['id'] = self.id if self.created_at: data['created_at'] = self.created_at if self.sender_id: data['sender_id'] = self.sender_id if self.sender_screen_name: data['sender_screen_name'] = self.sender_screen_name if self.recipient_id: data['recipient_id'] = self.recipient_id if self.recipient_screen_name: data['recipient_screen_name'] = self.recipient_screen_name if self.text: data['text'] = self.text return data @staticmethod def NewFromJsonDict(data): '''Create a new instance based on a JSON dict. Args: data: A JSON dict, as converted from the JSON in the twitter API Returns: A twitter.DirectMessage instance ''' return DirectMessage(created_at=data.get('created_at', None), recipient_id=data.get('recipient_id', None), sender_id=data.get('sender_id', None), text=data.get('text', None), sender_screen_name=data.get('sender_screen_name', None), id=data.get('id', None), recipient_screen_name=data.get('recipient_screen_name', None)) class Api(object): '''A python interface into the Twitter API By default, the Api caches results for 1 minute. Example usage: To create an instance of the twitter.Api class, with no authentication: >>> import twitter >>> api = twitter.Api() To fetch the most recently posted public twitter status messages: >>> statuses = api.GetPublicTimeline() >>> print [s.user.name for s in statuses] [u'DeWitt', u'Kesuke Miyagi', u'ev', u'Buzz Andersen', u'Biz Stone'] #... To fetch a single user's public status messages, where "user" is either a Twitter "short name" or their user id. >>> statuses = api.GetUserTimeline(user) >>> print [s.text for s in statuses] To use authentication, instantiate the twitter.Api class with a username and password: >>> api = twitter.Api(username='twitter user', password='twitter pass') To fetch your friends (after being authenticated): >>> users = api.GetFriends() >>> print [u.name for u in users] To post a twitter status message (after being authenticated): >>> status = api.PostUpdate('I love python-twitter!') >>> print status.text I love python-twitter! There are many other methods, including: >>> api.PostUpdates(status) >>> api.PostDirectMessage(user, text) >>> api.GetUser(user) >>> api.GetReplies() >>> api.GetUserTimeline(user) >>> api.GetStatus(id) >>> api.DestroyStatus(id) >>> api.GetFriendsTimeline(user) >>> api.GetFriends(user) >>> api.GetFollowers() >>> api.GetFeatured() >>> api.GetDirectMessages() >>> api.PostDirectMessage(user, text) >>> api.DestroyDirectMessage(id) >>> api.DestroyFriendship(user) >>> api.CreateFriendship(user) >>> api.GetUserByEmail(email) ''' DEFAULT_CACHE_TIMEOUT = 60 # cache for 1 minute _API_REALM = 'Twitter API' def __init__(self, username=None, password=None, input_encoding=None, request_headers=None): '''Instantiate a new twitter.Api object. Args: username: The username of the twitter account. [optional] password: The password for the twitter account. [optional] input_encoding: The encoding used to encode input strings. [optional] request_header: A dictionary of additional HTTP request headers. [optional] ''' self._cache = _FileCache() self._urllib = urllib2 self._cache_timeout = Api.DEFAULT_CACHE_TIMEOUT self._InitializeRequestHeaders(request_headers) self._InitializeUserAgent() self._InitializeDefaultParameters() self._input_encoding = input_encoding self.SetCredentials(username, password) def GetPublicTimeline(self, since_id=None): '''Fetch the sequnce of public twitter.Status message for all users. Args: since_id: Returns only public statuses with an ID greater than (that is, more recent than) the specified ID. [Optional] Returns: An sequence of twitter.Status instances, one for each message ''' parameters = {} if since_id: parameters['since_id'] = since_id url = 'http://twitter.com/statuses/public_timeline.json' json = self._FetchUrl(url, parameters=parameters) data = simplejson.loads(json) self._CheckForTwitterError(data) return [Status.NewFromJsonDict(x) for x in data] def GetFriendsTimeline(self, user=None, count=None, since=None, since_id=None): '''Fetch the sequence of twitter.Status messages for a user's friends The twitter.Api instance must be authenticated if the user is private. Args: user: Specifies the ID or screen name of the user for whom to return the friends_timeline. If unspecified, the username and password must be set in the twitter.Api instance. [Optional] count: Specifies the number of statuses to retrieve. May not be greater than 200. [Optional] since: Narrows the returned results to just those statuses created after the specified HTTP-formatted date. [Optional] since_id: Returns only public statuses with an ID greater than (that is, more recent than) the specified ID. [Optional] Returns: A sequence of twitter.Status instances, one for each message ''' if user: url = 'http://twitter.com/statuses/friends_timeline/%s.json' % user elif not user and not self._username: raise TwitterError("User must be specified if API is not authenticated.") else: url = 'http://twitter.com/statuses/friends_timeline.json' parameters = {} if count is not None: try: if int(count) > 200: raise TwitterError("'count' may not be greater than 200") except ValueError: raise TwitterError("'count' must be an integer") parameters['count'] = count if since: parameters['since'] = since if since_id: parameters['since_id'] = since_id json = self._FetchUrl(url, parameters=parameters) data = simplejson.loads(json) self._CheckForTwitterError(data) return [Status.NewFromJsonDict(x) for x in data] def GetUserTimeline(self, user=None, count=None, since=None, since_id=None): '''Fetch the sequence of public twitter.Status messages for a single user. The twitter.Api instance must be authenticated if the user is private. Args: user: either the username (short_name) or id of the user to retrieve. If not specified, then the current authenticated user is used. [optional] count: the number of status messages to retrieve [optional] since: Narrows the returned results to just those statuses created after the specified HTTP-formatted date. [optional] since_id: Returns only public statuses with an ID greater than (that is, more recent than) the specified ID. [Optional] Returns: A sequence of twitter.Status instances, one for each message up to count ''' try: if count: int(count) except: raise TwitterError("Count must be an integer") parameters = {} if count: parameters['count'] = count if since: parameters['since'] = since if since_id: parameters['since_id'] = since_id if user: url = 'http://twitter.com/statuses/user_timeline/%s.json' % user elif not user and not self._username: raise TwitterError("User must be specified if API is not authenticated.") else: url = 'http://twitter.com/statuses/user_timeline.json' json = self._FetchUrl(url, parameters=parameters) data = simplejson.loads(json) self._CheckForTwitterError(data) return [Status.NewFromJsonDict(x) for x in data] def GetStatus(self, id): '''Returns a single status message. The twitter.Api instance must be authenticated if the status message is private. Args: id: The numerical ID of the status you're trying to retrieve. Returns: A twitter.Status instance representing that status message ''' try: if id: long(id) except: raise TwitterError("id must be an long integer") url = 'http://twitter.com/statuses/show/%s.json' % id json = self._FetchUrl(url) data = simplejson.loads(json) self._CheckForTwitterError(data) return Status.NewFromJsonDict(data) def DestroyStatus(self, id): '''Destroys the status specified by the required ID parameter. The twitter.Api instance must be authenticated and thee authenticating user must be the author of the specified status. Args: id: The numerical ID of the status you're trying to destroy. Returns: A twitter.Status instance representing the destroyed status message ''' try: if id: long(id) except: raise TwitterError("id must be an integer") url = 'http://twitter.com/statuses/destroy/%s.json' % id json = self._FetchUrl(url, post_data={}) data = simplejson.loads(json) self._CheckForTwitterError(data) return Status.NewFromJsonDict(data) def PostUpdate(self, status, in_reply_to_status_id=None): '''Post a twitter status message from the authenticated user. The twitter.Api instance must be authenticated. Args: status: The message text to be posted. Must be less than or equal to 140 characters. in_reply_to_status_id: The ID of an existing status that the status to be posted is in reply to. This implicitly sets the in_reply_to_user_id attribute of the resulting status to the user ID of the message being replied to. Invalid/missing status IDs will be ignored. [Optional] Returns: A twitter.Status instance representing the message posted. ''' if not self._username: raise TwitterError("The twitter.Api instance must be authenticated.") url = 'http://twitter.com/statuses/update.json' if len(status) > CHARACTER_LIMIT: raise TwitterError("Text must be less than or equal to %d characters. " "Consider using PostUpdates." % CHARACTER_LIMIT) data = {'status': status} if in_reply_to_status_id: data['in_reply_to_status_id'] = in_reply_to_status_id json = self._FetchUrl(url, post_data=data) data = simplejson.loads(json) self._CheckForTwitterError(data) return Status.NewFromJsonDict(data) def PostUpdates(self, status, continuation=None, **kwargs): '''Post one or more twitter status messages from the authenticated user. Unlike api.PostUpdate, this method will post multiple status updates if the message is longer than 140 characters. The twitter.Api instance must be authenticated. Args: status: The message text to be posted. May be longer than 140 characters. continuation: The character string, if any, to be appended to all but the last message. Note that Twitter strips trailing '...' strings from messages. Consider using the unicode \u2026 character (horizontal ellipsis) instead. [Defaults to None] **kwargs: See api.PostUpdate for a list of accepted parameters. Returns: A of list twitter.Status instance representing the messages posted. ''' results = list() if continuation is None: continuation = '' line_length = CHARACTER_LIMIT - len(continuation) lines = textwrap.wrap(status, line_length) for line in lines[0:-1]: results.append(self.PostUpdate(line + continuation, **kwargs)) results.append(self.PostUpdate(lines[-1], **kwargs)) return results def GetReplies(self, since=None, since_id=None, page=None): '''Get a sequence of status messages representing the 20 most recent replies (status updates prefixed with @username) to the authenticating user. Args: page: since: Narrows the returned results to just those statuses created after the specified HTTP-formatted date. [optional] since_id: Returns only public statuses with an ID greater than (that is, more recent than) the specified ID. [Optional] Returns: A sequence of twitter.Status instances, one for each reply to the user. ''' url = 'http://twitter.com/statuses/replies.json' if not self._username: raise TwitterError("The twitter.Api instance must be authenticated.") parameters = {} if since: parameters['since'] = since if since_id: parameters['since_id'] = since_id if page: parameters['page'] = page json = self._FetchUrl(url, parameters=parameters) data = simplejson.loads(json) self._CheckForTwitterError(data) return [Status.NewFromJsonDict(x) for x in data] def GetFriends(self, user=None, page=None): '''Fetch the sequence of twitter.User instances, one for each friend. Args: user: the username or id of the user whose friends you are fetching. If not specified, defaults to the authenticated user. [optional] The twitter.Api instance must be authenticated. Returns: A sequence of twitter.User instances, one for each friend ''' if not self._username: raise TwitterError("twitter.Api instance must be authenticated") if user: url = 'http://twitter.com/statuses/friends/%s.json' % user else: url = 'http://twitter.com/statuses/friends.json' parameters = {} if page: parameters['page'] = page json = self._FetchUrl(url, parameters=parameters) data = simplejson.loads(json) self._CheckForTwitterError(data) return [User.NewFromJsonDict(x) for x in data] def GetFollowers(self, page=None): '''Fetch the sequence of twitter.User instances, one for each follower The twitter.Api instance must be authenticated. Returns: A sequence of twitter.User instances, one for each follower ''' if not self._username: raise TwitterError("twitter.Api instance must be authenticated") url = 'http://twitter.com/statuses/followers.json' parameters = {} if page: parameters['page'] = page json = self._FetchUrl(url, parameters=parameters) data = simplejson.loads(json) self._CheckForTwitterError(data) return [User.NewFromJsonDict(x) for x in data] def GetFeatured(self): '''Fetch the sequence of twitter.User instances featured on twitter.com The twitter.Api instance must be authenticated. Returns: A sequence of twitter.User instances ''' url = 'http://twitter.com/statuses/featured.json' json = self._FetchUrl(url) data = simplejson.loads(json) self._CheckForTwitterError(data) return [User.NewFromJsonDict(x) for x in data] def GetUser(self, user): '''Returns a single user. The twitter.Api instance must be authenticated. Args: user: The username or id of the user to retrieve. Returns: A twitter.User instance representing that user ''' url = 'http://twitter.com/users/show/%s.json' % user json = self._FetchUrl(url) data = simplejson.loads(json) self._CheckForTwitterError(data) return User.NewFromJsonDict(data) def GetDirectMessages(self, since=None, since_id=None, page=None): '''Returns a list of the direct messages sent to the authenticating user. The twitter.Api instance must be authenticated. Args: since: Narrows the returned results to just those statuses created after the specified HTTP-formatted date. [optional] since_id: Returns only public statuses with an ID greater than (that is, more recent than) the specified ID. [Optional] Returns: A sequence of twitter.DirectMessage instances ''' url = 'http://twitter.com/direct_messages.json' if not self._username: raise TwitterError("The twitter.Api instance must be authenticated.") parameters = {} if since: parameters['since'] = since if since_id: parameters['since_id'] = since_id if page: parameters['page'] = page json = self._FetchUrl(url, parameters=parameters) data = simplejson.loads(json) self._CheckForTwitterError(data) return [DirectMessage.NewFromJsonDict(x) for x in data] def PostDirectMessage(self, user, text): '''Post a twitter direct message from the authenticated user The twitter.Api instance must be authenticated. Args: user: The ID or screen name of the recipient user. text: The message text to be posted. Must be less than 140 characters. Returns: A twitter.DirectMessage instance representing the message posted ''' if not self._username: raise TwitterError("The twitter.Api instance must be authenticated.") url = 'http://twitter.com/direct_messages/new.json' data = {'text': text, 'user': user} json = self._FetchUrl(url, post_data=data) data = simplejson.loads(json) self._CheckForTwitterError(data) return DirectMessage.NewFromJsonDict(data) def DestroyDirectMessage(self, id): '''Destroys the direct message specified in the required ID parameter. The twitter.Api instance must be authenticated, and the authenticating user must be the recipient of the specified direct message. Args: id: The id of the direct message to be destroyed Returns: A twitter.DirectMessage instance representing the message destroyed ''' url = 'http://twitter.com/direct_messages/destroy/%s.json' % id json = self._FetchUrl(url, post_data={}) data = simplejson.loads(json) self._CheckForTwitterError(data) return DirectMessage.NewFromJsonDict(data) def CreateFriendship(self, user): '''Befriends the user specified in the user parameter as the authenticating user. The twitter.Api instance must be authenticated. Args: The ID or screen name of the user to befriend. Returns: A twitter.User instance representing the befriended user. ''' url = 'http://twitter.com/friendships/create/%s.json' % user json = self._FetchUrl(url, post_data={}) data = simplejson.loads(json) self._CheckForTwitterError(data) return User.NewFromJsonDict(data) def DestroyFriendship(self, user): '''Discontinues friendship with the user specified in the user parameter. The twitter.Api instance must be authenticated. Args: The ID or screen name of the user with whom to discontinue friendship. Returns: A twitter.User instance representing the discontinued friend. ''' url = 'http://twitter.com/friendships/destroy/%s.json' % user json = self._FetchUrl(url, post_data={}) data = simplejson.loads(json) self._CheckForTwitterError(data) return User.NewFromJsonDict(data) def CreateFavorite(self, status): '''Favorites the status specified in the status parameter as the authenticating user. Returns the favorite status when successful. The twitter.Api instance must be authenticated. Args: The twitter.Status instance to mark as a favorite. Returns: A twitter.Status instance representing the newly-marked favorite. ''' url = 'http://twitter.com/favorites/create/%s.json' % status.id json = self._FetchUrl(url, post_data={}) data = simplejson.loads(json) self._CheckForTwitterError(data) return Status.NewFromJsonDict(data) def DestroyFavorite(self, status): '''Un-favorites the status specified in the ID parameter as the authenticating user. Returns the un-favorited status in the requested format when successful. The twitter.Api instance must be authenticated. Args: The twitter.Status to unmark as a favorite. Returns: A twitter.Status instance representing the newly-unmarked favorite. ''' url = 'http://twitter.com/favorites/destroy/%s.json' % status.id json = self._FetchUrl(url, post_data={}) data = simplejson.loads(json) self._CheckForTwitterError(data) return Status.NewFromJsonDict(data) def GetUserByEmail(self, email): '''Returns a single user by email address. Args: email: The email of the user to retrieve. Returns: A twitter.User instance representing that user ''' url = 'http://twitter.com/users/show.json?email=%s' % email json = self._FetchUrl(url) data = simplejson.loads(json) self._CheckForTwitterError(data) return User.NewFromJsonDict(data) def SetCredentials(self, username, password): '''Set the username and password for this instance Args: username: The twitter username. password: The twitter password. ''' self._username = username self._password = password def ClearCredentials(self): '''Clear the username and password for this instance ''' self._username = None self._password = None def SetCache(self, cache): '''Override the default cache. Set to None to prevent caching. Args: cache: an instance that supports the same API as the twitter._FileCache ''' self._cache = cache def SetUrllib(self, urllib): '''Override the default urllib implementation. Args: urllib: an instance that supports the same API as the urllib2 module ''' self._urllib = urllib def SetCacheTimeout(self, cache_timeout): '''Override the default cache timeout. Args: cache_timeout: time, in seconds, that responses should be reused. ''' self._cache_timeout = cache_timeout def SetUserAgent(self, user_agent): '''Override the default user agent Args: user_agent: a string that should be send to the server as the User-agent ''' self._request_headers['User-Agent'] = user_agent def SetXTwitterHeaders(self, client, url, version): '''Set the X-Twitter HTTP headers that will be sent to the server. Args: client: The client name as a string. Will be sent to the server as the 'X-Twitter-Client' header. url: The URL of the meta.xml as a string. Will be sent to the server as the 'X-Twitter-Client-URL' header. version: The client version as a string. Will be sent to the server as the 'X-Twitter-Client-Version' header. ''' self._request_headers['X-Twitter-Client'] = client self._request_headers['X-Twitter-Client-URL'] = url self._request_headers['X-Twitter-Client-Version'] = version def SetSource(self, source): '''Suggest the "from source" value to be displayed on the Twitter web site. The value of the 'source' parameter must be first recognized by the Twitter server. New source values are authorized on a case by case basis by the Twitter development team. Args: source: The source name as a string. Will be sent to the server as the 'source' parameter. ''' self._default_params['source'] = source def _BuildUrl(self, url, path_elements=None, extra_params=None): # Break url into consituent parts (scheme, netloc, path, params, query, fragment) = urlparse.urlparse(url) # Add any additional path elements to the path if path_elements: # Filter out the path elements that have a value of None p = [i for i in path_elements if i] if not path.endswith('/'): path += '/' path += '/'.join(p) # Add any additional query parameters to the query string if extra_params and len(extra_params) > 0: extra_query = self._EncodeParameters(extra_params) # Add it to the existing query if query: query += '&' + extra_query else: query = extra_query # Return the rebuilt URL return urlparse.urlunparse((scheme, netloc, path, params, query, fragment)) def _InitializeRequestHeaders(self, request_headers): if request_headers: self._request_headers = request_headers else: self._request_headers = {} def _InitializeUserAgent(self): user_agent = 'Python-urllib/%s (python-twitter/%s)' % \ (self._urllib.__version__, __version__) self.SetUserAgent(user_agent) def _InitializeDefaultParameters(self): self._default_params = {} def _AddAuthorizationHeader(self, username, password): if username and password: basic_auth = base64.encodestring('%s:%s' % (username, password))[:-1] self._request_headers['Authorization'] = 'Basic %s' % basic_auth def _RemoveAuthorizationHeader(self): if self._request_headers and 'Authorization' in self._request_headers: del self._request_headers['Authorization'] def _GetOpener(self, url, username=None, password=None): if username and password: self._AddAuthorizationHeader(username, password) handler = self._urllib.HTTPBasicAuthHandler() (scheme, netloc, path, params, query, fragment) = urlparse.urlparse(url) handler.add_password(Api._API_REALM, netloc, username, password) opener = self._urllib.build_opener(handler) else: opener = self._urllib.build_opener() opener.addheaders = self._request_headers.items() return opener def _Encode(self, s): if self._input_encoding: return unicode(s, self._input_encoding).encode('utf-8') else: return unicode(s).encode('utf-8') def _EncodeParameters(self, parameters): '''Return a string in key=value&key=value form Values of None are not included in the output string. Args: parameters: A dict of (key, value) tuples, where value is encoded as specified by self._encoding Returns: A URL-encoded string in "key=value&key=value" form ''' if parameters is None: return None else: return urllib.urlencode(dict([(k, self._Encode(v)) for k, v in parameters.items() if v is not None])) def _EncodePostData(self, post_data): '''Return a string in key=value&key=value form Values are assumed to be encoded in the format specified by self._encoding, and are subsequently URL encoded. Args: post_data: A dict of (key, value) tuples, where value is encoded as specified by self._encoding Returns: A URL-encoded string in "key=value&key=value" form ''' if post_data is None: return None else: return urllib.urlencode(dict([(k, self._Encode(v)) for k, v in post_data.items()])) def _CheckForTwitterError(self, data): """Raises a TwitterError if twitter returns an error message. Args: data: A python dict created from the Twitter json response Raises: TwitterError wrapping the twitter error message if one exists. """ # Twitter errors are relatively unlikely, so it is faster # to check first, rather than try and catch the exception if 'error' in data: raise TwitterError(data['error']) def _FetchUrl(self, url, post_data=None, parameters=None, no_cache=None): '''Fetch a URL, optionally caching for a specified time. Args: url: The URL to retrieve post_data: A dict of (str, unicode) key/value pairs. If set, POST will be used. parameters: A dict whose key/value pairs should encoded and added to the query string. [OPTIONAL] no_cache: If true, overrides the cache on the current request Returns: A string containing the body of the response. ''' # Build the extra parameters dict extra_params = {} if self._default_params: extra_params.update(self._default_params) if parameters: extra_params.update(parameters) # Add key/value parameters to the query string of the url url = self._BuildUrl(url, extra_params=extra_params) # Get a url opener that can handle basic auth opener = self._GetOpener(url, username=self._username, password=self._password) encoded_post_data = self._EncodePostData(post_data) # Open and return the URL immediately if we're not going to cache if encoded_post_data or no_cache or not self._cache or not self._cache_timeout: url_data = opener.open(url, encoded_post_data).read() opener.close() else: # Unique keys are a combination of the url and the username if self._username: key = self._username + ':' + url else: key = url # See if it has been cached before last_cached = self._cache.GetCachedTime(key) # If the cached version is outdated then fetch another and store it if not last_cached or time.time() >= last_cached + self._cache_timeout: url_data = opener.open(url, encoded_post_data).read() opener.close() self._cache.Set(key, url_data) else: url_data = self._cache.Get(key) # Always return the latest version return url_data class _FileCacheError(Exception): '''Base exception class for FileCache related errors''' class _FileCache(object): DEPTH = 3 def __init__(self,root_directory=None): self._InitializeRootDirectory(root_directory) def Get(self,key): path = self._GetPath(key) if os.path.exists(path): return open(path).read() else: return None def Set(self,key,data): path = self._GetPath(key) directory = os.path.dirname(path) if not os.path.exists(directory): os.makedirs(directory) if not os.path.isdir(directory): raise _FileCacheError('%s exists but is not a directory' % directory) temp_fd, temp_path = tempfile.mkstemp() temp_fp = os.fdopen(temp_fd, 'w') temp_fp.write(data) temp_fp.close() if not path.startswith(self._root_directory): raise _FileCacheError('%s does not appear to live under %s' % (path, self._root_directory)) if os.path.exists(path): os.remove(path) os.rename(temp_path, path) def Remove(self,key): path = self._GetPath(key) if not path.startswith(self._root_directory): raise _FileCacheError('%s does not appear to live under %s' % (path, self._root_directory )) if os.path.exists(path): os.remove(path) def GetCachedTime(self,key): path = self._GetPath(key) if os.path.exists(path): return os.path.getmtime(path) else: return None def _GetUsername(self): '''Attempt to find the username in a cross-platform fashion.''' try: return os.getenv('USER') or \ os.getenv('LOGNAME') or \ os.getenv('USERNAME') or \ os.getlogin() or \ 'nobody' except (IOError, OSError), e: return 'nobody' def _GetTmpCachePath(self): username = self._GetUsername() cache_directory = 'python.cache_' + username return os.path.join(tempfile.gettempdir(), cache_directory) def _InitializeRootDirectory(self, root_directory): if not root_directory: root_directory = self._GetTmpCachePath() root_directory = os.path.abspath(root_directory) if not os.path.exists(root_directory): os.mkdir(root_directory) if not os.path.isdir(root_directory): raise _FileCacheError('%s exists but is not a directory' % root_directory) self._root_directory = root_directory def _GetPath(self,key): try: hashed_key = md5(key).hexdigest() except TypeError: hashed_key = md5.new(key).hexdigest() return os.path.join(self._root_directory, self._GetPrefix(hashed_key), hashed_key) def _GetPrefix(self,hashed_key): return os.path.sep.join(hashed_key[0:_FileCache.DEPTH]) ================================================ FILE: socialauth/models.py ================================================ from django.db import models from django.contrib.auth.models import User class AuthMeta(models.Model): """Metadata for Authentication""" def __unicode__(self): return '%s - %s' % (self.user, self.provider) user = models.ForeignKey(User) provider = models.CharField(max_length=200) is_email_filled = models.BooleanField(default=False) is_profile_modified = models.BooleanField(default=False) class OpenidProfile(models.Model): """A class associating an User to a Openid""" openid_key = models.CharField(max_length=200, unique=True, db_index=True) user = models.ForeignKey(User, related_name='openid_profiles') is_username_valid = models.BooleanField(default=False) #Values which we get from openid.sreg email = models.EmailField() nickname = models.CharField(max_length=100) def __unicode__(self): return unicode(self.openid_key) def __repr__(self): return unicode(self.openid_key) class LinkedInUserProfile(models.Model): """ For users who login via Linkedin. """ linkedin_uid = models.CharField(max_length=50, unique=True, db_index=True) user = models.ForeignKey(User, related_name='linkedin_profiles') headline = models.CharField(max_length=120, blank=True, null=True) company = models.CharField(max_length=255, blank=True, null=True) location = models.TextField(blank=True, null=True) industry = models.CharField(max_length=255, blank=True, null=True) profile_image_url = models.URLField(blank=True, null=True) url = models.URLField(blank=True, null=True) access_token = models.CharField(max_length=255, blank=True, null=True, editable=False) def __unicode__(self): return "%s's profile" % self.user class TwitterUserProfile(models.Model): """ For users who login via Twitter. """ screen_name = models.CharField(max_length=200, unique=True, db_index=True) user = models.ForeignKey(User, related_name='twitter_profiles') access_token = models.CharField(max_length=255, blank=True, null=True, editable=False) profile_image_url = models.URLField(blank=True, null=True) location = models.TextField(blank=True, null=True) url = models.URLField(blank=True, null=True) description = models.CharField(max_length=160, blank=True, null=True) def __unicode__(self): return "%s's profile" % self.user class FacebookUserProfile(models.Model): """ For users who login via Facebook. """ facebook_uid = models.CharField(max_length=20, unique=True, db_index=True) user = models.ForeignKey(User, related_name='facebook_profiles') profile_image_url = models.URLField(blank=True, null=True) profile_image_url_big = models.URLField(blank=True, null=True) profile_image_url_small = models.URLField(blank=True, null=True) location = models.TextField(blank=True, null=True) url = models.URLField(blank=True, null=True) about_me = models.CharField(max_length=160, blank=True, null=True) def __unicode__(self): return "%s's profile" % self.user class GithubUserProfile(models.Model): user = models.ForeignKey(User) access_token = models.CharField(max_length=100, blank=True, null=True, editable=False) def __unicode__(self): return "%s's profile" % self.user class FoursquareUserProfile(models.Model): user = models.ForeignKey(User) access_token = models.CharField(max_length=255, blank=True, null=True, editable=False) def __unicode__(self): return "%s's profile" % self.user ================================================ FILE: socialauth/templates/openid/index.html ================================================ JQuery Simple OpenID Selector Demo

JQuery Simple OpenID Selector Demo

This is a simple example to show how you can include the Javascript into your page.


Sign-in or Create New Account

Please click your account provider:

================================================ FILE: socialauth/templates/socialauth/base.html ================================================ {% block html_declare %} {% endblock %} {% if user.is_authenticated %}Hello {{ user }}{% else %}Please login{% endif %} {% block extra_head %} {% endblock %}

social-auth

{% if FACEBOOK_APP_ID %}
{% endif %} {% block extra_body %} {% endblock %} {% block main_content %} {% endblock %} {% block content %} {% endblock %} ================================================ FILE: socialauth/templates/socialauth/editprofile.html ================================================ {% extends 'socialauth/socialauth_base.html' %} {% block content %}
{{ edit_form }}
{% endblock %} ================================================ FILE: socialauth/templates/socialauth/login_page.html ================================================ {% extends 'socialauth/base.html' %} {% block html_declare %} {% endblock %} {% block extra_head %} {% endblock %} {% block extra_body %} {{ block.super }} {% endblock %} {% block main_content %} {% if user.is_authenticated %}

You are already logged in. Logout?

{% else %}
Sign-in or Create New Account via these openid providers

Please click your account provider:

{% if FACEBOOK_APP_ID %}

{% endif %}



{% endif %} {% endblock %} ================================================ FILE: socialauth/templates/socialauth/signin_complete.html ================================================ {% extends 'socialauth/base.html' %} {% block main_content %} You have signed in.
Logout {% endblock %} ================================================ FILE: socialauth/templates/socialauth/socialauth_base.html ================================================ {% extends "socialauth/base.html" %} {% block head %} {% block html_declare %} {% endblock %} {% endblock %} {% block content %} {% if user.is_authenticated %}Hello {{ user }}{% else %}Please login{% endif %} {% block extra_head %} {% endblock %} {% block extra_body %} {% endblock %} {% block main_content %} {% endblock %} {% endblock %} ================================================ FILE: socialauth/templates/socialauth/xd_receiver.htm ================================================ ================================================ FILE: socialauth/templatetags/__init__.py ================================================ ================================================ FILE: socialauth/templatetags/socialauth_tags.py ================================================ from django import template register = template.Library() @register.simple_tag def get_calculated_username(user): if hasattr(user, 'openidprofile_set') and user.openidprofile_set.filter().count(): if user.openidprofile_set.filter(is_username_valid = True).count(): return user.openidprofile_set.filter(is_username_valid = True)[0].user.username else: from django.core.urlresolvers import reverse editprof_url = reverse('socialauth_editprofile') return u'Anonymous User. Add name'%editprof_url else: return user.username ================================================ FILE: socialauth/test_data.py.example ================================================ test_base_url = "http://nothing.uswaretech.net:8000/" twitter_username = "socialauthtest2" twitter_password = "" myopenid_url = "http://socialauthtest2.myopenid.com/" myopenid_password = "" ================================================ FILE: socialauth/tests.py ================================================ from selenium import selenium import unittest from django.contrib.auth.models import User from test_data import * class TwitterTester(unittest.TestCase): def setUp(self): self.verificationErrors = [] self.selenium = selenium("localhost", 4444, "*chrome", test_base_url) self.selenium.start() def testTwitter(self): sel = self.selenium #Test that a user is created after # logging in via Twitter for the first time. initial_user_count = User.objects.count() sel.open("/accounts/login/") sel.click("link=Login via twitter") sel.wait_for_page_to_load("30000") try: sel.click("link=Sign out") sel.wait_for_page_to_load("30000") except: pass sel.type("username_or_email", twitter_username) sel.type("session[password]", twitter_password) sel.click("allow") sel.wait_for_page_to_load("30000") sel.open("/accounts/login/") sel.open("/accounts/edit/profile/") self.assertEqual(initial_user_count + 1, User.objects.count()) def tearDown(self): self.selenium.stop() self.assertEqual([], self.verificationErrors) class OpenIdTester(unittest.TestCase): def setUp(self): self.verificationErrors = [] self.selenium = selenium("localhost", 4444, "*chrome", test_base_url) self.selenium.start() def testOpenId(self): initial_user_count = User.objects.count() sel = self.selenium sel.open("/accounts/login/") sel.click("openid_login_link") sel.wait_for_page_to_load("30000") sel.type("openid_url", myopenid_url) sel.click("//input[@value='Sign in']") sel.wait_for_page_to_load("30000") try: sel.type("password", myopenid_password) sel.click("signin_button") sel.wait_for_page_to_load("30000") except: sel.click("continue-button") sel.wait_for_page_to_load("30000") sel.open("/accounts/login/") self.assertEqual(initial_user_count + 1, User.objects.count()) def tearDown(self): self.selenium.stop() self.assertEqual([], self.verificationErrors) if __name__ == "__main__": unittest.main() if __name__ == "__main__": unittest.main() ================================================ FILE: socialauth/urls.py ================================================ from django.conf.urls.defaults import * from openid_consumer.views import complete, signout from django.views.generic.base import TemplateView #Login Views urlpatterns = patterns('socialauth.views', url(r'^facebook_login/xd_receiver.htm$', TemplateView.as_view(template_name='socialauth/xd_receiver.htm'), name='socialauth_xd_receiver'), url(r'^facebook_login/$', 'facebook_login', name='socialauth_facebook_login'), url(r'^facebook_login/done/$', 'facebook_login_done', name='socialauth_facebook_login_done'), url(r'^login/$', 'login_page', name='socialauth_login_page'), url(r'^openid_login/$', 'openid_login_page', name='socialauth_openid_login_page'), url(r'^twitter_login/$', 'twitter_login', name='socialauth_twitter_login'), url(r'^twitter_login/done/$', 'twitter_login_done', name='socialauth_twitter_login_done'), url(r'^linkedin_login/$', 'linkedin_login', name='socialauth_linkedin_login'), url(r'^linkedin_login/done/$', 'linkedin_login_done', name='socialauth_linkedin_login_done'), url(r'^yahoo_login/$', 'yahoo_login', name='socialauth_yahoo_login'), url(r'^yahoo_login/complete/$', complete, name='socialauth_yahoo_complete'), url(r'^gmail_login/$', 'gmail_login', name='socialauth_google_login'), url(r'^gmail_login/complete/$', complete, name='socialauth_google_complete'), url(r'^openid/$', 'openid_login', name='socialauth_openid_login'), url(r'^openid/complete/$', complete, name='socialauth_openid_complete'), url(r'^openid/signout/$', signout, name='openid_signout'), url(r'^openid/done/$', 'openid_done', name='openid_openid_done'), url(r'^github_login/$', 'github_login', name='github_login'), url(r'github_login/done/$', 'github_login_done', name='github_login_done'), url(r'^foursquare_login/$', 'foursquare_login', name='foursquare_login'), url(r'^foursquare_login/done/$', 'foursquare_login_done', name='foursquare_login_done'), ) #Other views. urlpatterns += patterns('socialauth.views', url(r'^$', 'login_page', name='socialauth_index'), url(r'^done/$', 'signin_complete', name='socialauth_signin_complete'), url(r'^edit/profile/$', 'editprofile', name='socialauth_editprofile'), url(r'^logout/$', 'social_logout', name='socialauth_social_logout'), ) ================================================ FILE: socialauth/views.py ================================================ import logging import urllib from oauth import oauth from django.shortcuts import render_to_response from django.template import RequestContext from django.contrib.auth import authenticate, login from django.http import HttpResponseRedirect from django.core.urlresolvers import reverse from django.conf import settings from django.contrib.auth.decorators import login_required from django.contrib.auth.views import logout from socialauth.models import AuthMeta from socialauth.forms import EditProfileForm from openid_consumer.views import begin from socialauth.lib import oauthtwitter2 as oauthtwitter from socialauth.lib.linkedin import * from socialauth.lib.github import GithubClient from socialauth.lib import foursquare LINKEDIN_CONSUMER_KEY = getattr(settings, 'LINKEDIN_CONSUMER_KEY', '') LINKEDIN_CONSUMER_SECRET = getattr(settings, 'LINKEDIN_CONSUMER_SECRET', '') ADD_LOGIN_REDIRECT_URL = getattr(settings, 'ADD_LOGIN_REDIRECT_URL', '') LOGIN_REDIRECT_URL = getattr(settings, 'LOGIN_REDIRECT_URL', '') LOGIN_URL = getattr(settings, 'LOGIN_URL', '') TWITTER_CONSUMER_KEY = getattr(settings, 'TWITTER_CONSUMER_KEY', '') TWITTER_CONSUMER_SECRET = getattr(settings, 'TWITTER_CONSUMER_SECRET', '') FACEBOOK_APP_ID = getattr(settings, 'FACEBOOK_APP_ID', '') FACEBOOK_API_KEY = getattr(settings, 'FACEBOOK_API_KEY', '') FACEBOOK_SECRET_KEY = getattr(settings, 'FACEBOOK_SECRET_KEY', '') def del_dict_key(src_dict, key): if key in src_dict: del src_dict[key] def login_page(request): return render_to_response('socialauth/login_page.html', {'next': request.GET.get('next', LOGIN_REDIRECT_URL)}, context_instance=RequestContext(request)) def linkedin_login(request): linkedin = LinkedIn(LINKEDIN_CONSUMER_KEY, LINKEDIN_CONSUMER_SECRET) request_token = linkedin.getRequestToken(callback=request.build_absolute_uri(reverse('socialauth_linkedin_login_done'))) request.session['linkedin_request_token'] = request_token signin_url = linkedin.getAuthorizeUrl(request_token) return HttpResponseRedirect(signin_url) def linkedin_login_done(request): request_token = request.session.get('linkedin_request_token', None) # If there is no request_token for session # Means we didn't redirect user to linkedin if not request_token: # Send them to the login page return HttpResponseRedirect(reverse("socialauth_login_page")) try: linkedin = LinkedIn(settings.LINKEDIN_CONSUMER_KEY, settings.LINKEDIN_CONSUMER_SECRET) verifier = request.GET.get('oauth_verifier', None) access_token = linkedin.getAccessToken(request_token,verifier) request.session['access_token'] = access_token user = authenticate(linkedin_access_token=access_token) except: user = None # if user is authenticated then login user if user: login(request, user) else: # We were not able to authenticate user # Redirect to login page del_dict_key(request.session, 'access_token') del_dict_key(request.session, 'request_token') return HttpResponseRedirect(reverse('socialauth_login_page')) # authentication was successful, user is now logged in return HttpResponseRedirect(LOGIN_REDIRECT_URL) def twitter_login(request): next = request.GET.get('next', None) if next: request.session['twitter_login_next'] = next twitter = oauthtwitter.TwitterOAuthClient(settings.TWITTER_CONSUMER_KEY, settings.TWITTER_CONSUMER_SECRET) request_token = twitter.fetch_request_token(callback=request.build_absolute_uri(reverse('socialauth_twitter_login_done'))) request.session['request_token'] = request_token.to_string() signin_url = twitter.authorize_token_url(request_token) return HttpResponseRedirect(signin_url) def twitter_login_done(request): request_token = request.session.get('request_token', None) verifier = request.GET.get('oauth_verifier', None) denied = request.GET.get('denied', None) # If we've been denied, put them back to the signin page # They probably meant to sign in with facebook >:D if denied: return HttpResponseRedirect(reverse("socialauth_login_page")) # If there is no request_token for session, # Means we didn't redirect user to twitter if not request_token: # Redirect the user to the login page, return HttpResponseRedirect(reverse("socialauth_login_page")) token = oauth.OAuthToken.from_string(request_token) # If the token from session and token from twitter does not match # means something bad happened to tokens if token.key != request.GET.get('oauth_token', 'no-token'): del_dict_key(request.session, 'request_token') # Redirect the user to the login page return HttpResponseRedirect(reverse("socialauth_login_page")) try: twitter = oauthtwitter.TwitterOAuthClient(settings.TWITTER_CONSUMER_KEY, settings.TWITTER_CONSUMER_SECRET) access_token = twitter.fetch_access_token(token, verifier) request.session['access_token'] = access_token.to_string() user = authenticate(twitter_access_token=access_token) except: user = None # if user is authenticated then login user if user: login(request, user) else: # We were not able to authenticate user # Redirect to login page del_dict_key(request.session, 'access_token') del_dict_key(request.session, 'request_token') return HttpResponseRedirect(reverse('socialauth_login_page')) # authentication was successful, use is now logged in next = request.session.get('twitter_login_next', None) if next: del_dict_key(request.session, 'twitter_login_next') return HttpResponseRedirect(next) else: return HttpResponseRedirect(LOGIN_REDIRECT_URL) def openid_login(request): if 'openid_next' in request.GET: request.session['openid_next'] = request.GET.get('openid_next') if 'openid_identifier' in request.GET: user_url = request.GET.get('openid_identifier') request.session['openid_provider'] = user_url return begin(request, user_url=user_url) else: request.session['openid_provider'] = 'Openid' return begin(request) def gmail_login(request): request.session['openid_provider'] = 'Google' return begin(request, user_url='https://www.google.com/accounts/o8/id') def gmail_login_complete(request): pass def yahoo_login(request): request.session['openid_provider'] = 'Yahoo' return begin(request, user_url='https://me.yahoo.com/') def openid_done(request, provider=None): """ When the request reaches here, the user has completed the Openid authentication flow. He has authorised us to login via Openid, so request.openid is populated. After coming here, we want to check if we are seeing this openid first time. If we are, we will create a new Django user for this Openid, else login the existing openid. """ if not provider: provider = request.session.get('openid_provider', '') if hasattr(request,'openid') and request.openid: #check for already existing associations openid_key = str(request.openid) #authenticate and login try: user = authenticate(openid_key=openid_key, request=request, provider=provider) except: user = None if user: login(request, user) if 'openid_next' in request.session : openid_next = request.session.get('openid_next') if len(openid_next.strip()) > 0 : return HttpResponseRedirect(openid_next) return HttpResponseRedirect(LOGIN_REDIRECT_URL) # redirect_url = reverse('socialauth_editprofile') # return HttpResponseRedirect(redirect_url) else: return HttpResponseRedirect(LOGIN_URL) else: return HttpResponseRedirect(LOGIN_URL) def facebook_login(request): """ Facebook login page """ next = request.GET.get('next', None) if next: request.session['facebook_login_next'] = next if request.REQUEST.get("device"): device = request.REQUEST.get("device") else: device = "user-agent" params = {} params["client_id"] = FACEBOOK_APP_ID params["redirect_uri"] = request.build_absolute_uri(reverse("socialauth_facebook_login_done")) url = "https://graph.facebook.com/oauth/authorize?"+urllib.urlencode(params) return HttpResponseRedirect(url) def facebook_login_done(request): user = authenticate(request=request) if not user: request.COOKIES.pop(FACEBOOK_API_KEY + '_session_key', None) request.COOKIES.pop(FACEBOOK_API_KEY + '_user', None) # TODO: maybe the project has its own login page? logging.debug("SOCIALAUTH: Couldn't authenticate user with Django, redirecting to Login page") return HttpResponseRedirect(reverse('socialauth_login_page')) login(request, user) logging.debug("SOCIALAUTH: Successfully logged in with Facebook!") next = request.GET.get('next') if not next: next = request.session.get('facebook_login_next') del_dict_key(request.session, 'facebook_login_next') if next: return HttpResponseRedirect(next) else: return HttpResponseRedirect(LOGIN_REDIRECT_URL) def openid_login_page(request): return render_to_response('openid/index.html', context_instance=RequestContext(request)) @login_required def signin_complete(request): return render_to_response('socialauth/signin_complete.html', context_instance=RequestContext(request)) @login_required def editprofile(request): if request.method == 'POST': edit_form = EditProfileForm(user=request.user, data=request.POST) if edit_form.is_valid(): user = edit_form.save() try: user.authmeta.is_profile_modified = True user.authmeta.save() except AuthMeta.DoesNotExist: pass if hasattr(user,'openidprofile_set') and user.openidprofile_set.count(): openid_profile = user.openidprofile_set.all()[0] openid_profile.is_valid_username = True openid_profile.save() try: #If there is a profile. notify that we have set the username profile = user.get_profile() profile.is_valid_username = True profile.save() except: pass request.user.message_set.create(message='Your profile has been updated.') return HttpResponseRedirect('.') if request.method == 'GET': edit_form = EditProfileForm(user=request.user) payload = {'edit_form':edit_form} return render_to_response('socialauth/editprofile.html', payload, RequestContext(request)) def social_logout(request): # Todo # still need to handle FB cookies, session etc. # let the openid_consumer app handle openid-related cleanup from openid_consumer.views import signout as oid_signout oid_signout(request) # normal logout logout_response = logout(request) if 'next' in request.GET: response = HttpResponseRedirect(request.GET.get('next')) elif getattr(settings, 'LOGOUT_REDIRECT_URL', None): response = HttpResponseRedirect(settings.LOGOUT_REDIRECT_URL) else: response = logout_response # Delete the facebook cookie response.delete_cookie("fbs_" + FACEBOOK_APP_ID) return response def github_login(request): github_client = GithubClient() authorize_url = github_client.get_authorize_url() return HttpResponseRedirect(authorize_url) def github_login_done(request): try: code = request.GET['code'] except: """Either github did not respond properly or someone is playing with this url""" return HttpResponseRedirect(LOGIN_URL) github_client = GithubClient() access_token = github_client.get_access_token(code) try: user = authenticate(github_access_token=access_token) except: user = None if user: login(request, user) return HttpResponseRedirect(LOGIN_REDIRECT_URL) return HttpResponseRedirect(LOGIN_URL) def foursquare_login(request): foursquare_client = foursquare.FourSquareClient() return HttpResponseRedirect(foursquare_client.get_authentication_url()) def foursquare_login_done(request): try: code = request.GET.get('code') except: """Some error ocurred. Rediect to login page""" return HttpResponseRedirect(LOGIN_URL) request.session['foursquare_code'] = code foursquare_client = foursquare.FourSquareClient() access_token_response = foursquare_client.get_access_token(request.session['foursquare_code']) import json access_token = json.loads(access_token_response)['access_token'] try: user = authenticate(foursquare_access_token=access_token) except: user=None if user: login(request, user) return HttpResponseRedirect(LOGIN_REDIRECT_URL) return HttpResponseRedirect(LOGIN_URL)