-
-
-
-
diff --git a/docs/epy/crarr.png b/docs/epy/crarr.png
deleted file mode 100644
index 26b43c5..0000000
Binary files a/docs/epy/crarr.png and /dev/null differ
diff --git a/docs/epy/epydoc.css b/docs/epy/epydoc.css
deleted file mode 100644
index 86d4170..0000000
--- a/docs/epy/epydoc.css
+++ /dev/null
@@ -1,322 +0,0 @@
-
-
-/* Epydoc CSS Stylesheet
- *
- * This stylesheet can be used to customize the appearance of epydoc's
- * HTML output.
- *
- */
-
-/* Default Colors & Styles
- * - Set the default foreground & background color with 'body'; and
- * link colors with 'a:link' and 'a:visited'.
- * - Use bold for decision list terms.
- * - The heading styles defined here are used for headings *within*
- * docstring descriptions. All headings used by epydoc itself use
- * either class='epydoc' or class='toc' (CSS styles for both
- * defined below).
- */
-body { background: #ffffff; color: #000000; }
-p { margin-top: 0.5em; margin-bottom: 0.5em; }
-a:link { color: #0000ff; }
-a:visited { color: #204080; }
-dt { font-weight: bold; }
-h1 { font-size: +140%; font-style: italic;
- font-weight: bold; }
-h2 { font-size: +125%; font-style: italic;
- font-weight: bold; }
-h3 { font-size: +110%; font-style: italic;
- font-weight: normal; }
-code { font-size: 100%; }
-/* N.B.: class, not pseudoclass */
-a.link { font-family: monospace; }
-
-/* Page Header & Footer
- * - The standard page header consists of a navigation bar (with
- * pointers to standard pages such as 'home' and 'trees'); a
- * breadcrumbs list, which can be used to navigate to containing
- * classes or modules; options links, to show/hide private
- * variables and to show/hide frames; and a page title (using
- *
). The page title may be followed by a link to the
- * corresponding source code (using 'span.codelink').
- * - The footer consists of a navigation bar, a timestamp, and a
- * pointer to epydoc's homepage.
- */
-h1.epydoc { margin: 0; font-size: +140%; font-weight: bold; }
-h2.epydoc { font-size: +130%; font-weight: bold; }
-h3.epydoc { font-size: +115%; font-weight: bold;
- margin-top: 0.2em; }
-td h3.epydoc { font-size: +115%; font-weight: bold;
- margin-bottom: 0; }
-table.navbar { background: #a0c0ff; color: #000000;
- border: 2px groove #c0d0d0; }
-table.navbar table { color: #000000; }
-th.navbar-select { background: #70b0ff;
- color: #000000; }
-table.navbar a { text-decoration: none; }
-table.navbar a:link { color: #0000ff; }
-table.navbar a:visited { color: #204080; }
-span.breadcrumbs { font-size: 85%; font-weight: bold; }
-span.options { font-size: 70%; }
-span.codelink { font-size: 85%; }
-td.footer { font-size: 85%; }
-
-/* Table Headers
- * - Each summary table and details section begins with a 'header'
- * row. This row contains a section title (marked by
- * 'span.table-header') as well as a show/hide private link
- * (marked by 'span.options', defined above).
- * - Summary tables that contain user-defined groups mark those
- * groups using 'group header' rows.
- */
-td.table-header { background: #70b0ff; color: #000000;
- border: 1px solid #608090; }
-td.table-header table { color: #000000; }
-td.table-header table a:link { color: #0000ff; }
-td.table-header table a:visited { color: #204080; }
-span.table-header { font-size: 120%; font-weight: bold; }
-th.group-header { background: #c0e0f8; color: #000000;
- text-align: left; font-style: italic;
- font-size: 115%;
- border: 1px solid #608090; }
-
-/* Summary Tables (functions, variables, etc)
- * - Each object is described by a single row of the table with
- * two cells. The left cell gives the object's type, and is
- * marked with 'code.summary-type'. The right cell gives the
- * object's name and a summary description.
- * - CSS styles for the table's header and group headers are
- * defined above, under 'Table Headers'
- */
-table.summary { border-collapse: collapse;
- background: #e8f0f8; color: #000000;
- border: 1px solid #608090;
- margin-bottom: 0.5em; }
-td.summary { border: 1px solid #608090; }
-code.summary-type { font-size: 85%; }
-table.summary a:link { color: #0000ff; }
-table.summary a:visited { color: #204080; }
-
-
-/* Details Tables (functions, variables, etc)
- * - Each object is described in its own div.
- * - A single-row summary table w/ table-header is used as
- * a header for each details section (CSS style for table-header
- * is defined above, under 'Table Headers').
- */
-table.details { border-collapse: collapse;
- background: #e8f0f8; color: #000000;
- border: 1px solid #608090;
- margin: .2em 0 0 0; }
-table.details table { color: #000000; }
-table.details a:link { color: #0000ff; }
-table.details a:visited { color: #204080; }
-
-/* Fields */
-dl.fields { margin-left: 2em; margin-top: 1em;
- margin-bottom: 1em; }
-dl.fields dd ul { margin-left: 0em; padding-left: 0em; }
-dl.fields dd ul li ul { margin-left: 2em; padding-left: 0em; }
-div.fields { margin-left: 2em; }
-div.fields p { margin-bottom: 0.5em; }
-
-/* Index tables (identifier index, term index, etc)
- * - link-index is used for indices containing lists of links
- * (namely, the identifier index & term index).
- * - index-where is used in link indices for the text indicating
- * the container/source for each link.
- * - metadata-index is used for indices containing metadata
- * extracted from fields (namely, the bug index & todo index).
- */
-table.link-index { border-collapse: collapse;
- background: #e8f0f8; color: #000000;
- border: 1px solid #608090; }
-td.link-index { border-width: 0px; }
-table.link-index a:link { color: #0000ff; }
-table.link-index a:visited { color: #204080; }
-span.index-where { font-size: 70%; }
-table.metadata-index { border-collapse: collapse;
- background: #e8f0f8; color: #000000;
- border: 1px solid #608090;
- margin: .2em 0 0 0; }
-td.metadata-index { border-width: 1px; border-style: solid; }
-table.metadata-index a:link { color: #0000ff; }
-table.metadata-index a:visited { color: #204080; }
-
-/* Function signatures
- * - sig* is used for the signature in the details section.
- * - .summary-sig* is used for the signature in the summary
- * table, and when listing property accessor functions.
- * */
-.sig-name { color: #006080; }
-.sig-arg { color: #008060; }
-.sig-default { color: #602000; }
-.summary-sig { font-family: monospace; }
-.summary-sig-name { color: #006080; font-weight: bold; }
-table.summary a.summary-sig-name:link
- { color: #006080; font-weight: bold; }
-table.summary a.summary-sig-name:visited
- { color: #006080; font-weight: bold; }
-.summary-sig-arg { color: #006040; }
-.summary-sig-default { color: #501800; }
-
-/* Subclass list
- */
-ul.subclass-list { display: inline; }
-ul.subclass-list li { display: inline; }
-
-/* To render variables, classes etc. like functions */
-table.summary .summary-name { color: #006080; font-weight: bold;
- font-family: monospace; }
-table.summary
- a.summary-name:link { color: #006080; font-weight: bold;
- font-family: monospace; }
-table.summary
- a.summary-name:visited { color: #006080; font-weight: bold;
- font-family: monospace; }
-
-/* Variable values
- * - In the 'variable details' sections, each varaible's value is
- * listed in a 'pre.variable' box. The width of this box is
- * restricted to 80 chars; if the value's repr is longer than
- * this it will be wrapped, using a backslash marked with
- * class 'variable-linewrap'. If the value's repr is longer
- * than 3 lines, the rest will be ellided; and an ellipsis
- * marker ('...' marked with 'variable-ellipsis') will be used.
- * - If the value is a string, its quote marks will be marked
- * with 'variable-quote'.
- * - If the variable is a regexp, it is syntax-highlighted using
- * the re* CSS classes.
- */
-pre.variable { padding: .5em; margin: 0;
- background: #dce4ec; color: #000000;
- border: 1px solid #708890; }
-.variable-linewrap { color: #604000; font-weight: bold; }
-.variable-ellipsis { color: #604000; font-weight: bold; }
-.variable-quote { color: #604000; font-weight: bold; }
-.variable-group { color: #008000; font-weight: bold; }
-.variable-op { color: #604000; font-weight: bold; }
-.variable-string { color: #006030; }
-.variable-unknown { color: #a00000; font-weight: bold; }
-.re { color: #000000; }
-.re-char { color: #006030; }
-.re-op { color: #600000; }
-.re-group { color: #003060; }
-.re-ref { color: #404040; }
-
-/* Base tree
- * - Used by class pages to display the base class hierarchy.
- */
-pre.base-tree { font-size: 80%; margin: 0; }
-
-/* Frames-based table of contents headers
- * - Consists of two frames: one for selecting modules; and
- * the other listing the contents of the selected module.
- * - h1.toc is used for each frame's heading
- * - h2.toc is used for subheadings within each frame.
- */
-h1.toc { text-align: center; font-size: 105%;
- margin: 0; font-weight: bold;
- padding: 0; }
-h2.toc { font-size: 100%; font-weight: bold;
- margin: 0.5em 0 0 -0.3em; }
-
-/* Syntax Highlighting for Source Code
- * - doctest examples are displayed in a 'pre.py-doctest' block.
- * If the example is in a details table entry, then it will use
- * the colors specified by the 'table pre.py-doctest' line.
- * - Source code listings are displayed in a 'pre.py-src' block.
- * Each line is marked with 'span.py-line' (used to draw a line
- * down the left margin, separating the code from the line
- * numbers). Line numbers are displayed with 'span.py-lineno'.
- * The expand/collapse block toggle button is displayed with
- * 'a.py-toggle' (Note: the CSS style for 'a.py-toggle' should not
- * modify the font size of the text.)
- * - If a source code page is opened with an anchor, then the
- * corresponding code block will be highlighted. The code
- * block's header is highlighted with 'py-highlight-hdr'; and
- * the code block's body is highlighted with 'py-highlight'.
- * - The remaining py-* classes are used to perform syntax
- * highlighting (py-string for string literals, py-name for names,
- * etc.)
- */
-pre.py-doctest { padding: .5em; margin: 1em;
- background: #e8f0f8; color: #000000;
- border: 1px solid #708890; }
-table pre.py-doctest { background: #dce4ec;
- color: #000000; }
-pre.py-src { border: 2px solid #000000;
- background: #f0f0f0; color: #000000; }
-.py-line { border-left: 2px solid #000000;
- margin-left: .2em; padding-left: .4em; }
-.py-lineno { font-style: italic; font-size: 90%;
- padding-left: .5em; }
-a.py-toggle { text-decoration: none; }
-div.py-highlight-hdr { border-top: 2px solid #000000;
- border-bottom: 2px solid #000000;
- background: #d8e8e8; }
-div.py-highlight { border-bottom: 2px solid #000000;
- background: #d0e0e0; }
-.py-prompt { color: #005050; font-weight: bold;}
-.py-more { color: #005050; font-weight: bold;}
-.py-string { color: #006030; }
-.py-comment { color: #003060; }
-.py-keyword { color: #600000; }
-.py-output { color: #404040; }
-.py-name { color: #000050; }
-.py-name:link { color: #000050 !important; }
-.py-name:visited { color: #000050 !important; }
-.py-number { color: #005000; }
-.py-defname { color: #000060; font-weight: bold; }
-.py-def-name { color: #000060; font-weight: bold; }
-.py-base-class { color: #000060; }
-.py-param { color: #000060; }
-.py-docstring { color: #006030; }
-.py-decorator { color: #804020; }
-/* Use this if you don't want links to names underlined: */
-/*a.py-name { text-decoration: none; }*/
-
-/* Graphs & Diagrams
- * - These CSS styles are used for graphs & diagrams generated using
- * Graphviz dot. 'img.graph-without-title' is used for bare
- * diagrams (to remove the border created by making the image
- * clickable).
- */
-img.graph-without-title { border: none; }
-img.graph-with-title { border: 1px solid #000000; }
-span.graph-title { font-weight: bold; }
-span.graph-caption { }
-
-/* General-purpose classes
- * - 'p.indent-wrapped-lines' defines a paragraph whose first line
- * is not indented, but whose subsequent lines are.
- * - The 'nomargin-top' class is used to remove the top margin (e.g.
- * from lists). The 'nomargin' class is used to remove both the
- * top and bottom margin (but not the left or right margin --
- * for lists, that would cause the bullets to disappear.)
- */
-p.indent-wrapped-lines { padding: 0 0 0 7em; text-indent: -7em;
- margin: 0; }
-.nomargin-top { margin-top: 0; }
-.nomargin { margin-top: 0; margin-bottom: 0; }
-
-/* HTML Log */
-div.log-block { padding: 0; margin: .5em 0 .5em 0;
- background: #e8f0f8; color: #000000;
- border: 1px solid #000000; }
-div.log-error { padding: .1em .3em .1em .3em; margin: 4px;
- background: #ffb0b0; color: #000000;
- border: 1px solid #000000; }
-div.log-warning { padding: .1em .3em .1em .3em; margin: 4px;
- background: #ffffb0; color: #000000;
- border: 1px solid #000000; }
-div.log-info { padding: .1em .3em .1em .3em; margin: 4px;
- background: #b0ffb0; color: #000000;
- border: 1px solid #000000; }
-h2.log-hdr { background: #70b0ff; color: #000000;
- margin: 0; padding: 0em 0.5em 0em 0.5em;
- border-bottom: 1px solid #000000; font-size: 110%; }
-p.log { font-weight: bold; margin: .5em 0 .5em 0; }
-tr.opt-changed { color: #000000; font-weight: bold; }
-tr.opt-default { color: #606060; }
-pre.log { margin: 0; padding: 0; padding-left: 1em; }
diff --git a/docs/epy/epydoc.js b/docs/epy/epydoc.js
deleted file mode 100644
index 119e71e..0000000
--- a/docs/epy/epydoc.js
+++ /dev/null
@@ -1,293 +0,0 @@
-function toggle_private() {
- // Search for any private/public links on this page. Store
- // their old text in "cmd," so we will know what action to
- // take; and change their text to the opposite action.
- var cmd = "?";
- var elts = document.getElementsByTagName("a");
- for(var i=0; i";
- s += " ";
- for (var i=0; i... ";
- elt.innerHTML = s;
- }
-}
-
-function toggle(id) {
- elt = document.getElementById(id+"-toggle");
- if (elt.innerHTML == "-")
- collapse(id);
- else
- expand(id);
- return false;
-}
-
-function highlight(id) {
- var elt = document.getElementById(id+"-def");
- if (elt) elt.className = "py-highlight-hdr";
- var elt = document.getElementById(id+"-expanded");
- if (elt) elt.className = "py-highlight";
- var elt = document.getElementById(id+"-collapsed");
- if (elt) elt.className = "py-highlight";
-}
-
-function num_lines(s) {
- var n = 1;
- var pos = s.indexOf("\n");
- while ( pos > 0) {
- n += 1;
- pos = s.indexOf("\n", pos+1);
- }
- return n;
-}
-
-// Collapse all blocks that mave more than `min_lines` lines.
-function collapse_all(min_lines) {
- var elts = document.getElementsByTagName("div");
- for (var i=0; i 0)
- if (elt.id.substring(split, elt.id.length) == "-expanded")
- if (num_lines(elt.innerHTML) > min_lines)
- collapse(elt.id.substring(0, split));
- }
-}
-
-function expandto(href) {
- var start = href.indexOf("#")+1;
- if (start != 0 && start != href.length) {
- if (href.substring(start, href.length) != "-") {
- collapse_all(4);
- pos = href.indexOf(".", start);
- while (pos != -1) {
- var id = href.substring(start, pos);
- expand(id);
- pos = href.indexOf(".", pos+1);
- }
- var id = href.substring(start, href.length);
- expand(id);
- highlight(id);
- }
- }
-}
-
-function kill_doclink(id) {
- var parent = document.getElementById(id);
- parent.removeChild(parent.childNodes.item(0));
-}
-function auto_kill_doclink(ev) {
- if (!ev) var ev = window.event;
- if (!this.contains(ev.toElement)) {
- var parent = document.getElementById(this.parentID);
- parent.removeChild(parent.childNodes.item(0));
- }
-}
-
-function doclink(id, name, targets_id) {
- var elt = document.getElementById(id);
-
- // If we already opened the box, then destroy it.
- // (This case should never occur, but leave it in just in case.)
- if (elt.childNodes.length > 1) {
- elt.removeChild(elt.childNodes.item(0));
- }
- else {
- // The outer box: relative + inline positioning.
- var box1 = document.createElement("div");
- box1.style.position = "relative";
- box1.style.display = "inline";
- box1.style.top = 0;
- box1.style.left = 0;
-
- // A shadow for fun
- var shadow = document.createElement("div");
- shadow.style.position = "absolute";
- shadow.style.left = "-1.3em";
- shadow.style.top = "-1.3em";
- shadow.style.background = "#404040";
-
- // The inner box: absolute positioning.
- var box2 = document.createElement("div");
- box2.style.position = "relative";
- box2.style.border = "1px solid #a0a0a0";
- box2.style.left = "-.2em";
- box2.style.top = "-.2em";
- box2.style.background = "white";
- box2.style.padding = ".3em .4em .3em .4em";
- box2.style.fontStyle = "normal";
- box2.onmouseout=auto_kill_doclink;
- box2.parentID = id;
-
- // Get the targets
- var targets_elt = document.getElementById(targets_id);
- var targets = targets_elt.getAttribute("targets");
- var links = "";
- target_list = targets.split(",");
- for (var i=0; i" +
- target[0] + "";
- }
-
- // Put it all together.
- elt.insertBefore(box1, elt.childNodes.item(0));
- //box1.appendChild(box2);
- box1.appendChild(shadow);
- shadow.appendChild(box2);
- box2.innerHTML =
- "Which "+name+" do you want to see documentation for?" +
- "
This document contains the API (Application Programming Interface)
-documentation for this project. Documentation for the Python
-objects defined by the project is divided into separate pages for each
-package, module, and class. The API documentation also includes two
-pages containing information about the project as a whole: a trees
-page, and an index page.
-
-
Object Documentation
-
-
Each Package Documentation page contains:
-
-
A description of the package.
-
A list of the modules and sub-packages contained by the
- package.
-
A summary of the classes defined by the package.
-
A summary of the functions defined by the package.
-
A summary of the variables defined by the package.
-
A detailed description of each function defined by the
- package.
-
A detailed description of each variable defined by the
- package.
-
-
-
Each Module Documentation page contains:
-
-
A description of the module.
-
A summary of the classes defined by the module.
-
A summary of the functions defined by the module.
-
A summary of the variables defined by the module.
-
A detailed description of each function defined by the
- module.
-
A detailed description of each variable defined by the
- module.
-
-
-
Each Class Documentation page contains:
-
-
A class inheritance diagram.
-
A list of known subclasses.
-
A description of the class.
-
A summary of the methods defined by the class.
-
A summary of the instance variables defined by the class.
-
A summary of the class (static) variables defined by the
- class.
-
A detailed description of each method defined by the
- class.
-
A detailed description of each instance variable defined by the
- class.
-
A detailed description of each class (static) variable defined
- by the class.
-
-
-
Project Documentation
-
-
The Trees page contains the module and class hierarchies:
-
-
The module hierarchy lists every package and module, with
- modules grouped into packages. At the top level, and within each
- package, modules and sub-packages are listed alphabetically.
-
The class hierarchy lists every class, grouped by base
- class. If a class has more than one base class, then it will be
- listed under each base class. At the top level, and under each base
- class, classes are listed alphabetically.
-
-
-
The Index page contains indices of terms and
- identifiers:
-
-
The term index lists every term indexed by any object's
- documentation. For each term, the index provides links to each
- place where the term is indexed.
-
The identifier index lists the (short) name of every package,
- module, class, method, function, variable, and parameter. For each
- identifier, the index provides a short description, and a link to
- its documentation.
-
-
-
The Table of Contents
-
-
The table of contents occupies the two frames on the left side of
-the window. The upper-left frame displays the project
-contents, and the lower-left frame displays the module
-contents:
-
-
-
-
- Project Contents...
-
- API Documentation Frame
-
-
-
-
- Module Contents ...
-
-
-
-
-
The project contents frame contains a list of all packages
-and modules that are defined by the project. Clicking on an entry
-will display its contents in the module contents frame. Clicking on a
-special entry, labeled "Everything," will display the contents of
-the entire project.
-
-
The module contents frame contains a list of every
-submodule, class, type, exception, function, and variable defined by a
-module or package. Clicking on an entry will display its
-documentation in the API documentation frame. Clicking on the name of
-the module, at the top of the frame, will display the documentation
-for the module itself.
-
-
The "frames" and "no frames" buttons below the top
-navigation bar can be used to control whether the table of contents is
-displayed or not.
-
-
The Navigation Bar
-
-
A navigation bar is located at the top and bottom of every page.
-It indicates what type of page you are currently viewing, and allows
-you to go to related pages. The following table describes the labels
-on the navigation bar. Note that not some labels (such as
-[Parent]) are not displayed on all pages.
-
-
-
-
Label
-
Highlighted when...
-
Links to...
-
-
[Parent]
-
(never highlighted)
-
the parent of the current package
-
[Package]
-
viewing a package
-
the package containing the current object
-
-
[Module]
-
viewing a module
-
the module containing the current object
-
-
[Class]
-
viewing a class
-
the class containing the current object
-
[Trees]
-
viewing the trees page
-
the trees page
-
[Index]
-
viewing the index page
-
the index page
-
[Help]
-
viewing the help page
-
the help page
-
-
-
The "show private" and "hide private" buttons below
-the top navigation bar can be used to control whether documentation
-for private objects is displayed. Private objects are usually defined
-as objects whose (short) names begin with a single underscore, but do
-not end with an underscore. For example, "_x",
-"__pprint", and "epydoc.epytext._tokenize"
-are private objects; but "re.sub",
-"__init__", and "type_" are not. However,
-if a module defines the "__all__" variable, then its
-contents are used to decide which objects are private.
-
-
A timestamp below the bottom navigation bar indicates when each
-page was last updated.
- 1# Copyright 2014 Google Inc. All rights reserved.
- 2#
- 3# Licensed under the Apache License, Version 2.0 (the "License");
- 4# you may not use this file except in compliance with the License.
- 5# You may obtain a copy of the License at
- 6#
- 7# http://www.apache.org/licenses/LICENSE-2.0
- 8#
- 9# Unless required by applicable law or agreed to in writing, software
-10# distributed under the License is distributed on an "AS IS" BASIS,
-11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-12# See the License for the specific language governing permissions and
-13# limitations under the License.
-14
-15"""Utility module to import a JSON module
-16
-17Hides all the messy details of exactly where
-18we get a simplejson module from.
-19"""
-20
-21__author__='jcgregorio@google.com (Joe Gregorio)'
-22
-23
-24try:# pragma: no cover
-25# Should work for Python2.6 and higher.
-26importjsonassimplejson
-27exceptImportError:# pragma: no cover
-28try:
-29importsimplejson
-30exceptImportError:
-31# Try to import from django, should work on App Engine
-32fromdjango.utilsimportsimplejson
-33
-
-Return the secret key for use for XSRF protection.
-
-If the Site entity does not have a secret key, this method will also create
-one and persist it.
-
-Returns:
- The secret key.
-
-
-Composes the value for the 'state' parameter.
-
-Packs the current request URI and an XSRF token into an opaque string that
-can be passed to the authentication server via the 'state' parameter.
-
-Args:
- request_handler: webapp.RequestHandler, The request.
- user: google.appengine.api.users.User, The current user.
-
-Returns:
- The state value as a string.
-
-
-Parse the value of the 'state' parameter.
-
-Parses the value and validates the XSRF token in the state parameter.
-
-Args:
- state: string, The value of the state parameter.
- user: google.appengine.api.users.User, The current user.
-
-Raises:
- InvalidXsrfTokenError: if the XSRF token is invalid.
-
-Returns:
- The redirect URI.
-
-
-Creates an OAuth2Decorator populated from a clientsecrets file.
-
-Args:
- filename: string, File name of client secrets.
- scope: string or list of strings, scope(s) of the credentials being
- requested.
- message: string, A friendly string to display to the user if the
- clientsecrets file is missing or invalid. The message may contain HTML and
- will be presented on the web interface for any method that uses the
- decorator.
- cache: An optional cache service client that implements get() and set()
- methods. See clientsecrets.loadfile() for details.
-
-Returns: An OAuth2Decorator
-
-
- 1# Copyright 2014 Google Inc. All rights reserved.
- 2#
- 3# Licensed under the Apache License, Version 2.0 (the "License");
- 4# you may not use this file except in compliance with the License.
- 5# You may obtain a copy of the License at
- 6#
- 7# http://www.apache.org/licenses/LICENSE-2.0
- 8#
- 9# Unless required by applicable law or agreed to in writing, software
- 10# distributed under the License is distributed on an "AS IS" BASIS,
- 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- 12# See the License for the specific language governing permissions and
- 13# limitations under the License.
- 14
- 15"""Utilities for Google App Engine
- 16
- 17Utilities for making it easier to use OAuth 2.0 on Google App Engine.
- 18"""
- 19
- 20__author__='jcgregorio@google.com (Joe Gregorio)'
- 21
- 22importcgi
- 23importjson
- 24importlogging
- 25importos
- 26importpickle
- 27importthreading
- 28
- 29importhttplib2
- 30
- 31fromgoogle.appengine.apiimportapp_identity
- 32fromgoogle.appengine.apiimportmemcache
- 33fromgoogle.appengine.apiimportusers
- 34fromgoogle.appengine.extimportdb
- 35fromgoogle.appengine.extimportwebapp
- 36fromgoogle.appengine.ext.webapp.utilimportlogin_required
- 37fromgoogle.appengine.ext.webapp.utilimportrun_wsgi_app
- 38fromoauth2clientimportGOOGLE_AUTH_URI
- 39fromoauth2clientimportGOOGLE_REVOKE_URI
- 40fromoauth2clientimportGOOGLE_TOKEN_URI
- 41fromoauth2clientimportclientsecrets
- 42fromoauth2clientimportutil
- 43fromoauth2clientimportxsrfutil
- 44fromoauth2client.clientimportAccessTokenRefreshError
- 45fromoauth2client.clientimportAssertionCredentials
- 46fromoauth2client.clientimportCredentials
- 47fromoauth2client.clientimportFlow
- 48fromoauth2client.clientimportOAuth2WebServerFlow
- 49fromoauth2client.clientimportStorage
- 50
- 51# TODO(dhermes): Resolve import issue.
- 52# This is a temporary fix for a Google internal issue.
- 53try:
- 54fromgoogle.appengine.extimportndb
- 55exceptImportError:
- 56ndb=None
- 57
- 58
- 59logger=logging.getLogger(__name__)
- 60
- 61OAUTH2CLIENT_NAMESPACE='oauth2client#ns'
- 62
- 63XSRF_MEMCACHE_ID='xsrf_secret_key'
-
67"""Escape text to make it safe to display.
- 68
- 69 Args:
- 70 s: string, The text to escape.
- 71
- 72 Returns:
- 73 The escaped text as a string.
- 74 """
- 75returncgi.escape(s,quote=1).replace("'",''')
-
87"""Storage for the sites XSRF secret key.
- 88
- 89 There will only be one instance stored of this model, the one used for the
- 90 site.
- 91 """
- 92secret=db.StringProperty()
-
96"""NDB Model for storage for the sites XSRF secret key.
- 97
- 98 Since this model uses the same kind as SiteXsrfSecretKey, it can be used
- 99 interchangeably. This simply provides an NDB model for interacting with the
-100 same data the DB model interacts with.
-101
-102 There should only be one instance stored of this model, the one used for the
-103 site.
-104 """
-105secret=ndb.StringProperty()
-106
-107@classmethod
-
142"""Credentials object for App Engine Assertion Grants
-143
-144 This object will allow an App Engine application to identify itself to Google
-145 and other OAuth 2.0 servers that can verify assertions. It can be used for the
-146 purpose of accessing data stored under an account assigned to the App Engine
-147 application itself.
-148
-149 This credential does not require a flow to instantiate because it represents
-150 a two legged flow, and therefore has all of the required information to
-151 generate and refresh its own access tokens.
-152 """
-153
-154@util.positional(2)
-
156"""Constructor for AppAssertionCredentials
-157
-158 Args:
-159 scope: string or iterable of strings, scope(s) of the credentials being
-160 requested.
-161 **kwargs: optional keyword args, including:
-162 service_account_id: service account id of the application. If None or
-163 unspecified, the default service account for the app is used.
-164 """
-165self.scope=util.scopes_to_string(scope)
-166self._kwargs=kwargs
-167self.service_account_id=kwargs.get('service_account_id',None)
-168
-169# Assertion type is no longer used, but still in the parent class signature.
-170super(AppAssertionCredentials,self).__init__(None)
-
178"""Refreshes the access_token.
-179
-180 Since the underlying App Engine app_identity implementation does its own
-181 caching we can skip all the storage hoops and just to a refresh using the
-182 API.
-183
-184 Args:
-185 http_request: callable, a callable that matches the method signature of
-186 httplib2.Http.request, used to make the refresh request.
-187
-188 Raises:
-189 AccessTokenRefreshError: When the refresh fails.
-190 """
-191try:
-192scopes=self.scope.split()
-193(token,_)=app_identity.get_access_token(
-194scopes,service_account_id=self.service_account_id)
-195exceptapp_identity.Errorase:
-196raiseAccessTokenRefreshError(str(e))
-197self.access_token=token
-
211"""App Engine datastore Property for Flow.
-212
-213 Utility property that allows easy storage and retrieval of an
-214 oauth2client.Flow"""
-215
-216# Tell what the user type is.
-217data_type=Flow
-218
-219# For writing to datastore.
-
232ifvalueisnotNoneandnotisinstance(value,Flow):
-233raisedb.BadValueError('Property %s must be convertible '
-234'to a FlowThreeLegged instance (%s)'%
-235(self.name,value))
-236returnsuper(FlowProperty,self).validate(value)
-
244"""App Engine NDB datastore Property for Flow.
-245
-246 Serves the same purpose as the DB FlowProperty, but for NDB models. Since
-247 PickleProperty inherits from BlobProperty, the underlying representation of
-248 the data in the datastore will be the same as in the DB case.
-249
-250 Utility property that allows easy storage and retrieval of an
-251 oauth2client.Flow
-252 """
-253
-
255"""Validates a value as a proper Flow object.
-256
-257 Args:
-258 value: A value to be set on the property.
-259
-260 Raises:
-261 TypeError if the value is not an instance of Flow.
-262 """
-263logger.info('validate: Got type %s',type(value))
-264ifvalueisnotNoneandnotisinstance(value,Flow):
-265raiseTypeError('Property %s must be convertible to a flow '
-266'instance; received: %s.'%(self._name,value))
-
270"""App Engine datastore Property for Credentials.
-271
-272 Utility property that allows easy storage and retrieval of
-273 oath2client.Credentials
-274 """
-275
-276# Tell what the user type is.
-277data_type=Credentials
-278
-279# For writing to datastore.
-
304value=super(CredentialsProperty,self).validate(value)
-305logger.info("validate: Got type "+str(type(value)))
-306ifvalueisnotNoneandnotisinstance(value,Credentials):
-307raisedb.BadValueError('Property %s must be convertible '
-308'to a Credentials instance (%s)'%
-309(self.name,value))
-310#if value is not None and not isinstance(value, Credentials):
-311# return None
-312returnvalue
-
316# TODO(dhermes): Turn this into a JsonProperty and overhaul the Credentials
-317# and subclass mechanics to use new_from_dict, to_dict,
-318# from_dict, etc.
-319-classCredentialsNDBProperty(ndb.BlobProperty):
-
320"""App Engine NDB datastore Property for Credentials.
-321
-322 Serves the same purpose as the DB CredentialsProperty, but for NDB models.
-323 Since CredentialsProperty stores data as a blob and this inherits from
-324 BlobProperty, the data in the datastore will be the same as in the DB case.
-325
-326 Utility property that allows easy storage and retrieval of Credentials and
-327 subclasses.
-328 """
-
330"""Validates a value as a proper credentials object.
-331
-332 Args:
-333 value: A value to be set on the property.
-334
-335 Raises:
-336 TypeError if the value is not an instance of Credentials.
-337 """
-338logger.info('validate: Got type %s',type(value))
-339ifvalueisnotNoneandnotisinstance(value,Credentials):
-340raiseTypeError('Property %s must be convertible to a credentials '
-341'instance; received: %s.'%(self._name,value))
-
344"""Converts our validated value to a JSON serialized string.
-345
-346 Args:
-347 value: A value to be set in the datastore.
-348
-349 Returns:
-350 A JSON serialized version of the credential, else '' if value is None.
-351 """
-352ifvalueisNone:
-353return''
-354else:
-355returnvalue.to_json()
-
358"""Converts our stored JSON string back to the desired type.
-359
-360 Args:
-361 value: A value from the datastore to be converted to the desired type.
-362
-363 Returns:
-364 A deserialized Credentials (or subclass) object, else None if the
-365 value can't be parsed.
-366 """
-367ifnotvalue:
-368returnNone
-369try:
-370# Uses the from_json method of the implied class of value
-371credentials=Credentials.new_from_json(value)
-372exceptValueError:
-373credentials=None
-374returncredentials
-
378"""Store and retrieve a credential to and from the App Engine datastore.
-379
-380 This Storage helper presumes the Credentials have been stored as a
-381 CredentialsProperty or CredentialsNDBProperty on a datastore model class, and
-382 that entities are stored by key_name.
-383 """
-384
-385@util.positional(4)
-
387"""Constructor for Storage.
-388
-389 Args:
-390 model: db.Model or ndb.Model, model class
-391 key_name: string, key name for the entity that has the credentials
-392 property_name: string, name of the property that is a CredentialsProperty
-393 or CredentialsNDBProperty.
-394 cache: memcache, a write-through cache to put in front of the datastore.
-395 If the model you are using is an NDB model, using a cache will be
-396 redundant since the model uses an instance cache and memcache for you.
-397 user: users.User object, optional. Can be used to grab user ID as a
-398 key_name if no key name is specified.
-399 """
-400ifkey_nameisNone:
-401ifuserisNone:
-402raiseValueError('StorageByKeyName called with no key name or user.')
-403key_name=user.user_id()
-404
-405self._model=model
-406self._key_name=key_name
-407self._property_name=property_name
-408self._cache=cache
-
411"""Determine whether the model of the instance is an NDB model.
-412
-413 Returns:
-414 Boolean indicating whether or not the model is an NDB or DB model.
-415 """
-416# issubclass will fail if one of the arguments is not a class, only need
-417# worry about new-style classes since ndb and db models are new-style
-418ifisinstance(self._model,type):
-419ifndbisnotNoneandissubclass(self._model,ndb.Model):
-420returnTrue
-421elifissubclass(self._model,db.Model):
-422returnFalse
-423
-424raiseTypeError('Model class not an NDB or DB model: %s.'%(self._model,))
-
427"""Retrieve entity from datastore.
-428
-429 Uses a different model method for db or ndb models.
-430
-431 Returns:
-432 Instance of the model corresponding to the current storage object
-433 and stored using the key name of the storage object.
-434 """
-435ifself._is_ndb():
-436returnself._model.get_by_id(self._key_name)
-437else:
-438returnself._model.get_by_key_name(self._key_name)
-
441"""Delete entity from datastore.
-442
-443 Attempts to delete using the key_name stored on the object, whether or not
-444 the given key is in the datastore.
-445 """
-446ifself._is_ndb():
-447ndb.Key(self._model,self._key_name).delete()
-448else:
-449entity_key=db.Key.from_path(self._model.kind(),self._key_name)
-450db.delete(entity_key)
-
477"""Write a Credentials to the datastore.
-478
-479 Args:
-480 credentials: Credentials, the credentials to store.
-481 """
-482entity=self._model.get_or_insert(self._key_name)
-483setattr(entity,self._property_name,credentials)
-484entity.put()
-485ifself._cache:
-486self._cache.set(self._key_name,credentials.to_json())
-
499"""Storage for OAuth 2.0 Credentials
-500
-501 Storage of the model is keyed by the user.user_id().
-502 """
-503credentials=CredentialsProperty()
-
508"""NDB Model for storage of OAuth 2.0 Credentials
-509
-510 Since this model uses the same kind as CredentialsModel and has a property
-511 which can serialize and deserialize Credentials correctly, it can be used
-512 interchangeably with a CredentialsModel to access, insert and delete the
-513 same entities. This simply provides an NDB model for interacting with the
-514 same data the DB model interacts with.
-515
-516 Storage of the model is keyed by the user.user_id().
-517 """
-518credentials=CredentialsNDBProperty()
-519
-520@classmethod
-
527"""Composes the value for the 'state' parameter.
-528
-529 Packs the current request URI and an XSRF token into an opaque string that
-530 can be passed to the authentication server via the 'state' parameter.
-531
-532 Args:
-533 request_handler: webapp.RequestHandler, The request.
-534 user: google.appengine.api.users.User, The current user.
-535
-536 Returns:
-537 The state value as a string.
-538 """
-539uri=request_handler.request.url
-540token=xsrfutil.generate_token(xsrf_secret_key(),user.user_id(),
-541action_id=str(uri))
-542returnuri+':'+token
-
546"""Parse the value of the 'state' parameter.
-547
-548 Parses the value and validates the XSRF token in the state parameter.
-549
-550 Args:
-551 state: string, The value of the state parameter.
-552 user: google.appengine.api.users.User, The current user.
-553
-554 Raises:
-555 InvalidXsrfTokenError: if the XSRF token is invalid.
-556
-557 Returns:
-558 The redirect URI.
-559 """
-560uri,token=state.rsplit(':',1)
-561ifnotxsrfutil.validate_token(xsrf_secret_key(),token,user.user_id(),
-562action_id=uri):
-563raiseInvalidXsrfTokenError()
-564
-565returnuri
-
569"""Utility for making OAuth 2.0 easier.
-570
-571 Instantiate and then use with oauth_required or oauth_aware
-572 as decorators on webapp.RequestHandler methods.
-573
-574 Example:
-575
-576 decorator = OAuth2Decorator(
-577 client_id='837...ent.com',
-578 client_secret='Qh...wwI',
-579 scope='https://www.googleapis.com/auth/plus')
-580
-581
-582 class MainHandler(webapp.RequestHandler):
-583
-584 @decorator.oauth_required
-585 def get(self):
-586 http = decorator.http()
-587 # http is authorized with the user's Credentials and can be used
-588 # in API calls
-589
-590 """
-591
-
596"""A thread local Credentials object.
-597
-598 Returns:
-599 A client.Credentials object, or None if credentials hasn't been set in
-600 this thread yet, which may happen when calling has_credentials inside
-601 oauth_aware.
-602 """
-603returngetattr(self._tls,'credentials',None)
-
611"""A thread local Flow object.
-612
-613 Returns:
-614 A credentials.Flow object, or None if the flow hasn't been set in this
-615 thread yet, which happens in _create_flow() since Flows are created
-616 lazily.
-617 """
-618returngetattr(self._tls,'flow',None)
-
636
-637"""Constructor for OAuth2Decorator
-638
-639 Args:
-640 client_id: string, client identifier.
-641 client_secret: string client secret.
-642 scope: string or iterable of strings, scope(s) of the credentials being
-643 requested.
-644 auth_uri: string, URI for authorization endpoint. For convenience
-645 defaults to Google's endpoints but any OAuth 2.0 provider can be used.
-646 token_uri: string, URI for token endpoint. For convenience
-647 defaults to Google's endpoints but any OAuth 2.0 provider can be used.
-648 revoke_uri: string, URI for revoke endpoint. For convenience
-649 defaults to Google's endpoints but any OAuth 2.0 provider can be used.
-650 user_agent: string, User agent of your application, default to None.
-651 message: Message to display if there are problems with the OAuth 2.0
-652 configuration. The message may contain HTML and will be presented on the
-653 web interface for any method that uses the decorator.
-654 callback_path: string, The absolute path to use as the callback URI. Note
-655 that this must match up with the URI given when registering the
-656 application in the APIs Console.
-657 token_response_param: string. If provided, the full JSON response
-658 to the access token request will be encoded and included in this query
-659 parameter in the callback URI. This is useful with providers (e.g.
-660 wordpress.com) that include extra fields that the client may want.
-661 _storage_class: "Protected" keyword argument not typically provided to
-662 this constructor. A storage class to aid in storing a Credentials object
-663 for a user in the datastore. Defaults to StorageByKeyName.
-664 _credentials_class: "Protected" keyword argument not typically provided to
-665 this constructor. A db or ndb Model class to hold credentials. Defaults
-666 to CredentialsModel.
-667 _credentials_property_name: "Protected" keyword argument not typically
-668 provided to this constructor. A string indicating the name of the field
-669 on the _credentials_class where a Credentials object will be stored.
-670 Defaults to 'credentials'.
-671 **kwargs: dict, Keyword arguments are passed along as kwargs to
-672 the OAuth2WebServerFlow constructor.
-673
-674 """
-675self._tls=threading.local()
-676self.flow=None
-677self.credentials=None
-678self._client_id=client_id
-679self._client_secret=client_secret
-680self._scope=util.scopes_to_string(scope)
-681self._auth_uri=auth_uri
-682self._token_uri=token_uri
-683self._revoke_uri=revoke_uri
-684self._user_agent=user_agent
-685self._kwargs=kwargs
-686self._message=message
-687self._in_error=False
-688self._callback_path=callback_path
-689self._token_response_param=token_response_param
-690self._storage_class=_storage_class
-691self._credentials_class=_credentials_class
-692self._credentials_property_name=_credentials_property_name
-
700"""Decorator that starts the OAuth 2.0 dance.
-701
-702 Starts the OAuth dance for the logged in user if they haven't already
-703 granted access for this application.
-704
-705 Args:
-706 method: callable, to be decorated method of a webapp.RequestHandler
-707 instance.
-708 """
-709
-710defcheck_oauth(request_handler,*args,**kwargs):
-711ifself._in_error:
-712self._display_error_message(request_handler)
-713return
-714
-715user=users.get_current_user()
-716# Don't use @login_decorator as this could be used in a POST request.
-717ifnotuser:
-718request_handler.redirect(users.create_login_url(
-719request_handler.request.uri))
-720return
-721
-722self._create_flow(request_handler)
-723
-724# Store the request URI in 'state' so we can use it later
-725self.flow.params['state']=_build_state_value(request_handler,user)
-726self.credentials=self._storage_class(
-727self._credentials_class,None,
-728self._credentials_property_name,user=user).get()
-729
-730ifnotself.has_credentials():
-731returnrequest_handler.redirect(self.authorize_url())
-732try:
-733resp=method(request_handler,*args,**kwargs)
-734exceptAccessTokenRefreshError:
-735returnrequest_handler.redirect(self.authorize_url())
-736finally:
-737self.credentials=None
-738returnresp
-
743"""Create the Flow object.
-744
-745 The Flow is calculated lazily since we don't know where this app is
-746 running until it receives a request, at which point redirect_uri can be
-747 calculated and then the Flow object can be constructed.
-748
-749 Args:
-750 request_handler: webapp.RequestHandler, the request handler.
-751 """
-752ifself.flowisNone:
-753redirect_uri=request_handler.request.relative_url(
-754self._callback_path)# Usually /oauth2callback
-755self.flow=OAuth2WebServerFlow(self._client_id,self._client_secret,
-756self._scope,redirect_uri=redirect_uri,
-757user_agent=self._user_agent,
-758auth_uri=self._auth_uri,
-759token_uri=self._token_uri,
-760revoke_uri=self._revoke_uri,
-761**self._kwargs)
-
764"""Decorator that sets up for OAuth 2.0 dance, but doesn't do it.
-765
-766 Does all the setup for the OAuth dance, but doesn't initiate it.
-767 This decorator is useful if you want to create a page that knows
-768 whether or not the user has granted access to this application.
-769 From within a method decorated with @oauth_aware the has_credentials()
-770 and authorize_url() methods can be called.
-771
-772 Args:
-773 method: callable, to be decorated method of a webapp.RequestHandler
-774 instance.
-775 """
-776
-777defsetup_oauth(request_handler,*args,**kwargs):
-778ifself._in_error:
-779self._display_error_message(request_handler)
-780return
-781
-782user=users.get_current_user()
-783# Don't use @login_decorator as this could be used in a POST request.
-784ifnotuser:
-785request_handler.redirect(users.create_login_url(
-786request_handler.request.uri))
-787return
-788
-789self._create_flow(request_handler)
-790
-791self.flow.params['state']=_build_state_value(request_handler,user)
-792self.credentials=self._storage_class(
-793self._credentials_class,None,
-794self._credentials_property_name,user=user).get()
-795try:
-796resp=method(request_handler,*args,**kwargs)
-797finally:
-798self.credentials=None
-799returnresp
-
804"""True if for the logged in user there are valid access Credentials.
-805
-806 Must only be called from with a webapp.RequestHandler subclassed method
-807 that had been decorated with either @oauth_required or @oauth_aware.
-808 """
-809returnself.credentialsisnotNoneandnotself.credentials.invalid
-
812"""Returns the URL to start the OAuth dance.
-813
-814 Must only be called from with a webapp.RequestHandler subclassed method
-815 that had been decorated with either @oauth_required or @oauth_aware.
-816 """
-817url=self.flow.step1_get_authorize_url()
-818returnstr(url)
-
821"""Returns an authorized http instance.
-822
-823 Must only be called from within an @oauth_required decorated method, or
-824 from within an @oauth_aware decorated method where has_credentials()
-825 returns True.
-826
-827 Args:
-828 *args: Positional arguments passed to httplib2.Http constructor.
-829 **kwargs: Positional arguments passed to httplib2.Http constructor.
-830 """
-831returnself.credentials.authorize(httplib2.Http(*args,**kwargs))
-
835"""The absolute path where the callback will occur.
-836
-837 Note this is the absolute path, not the absolute URI, that will be
-838 calculated by the decorator at runtime. See callback_handler() for how this
-839 should be used.
-840
-841 Returns:
-842 The callback path as a string.
-843 """
-844returnself._callback_path
-
893"""WSGI application for handling the OAuth 2.0 redirect callback.
-894
-895 If you need finer grained control use `callback_handler` which returns just
-896 the webapp.RequestHandler.
-897
-898 Returns:
-899 A webapp.WSGIApplication that handles the redirect back from the
-900 server during the OAuth 2.0 dance.
-901 """
-902returnwebapp.WSGIApplication([
-903(self.callback_path,self.callback_handler())
-904])
-
908"""An OAuth2Decorator that builds from a clientsecrets file.
-909
-910 Uses a clientsecrets file as the source for all the information when
-911 constructing an OAuth2Decorator.
-912
-913 Example:
-914
-915 decorator = OAuth2DecoratorFromClientSecrets(
-916 os.path.join(os.path.dirname(__file__), 'client_secrets.json')
-917 scope='https://www.googleapis.com/auth/plus')
-918
-919
-920 class MainHandler(webapp.RequestHandler):
-921
-922 @decorator.oauth_required
-923 def get(self):
-924 http = decorator.http()
-925 # http is authorized with the user's Credentials and can be used
-926 # in API calls
-927 """
-928
-929@util.positional(3)
-
931"""Constructor
-932
-933 Args:
-934 filename: string, File name of client secrets.
-935 scope: string or iterable of strings, scope(s) of the credentials being
-936 requested.
-937 message: string, A friendly string to display to the user if the
-938 clientsecrets file is missing or invalid. The message may contain HTML
-939 and will be presented on the web interface for any method that uses the
-940 decorator.
-941 cache: An optional cache service client that implements get() and set()
-942 methods. See clientsecrets.loadfile() for details.
-943 **kwargs: dict, Keyword arguments are passed along as kwargs to
-944 the OAuth2WebServerFlow constructor.
-945 """
-946client_type,client_info=clientsecrets.loadfile(filename,cache=cache)
-947ifclient_typenotin[
-948clientsecrets.TYPE_WEB,clientsecrets.TYPE_INSTALLED]:
-949raiseInvalidClientSecretsError(
-950"OAuth2Decorator doesn't support this OAuth 2.0 flow.")
-951constructor_kwargs=dict(kwargs)
-952constructor_kwargs.update({
-953'auth_uri':client_info['auth_uri'],
-954'token_uri':client_info['token_uri'],
-955'message':message,
-956})
-957revoke_uri=client_info.get('revoke_uri')
-958ifrevoke_uriisnotNone:
-959constructor_kwargs['revoke_uri']=revoke_uri
-960super(OAuth2DecoratorFromClientSecrets,self).__init__(
-961client_info['client_id'],client_info['client_secret'],
-962scope,**constructor_kwargs)
-963ifmessageisnotNone:
-964self._message=message
-965else:
-966self._message='Please configure your application for OAuth 2.0.'
-
972"""Creates an OAuth2Decorator populated from a clientsecrets file.
-973
-974 Args:
-975 filename: string, File name of client secrets.
-976 scope: string or list of strings, scope(s) of the credentials being
-977 requested.
-978 message: string, A friendly string to display to the user if the
-979 clientsecrets file is missing or invalid. The message may contain HTML and
-980 will be presented on the web interface for any method that uses the
-981 decorator.
-982 cache: An optional cache service client that implements get() and set()
-983 methods. See clientsecrets.loadfile() for details.
-984
-985 Returns: An OAuth2Decorator
-986
-987 """
-988returnOAuth2DecoratorFromClientSecrets(filename,scope,
-989message=message,cache=cache)
-
-Credentials object for App Engine Assertion Grants
-
-This object will allow an App Engine application to identify itself to Google
-and other OAuth 2.0 servers that can verify assertions. It can be used for the
-purpose of accessing data stored under an account assigned to the App Engine
-application itself.
-
-This credential does not require a flow to instantiate because it represents
-a two legged flow, and therefore has all of the required information to
-generate and refresh its own access tokens.
-
-
-Constructor for AppAssertionCredentials
-
-Args:
- scope: string or iterable of strings, scope(s) of the credentials being
- requested.
- **kwargs: optional keyword args, including:
- service_account_id: service account id of the application. If None or
- unspecified, the default service account for the app is used.
-
-
-Instantiate a Credentials object from a JSON description of it. The JSON
-should have been produced by calling .to_json() on the object.
-
-Args:
- data: dict, A deserialized JSON object.
-
-Returns:
- An instance of a Credentials subclass.
-
-
-Refreshes the access_token.
-
-Since the underlying App Engine app_identity implementation does its own
-caching we can skip all the storage hoops and just to a refresh using the
-API.
-
-Args:
- http_request: callable, a callable that matches the method signature of
- httplib2.Http.request, used to make the refresh request.
-
-Raises:
- AccessTokenRefreshError: When the refresh fails.
-
-
-Whether this Credentials object is scopeless.
-
-create_scoped(scopes) method needs to be called in order to create
-a Credentials object for API calls.
-
-
-NDB Model for storage of OAuth 2.0 Credentials
-
-Since this model uses the same kind as CredentialsModel and has a property
-which can serialize and deserialize Credentials correctly, it can be used
-interchangeably with a CredentialsModel to access, insert and delete the
-same entities. This simply provides an NDB model for interacting with the
-same data the DB model interacts with.
-
-Storage of the model is keyed by the user.user_id().
-
-
-App Engine NDB datastore Property for Credentials.
-
-Serves the same purpose as the DB CredentialsProperty, but for NDB models.
-Since CredentialsProperty stores data as a blob and this inherits from
-BlobProperty, the data in the datastore will be the same as in the DB case.
-
-Utility property that allows easy storage and retrieval of Credentials and
-subclasses.
-
-
-Validates a value as a proper credentials object.
-
-Args:
- value: A value to be set on the property.
-
-Raises:
- TypeError if the value is not an instance of Credentials.
-
-
-Converts our validated value to a JSON serialized string.
-
-Args:
- value: A value to be set in the datastore.
-
-Returns:
- A JSON serialized version of the credential, else '' if value is None.
-
-
-Converts our stored JSON string back to the desired type.
-
-Args:
- value: A value from the datastore to be converted to the desired type.
-
-Returns:
- A deserialized Credentials (or subclass) object, else None if the
- value can't be parsed.
-
-
-App Engine NDB datastore Property for Flow.
-
-Serves the same purpose as the DB FlowProperty, but for NDB models. Since
-PickleProperty inherits from BlobProperty, the underlying representation of
-the data in the datastore will be the same as in the DB case.
-
-Utility property that allows easy storage and retrieval of an
-oauth2client.Flow
-
-
-Validates a value as a proper Flow object.
-
-Args:
- value: A value to be set on the property.
-
-Raises:
- TypeError if the value is not an instance of Flow.
-
-
-Utility for making OAuth 2.0 easier.
-
-Instantiate and then use with oauth_required or oauth_aware
-as decorators on webapp.RequestHandler methods.
-
-Example:
-
- decorator = OAuth2Decorator(
- client_id='837...ent.com',
- client_secret='Qh...wwI',
- scope='https://www.googleapis.com/auth/plus')
-
-
- class MainHandler(webapp.RequestHandler):
-
- @decorator.oauth_required
- def get(self):
- http = decorator.http()
- # http is authorized with the user's Credentials and can be used
- # in API calls
-
-
-A thread local Credentials object.
-
-Returns:
- A client.Credentials object, or None if credentials hasn't been set in
- this thread yet, which may happen when calling has_credentials inside
- oauth_aware.
-
-
-A thread local Flow object.
-
-Returns:
- A credentials.Flow object, or None if the flow hasn't been set in this
- thread yet, which happens in _create_flow() since Flows are created
- lazily.
-
-
-Constructor for OAuth2Decorator
-
-Args:
- client_id: string, client identifier.
- client_secret: string client secret.
- scope: string or iterable of strings, scope(s) of the credentials being
- requested.
- auth_uri: string, URI for authorization endpoint. For convenience
- defaults to Google's endpoints but any OAuth 2.0 provider can be used.
- token_uri: string, URI for token endpoint. For convenience
- defaults to Google's endpoints but any OAuth 2.0 provider can be used.
- revoke_uri: string, URI for revoke endpoint. For convenience
- defaults to Google's endpoints but any OAuth 2.0 provider can be used.
- user_agent: string, User agent of your application, default to None.
- message: Message to display if there are problems with the OAuth 2.0
- configuration. The message may contain HTML and will be presented on the
- web interface for any method that uses the decorator.
- callback_path: string, The absolute path to use as the callback URI. Note
- that this must match up with the URI given when registering the
- application in the APIs Console.
- token_response_param: string. If provided, the full JSON response
- to the access token request will be encoded and included in this query
- parameter in the callback URI. This is useful with providers (e.g.
- wordpress.com) that include extra fields that the client may want.
- _storage_class: "Protected" keyword argument not typically provided to
- this constructor. A storage class to aid in storing a Credentials object
- for a user in the datastore. Defaults to StorageByKeyName.
- _credentials_class: "Protected" keyword argument not typically provided to
- this constructor. A db or ndb Model class to hold credentials. Defaults
- to CredentialsModel.
- _credentials_property_name: "Protected" keyword argument not typically
- provided to this constructor. A string indicating the name of the field
- on the _credentials_class where a Credentials object will be stored.
- Defaults to 'credentials'.
- **kwargs: dict, Keyword arguments are passed along as kwargs to
- the OAuth2WebServerFlow constructor.
-
-
-Decorator that starts the OAuth 2.0 dance.
-
-Starts the OAuth dance for the logged in user if they haven't already
-granted access for this application.
-
-Args:
- method: callable, to be decorated method of a webapp.RequestHandler
- instance.
-
-
-Create the Flow object.
-
-The Flow is calculated lazily since we don't know where this app is
-running until it receives a request, at which point redirect_uri can be
-calculated and then the Flow object can be constructed.
-
-Args:
- request_handler: webapp.RequestHandler, the request handler.
-
-
-Decorator that sets up for OAuth 2.0 dance, but doesn't do it.
-
-Does all the setup for the OAuth dance, but doesn't initiate it.
-This decorator is useful if you want to create a page that knows
-whether or not the user has granted access to this application.
-From within a method decorated with @oauth_aware the has_credentials()
-and authorize_url() methods can be called.
-
-Args:
- method: callable, to be decorated method of a webapp.RequestHandler
- instance.
-
-
-True if for the logged in user there are valid access Credentials.
-
-Must only be called from with a webapp.RequestHandler subclassed method
-that had been decorated with either @oauth_required or @oauth_aware.
-
-
-Returns the URL to start the OAuth dance.
-
-Must only be called from with a webapp.RequestHandler subclassed method
-that had been decorated with either @oauth_required or @oauth_aware.
-
-
-Returns an authorized http instance.
-
-Must only be called from within an @oauth_required decorated method, or
-from within an @oauth_aware decorated method where has_credentials()
-returns True.
-
-Args:
- *args: Positional arguments passed to httplib2.Http constructor.
- **kwargs: Positional arguments passed to httplib2.Http constructor.
-
-
-The absolute path where the callback will occur.
-
-Note this is the absolute path, not the absolute URI, that will be
-calculated by the decorator at runtime. See callback_handler() for how this
-should be used.
-
-Returns:
- The callback path as a string.
-
-
-RequestHandler for the OAuth 2.0 redirect callback.
-
-Usage:
- app = webapp.WSGIApplication([
- ('/index', MyIndexHandler),
- ...,
- (decorator.callback_path, decorator.callback_handler())
- ])
-
-Returns:
- A webapp.RequestHandler that handles the redirect back from the
- server during the OAuth 2.0 dance.
-
-
-WSGI application for handling the OAuth 2.0 redirect callback.
-
-If you need finer grained control use `callback_handler` which returns just
-the webapp.RequestHandler.
-
-Returns:
- A webapp.WSGIApplication that handles the redirect back from the
- server during the OAuth 2.0 dance.
-
-
-An OAuth2Decorator that builds from a clientsecrets file.
-
-Uses a clientsecrets file as the source for all the information when
-constructing an OAuth2Decorator.
-
-Example:
-
- decorator = OAuth2DecoratorFromClientSecrets(
- os.path.join(os.path.dirname(__file__), 'client_secrets.json')
- scope='https://www.googleapis.com/auth/plus')
-
-
- class MainHandler(webapp.RequestHandler):
-
- @decorator.oauth_required
- def get(self):
- http = decorator.http()
- # http is authorized with the user's Credentials and can be used
- # in API calls
-
-
-Constructor
-
-Args:
- filename: string, File name of client secrets.
- scope: string or iterable of strings, scope(s) of the credentials being
- requested.
- message: string, A friendly string to display to the user if the
- clientsecrets file is missing or invalid. The message may contain HTML
- and will be presented on the web interface for any method that uses the
- decorator.
- cache: An optional cache service client that implements get() and set()
- methods. See clientsecrets.loadfile() for details.
- **kwargs: dict, Keyword arguments are passed along as kwargs to
- the OAuth2WebServerFlow constructor.
-
-
-NDB Model for storage for the sites XSRF secret key.
-
-Since this model uses the same kind as SiteXsrfSecretKey, it can be used
-interchangeably. This simply provides an NDB model for interacting with the
-same data the DB model interacts with.
-
-There should only be one instance stored of this model, the one used for the
-site.
-
-
-Store and retrieve a credential to and from the App Engine datastore.
-
-This Storage helper presumes the Credentials have been stored as a
-CredentialsProperty or CredentialsNDBProperty on a datastore model class, and
-that entities are stored by key_name.
-
-
-Constructor for Storage.
-
-Args:
- model: db.Model or ndb.Model, model class
- key_name: string, key name for the entity that has the credentials
- property_name: string, name of the property that is a CredentialsProperty
- or CredentialsNDBProperty.
- cache: memcache, a write-through cache to put in front of the datastore.
- If the model you are using is an NDB model, using a cache will be
- redundant since the model uses an instance cache and memcache for you.
- user: users.User object, optional. Can be used to grab user ID as a
- key_name if no key name is specified.
-
-
-Retrieve entity from datastore.
-
-Uses a different model method for db or ndb models.
-
-Returns:
- Instance of the model corresponding to the current storage object
- and stored using the key name of the storage object.
-
-
-Forces header keys and values to be strings, i.e not unicode.
-
-The httplib module just concats the header keys and values in a way that may
-make the message header a unicode string, which, if it then tries to
-contatenate to a binary request body may result in a unicode decode error.
-
-Args:
- headers: dict, A dictionary of headers.
-
-Returns:
- The same dictionary but with all the keys converted to strings.
-
-
-Updates a URI with new query parameters.
-
-Args:
- uri: string, A valid URI, with potential existing query parameters.
- params: dict, A dictionary of query parameters.
-
-Returns:
- The same URI but with the new query parameters added.
-
-
-Save the provided GoogleCredentials to the well known file.
-
-Args:
- credentials:
- the credentials to be saved to the well known file;
- it should be an instance of GoogleCredentials
- well_known_file:
- the name of the file where the credentials are to be saved;
- this parameter is supposed to be used for testing only
-
-
-Ensure we have a crypto library, or throw CryptoUnavailableError.
-
-The oauth2client.crypt module requires either PyCrypto or PyOpenSSL
-to be available in order to function, but these are optional
-dependencies.
-
-
-Verifies a signed JWT id_token.
-
-This function requires PyOpenSSL and because of that it does not work on
-App Engine.
-
-Args:
- id_token: string, A Signed JWT.
- audience: string, The audience 'aud' that the token should be for.
- http: httplib2.Http, instance to use to make the HTTP request. Callers
- should supply an instance that has caching enabled.
- cert_uri: string, URI of the certificates in JSON format to
- verify the JWT against.
-
-Returns:
- The deserialized JSON in the JWT.
-
-Raises:
- oauth2client.crypt.AppIdentityError: if the JWT fails to verify.
- CryptoUnavailableError: if no crypto library is available.
-
-
-Parses response of an exchange token request.
-
-Most providers return JSON but some (e.g. Facebook) return a
-url-encoded string.
-
-Args:
- content: The body of a response
-
-Returns:
- Content as a dictionary object. Note that the dict could be empty,
- i.e. {}. That basically indicates a failure.
-
-
-Exchanges an authorization code for an OAuth2Credentials object.
-
-Args:
- client_id: string, client identifier.
- client_secret: string, client secret.
- scope: string or iterable of strings, scope(s) to request.
- code: string, An authroization code, most likely passed down from
- the client
- redirect_uri: string, this is generally set to 'postmessage' to match the
- redirect_uri that the client specified
- http: httplib2.Http, optional http instance to use to do the fetch
- token_uri: string, URI for token endpoint. For convenience
- defaults to Google's endpoints but any OAuth 2.0 provider can be used.
- auth_uri: string, URI for authorization endpoint. For convenience
- defaults to Google's endpoints but any OAuth 2.0 provider can be used.
- revoke_uri: string, URI for revoke endpoint. For convenience
- defaults to Google's endpoints but any OAuth 2.0 provider can be used.
- device_uri: string, URI for device authorization endpoint. For convenience
- defaults to Google's endpoints but any OAuth 2.0 provider can be used.
-
-Returns:
- An OAuth2Credentials object.
-
-Raises:
- FlowExchangeError if the authorization code cannot be exchanged for an
- access token
-
-
-Returns OAuth2Credentials from a clientsecrets file and an auth code.
-
-Will create the right kind of Flow based on the contents of the clientsecrets
-file or will raise InvalidClientSecretsError for unknown types of Flows.
-
-Args:
- filename: string, File name of clientsecrets.
- scope: string or iterable of strings, scope(s) to request.
- code: string, An authorization code, most likely passed down from
- the client
- message: string, A friendly string to display to the user if the
- clientsecrets file is missing or invalid. If message is provided then
- sys.exit will be called in the case of an error. If message in not
- provided then clientsecrets.InvalidClientSecretsError will be raised.
- redirect_uri: string, this is generally set to 'postmessage' to match the
- redirect_uri that the client specified
- http: httplib2.Http, optional http instance to use to do the fetch
- cache: An optional cache service client that implements get() and set()
- methods. See clientsecrets.loadfile() for details.
- device_uri: string, OAuth 2.0 device authorization endpoint
-
-Returns:
- An OAuth2Credentials object.
-
-Raises:
- FlowExchangeError if the authorization code cannot be exchanged for an
- access token
- UnknownClientSecretsFlowError if the file describes an unknown kind of Flow.
- clientsecrets.InvalidClientSecretsError if the clientsecrets file is
- invalid.
-
-
-Create a Flow from a clientsecrets file.
-
-Will create the right kind of Flow based on the contents of the clientsecrets
-file or will raise InvalidClientSecretsError for unknown types of Flows.
-
-Args:
- filename: string, File name of client secrets.
- scope: string or iterable of strings, scope(s) to request.
- redirect_uri: string, Either the string 'urn:ietf:wg:oauth:2.0:oob' for
- a non-web-based application, or a URI that handles the callback from
- the authorization server.
- message: string, A friendly string to display to the user if the
- clientsecrets file is missing or invalid. If message is provided then
- sys.exit will be called in the case of an error. If message in not
- provided then clientsecrets.InvalidClientSecretsError will be raised.
- cache: An optional cache service client that implements get() and set()
- methods. See clientsecrets.loadfile() for details.
- login_hint: string, Either an email address or domain. Passing this hint
- will either pre-fill the email box on the sign-in form or select the
- proper multi-login session, thereby simplifying the login flow.
- device_uri: string, URI for device authorization endpoint. For convenience
- defaults to Google's endpoints but any OAuth 2.0 provider can be used.
-
-Returns:
- A Flow object.
-
-Raises:
- UnknownClientSecretsFlowError if the file describes an unknown kind of Flow.
- clientsecrets.InvalidClientSecretsError if the clientsecrets file is
- invalid.
-
-
-'The Application Default Credentials are not available. They are avail
-able ' 'if running in Google Compute Engine. Otherwise, the environmen
-t variable '+ GOOGLE_APPLICATION_CREDENTIALS+ ' must be defined pointi
-ng to a file defining the credentials. See ' 'https://developers.googl
-e.com/accounts/docs/application-default-credentials' ' for more inform
-ation.'
-
- 1# Copyright 2014 Google Inc. All rights reserved.
- 2#
- 3# Licensed under the Apache License, Version 2.0 (the "License");
- 4# you may not use this file except in compliance with the License.
- 5# You may obtain a copy of the License at
- 6#
- 7# http://www.apache.org/licenses/LICENSE-2.0
- 8#
- 9# Unless required by applicable law or agreed to in writing, software
- 10# distributed under the License is distributed on an "AS IS" BASIS,
- 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- 12# See the License for the specific language governing permissions and
- 13# limitations under the License.
- 14
- 15"""An OAuth 2.0 client.
- 16
- 17Tools for interacting with OAuth 2.0 protected resources.
- 18"""
- 19
- 20__author__='jcgregorio@google.com (Joe Gregorio)'
- 21
- 22importbase64
- 23importcollections
- 24importcopy
- 25importdatetime
- 26importjson
- 27importlogging
- 28importos
- 29importsys
- 30importtime
- 31importsix
- 32fromsix.movesimporturllib
- 33
- 34importhttplib2
- 35fromoauth2clientimportclientsecrets
- 36fromoauth2clientimportGOOGLE_AUTH_URI
- 37fromoauth2clientimportGOOGLE_DEVICE_URI
- 38fromoauth2clientimportGOOGLE_REVOKE_URI
- 39fromoauth2clientimportGOOGLE_TOKEN_URI
- 40fromoauth2clientimportutil
- 41
- 42HAS_OPENSSL=False
- 43HAS_CRYPTO=False
- 44try:
- 45fromoauth2clientimportcrypt
- 46HAS_CRYPTO=True
- 47ifcrypt.OpenSSLVerifierisnotNone:
- 48HAS_OPENSSL=True
- 49exceptImportError:
- 50pass
- 51
- 52logger=logging.getLogger(__name__)
- 53
- 54# Expiry is stored in RFC3339 UTC format
- 55EXPIRY_FORMAT='%Y-%m-%dT%H:%M:%SZ'
- 56
- 57# Which certs to use to validate id_tokens received.
- 58ID_TOKEN_VERIFICATION_CERTS='https://www.googleapis.com/oauth2/v1/certs'
- 59# This symbol previously had a typo in the name; we keep the old name
- 60# around for now, but will remove it in the future.
- 61ID_TOKEN_VERIFICATON_CERTS=ID_TOKEN_VERIFICATION_CERTS
- 62
- 63# Constant to use for the out of band OAuth 2.0 flow.
- 64OOB_CALLBACK_URN='urn:ietf:wg:oauth:2.0:oob'
- 65
- 66# Google Data client libraries may need to set this to [401, 403].
- 67REFRESH_STATUS_CODES=[401]
- 68
- 69# The value representing user credentials.
- 70AUTHORIZED_USER='authorized_user'
- 71
- 72# The value representing service account credentials.
- 73SERVICE_ACCOUNT='service_account'
- 74
- 75# The environment variable pointing the file with local
- 76# Application Default Credentials.
- 77GOOGLE_APPLICATION_CREDENTIALS='GOOGLE_APPLICATION_CREDENTIALS'
- 78
- 79# The error message we show users when we can't find the Application
- 80# Default Credentials.
- 81ADC_HELP_MSG=(
- 82'The Application Default Credentials are not available. They are available '
- 83'if running in Google Compute Engine. Otherwise, the environment variable '
- 84+GOOGLE_APPLICATION_CREDENTIALS+
- 85' must be defined pointing to a file defining the credentials. See '
- 86'https://developers.google.com/accounts/docs/application-default-credentials'# pylint:disable=line-too-long
- 87' for more information.')
- 88
- 89# The access token along with the seconds in which it expires.
- 90AccessTokenInfo=collections.namedtuple(
- 91'AccessTokenInfo',['access_token','expires_in'])
-
159"""Base class for all Credentials objects.
- 160
- 161 Subclasses must define an authorize() method that applies the credentials to
- 162 an HTTP transport.
- 163
- 164 Subclasses must also specify a classmethod named 'from_json' that takes a JSON
- 165 string as input and returns an instantiated Credentials object.
- 166 """
- 167
- 168NON_SERIALIZED_MEMBERS=['store']
- 169
- 170
-
172"""Take an httplib2.Http instance (or equivalent) and authorizes it.
- 173
- 174 Authorizes it for the set of credentials, usually by replacing
- 175 http.request() with a method that adds in the appropriate headers and then
- 176 delegates to the original Http.request() method.
- 177
- 178 Args:
- 179 http: httplib2.Http, an http object to be used to make the refresh
- 180 request.
- 181 """
- 182_abstract()
-
186"""Forces a refresh of the access_token.
- 187
- 188 Args:
- 189 http: httplib2.Http, an http object to be used to make the refresh
- 190 request.
- 191 """
- 192_abstract()
-
196"""Revokes a refresh_token and makes the credentials void.
- 197
- 198 Args:
- 199 http: httplib2.Http, an http object to be used to make the revoke
- 200 request.
- 201 """
- 202_abstract()
-
206"""Add the authorization to the headers.
- 207
- 208 Args:
- 209 headers: dict, the headers to add the Authorization header to.
- 210 """
- 211_abstract()
-
214"""Utility function that creates JSON repr. of a Credentials object.
- 215
- 216 Args:
- 217 strip: array, An array of names of members to not include in the JSON.
- 218
- 219 Returns:
- 220 string, a JSON representation of this instance, suitable to pass to
- 221 from_json().
- 222 """
- 223t=type(self)
- 224d=copy.copy(self.__dict__)
- 225formemberinstrip:
- 226ifmemberind:
- 227deld[member]
- 228if(d.get('token_expiry')and
- 229isinstance(d['token_expiry'],datetime.datetime)):
- 230d['token_expiry']=d['token_expiry'].strftime(EXPIRY_FORMAT)
- 231# Add in information we will need later to reconsistitue this instance.
- 232d['_class']=t.__name__
- 233d['_module']=t.__module__
- 234forkey,valind.items():
- 235ifisinstance(val,bytes):
- 236d[key]=val.decode('utf-8')
- 237returnjson.dumps(d)
-
240"""Creating a JSON representation of an instance of Credentials.
- 241
- 242 Returns:
- 243 string, a JSON representation of this instance, suitable to pass to
- 244 from_json().
- 245 """
- 246returnself._to_json(Credentials.NON_SERIALIZED_MEMBERS)
-
250"""Utility class method to instantiate a Credentials subclass from a JSON
- 251 representation produced by to_json().
- 252
- 253 Args:
- 254 s: string, JSON from to_json().
- 255
- 256 Returns:
- 257 An instance of the subclass of Credentials that was serialized with
- 258 to_json().
- 259 """
- 260ifsix.PY3andisinstance(s,bytes):
- 261s=s.decode('utf-8')
- 262data=json.loads(s)
- 263# Find and call the right classmethod from_json() to restore the object.
- 264module=data['_module']
- 265try:
- 266m=__import__(module)
- 267exceptImportError:
- 268# In case there's an object from the old package structure, update it
- 269module=module.replace('.googleapiclient','')
- 270m=__import__(module)
- 271
- 272m=__import__(module,fromlist=module.split('.')[:-1])
- 273kls=getattr(m,data['_class'])
- 274from_json=getattr(kls,'from_json')
- 275returnfrom_json(s)
-
279"""Instantiate a Credentials object from a JSON description of it.
- 280
- 281 The JSON should have been produced by calling .to_json() on the object.
- 282
- 283 Args:
- 284 unused_data: dict, A deserialized JSON object.
- 285
- 286 Returns:
- 287 An instance of a Credentials subclass.
- 288 """
- 289returnCredentials()
-
298"""Base class for all Storage objects.
- 299
- 300 Store and retrieve a single credential. This class supports locking
- 301 such that multiple processes and threads can operate on a single
- 302 store.
- 303 """
- 304
-
321"""Retrieve credential.
- 322
- 323 The Storage lock must be held when this is called.
- 324
- 325 Returns:
- 326 oauth2client.client.Credentials
- 327 """
- 328_abstract()
-
331"""Write a credential.
- 332
- 333 The Storage lock must be held when this is called.
- 334
- 335 Args:
- 336 credentials: Credentials, the credentials to store.
- 337 """
- 338_abstract()
-
362"""Write a credential.
- 363
- 364 The Storage lock must be held when this is called.
- 365
- 366 Args:
- 367 credentials: Credentials, the credentials to store.
- 368 """
- 369self.acquire_lock()
- 370try:
- 371self.locked_put(credentials)
- 372finally:
- 373self.release_lock()
-
376"""Delete credential.
- 377
- 378 Frees any resources associated with storing the credential.
- 379 The Storage lock must *not* be held when this is called.
- 380
- 381 Returns:
- 382 None
- 383 """
- 384self.acquire_lock()
- 385try:
- 386returnself.locked_delete()
- 387finally:
- 388self.release_lock()
-
392"""Forces header keys and values to be strings, i.e not unicode.
- 393
- 394 The httplib module just concats the header keys and values in a way that may
- 395 make the message header a unicode string, which, if it then tries to
- 396 contatenate to a binary request body may result in a unicode decode error.
- 397
- 398 Args:
- 399 headers: dict, A dictionary of headers.
- 400
- 401 Returns:
- 402 The same dictionary but with all the keys converted to strings.
- 403 """
- 404clean={}
- 405try:
- 406fork,vinsix.iteritems(headers):
- 407clean[str(k)]=str(v)
- 408exceptUnicodeEncodeError:
- 409raiseNonAsciiHeaderError(k+': '+v)
- 410returnclean
-
414"""Updates a URI with new query parameters.
- 415
- 416 Args:
- 417 uri: string, A valid URI, with potential existing query parameters.
- 418 params: dict, A dictionary of query parameters.
- 419
- 420 Returns:
- 421 The same URI but with the new query parameters added.
- 422 """
- 423parts=urllib.parse.urlparse(uri)
- 424query_params=dict(urllib.parse.parse_qsl(parts.query))
- 425query_params.update(params)
- 426new_parts=parts._replace(query=urllib.parse.urlencode(query_params))
- 427returnurllib.parse.urlunparse(new_parts)
-
431"""Credentials object for OAuth 2.0.
- 432
- 433 Credentials can be applied to an httplib2.Http object using the authorize()
- 434 method, which then adds the OAuth 2.0 access token to each request.
- 435
- 436 OAuth2Credentials objects may be safely pickled and unpickled.
- 437 """
- 438
- 439@util.positional(8)
-
443"""Create an instance of OAuth2Credentials.
- 444
- 445 This constructor is not usually called by the user, instead
- 446 OAuth2Credentials objects are instantiated by the OAuth2WebServerFlow.
- 447
- 448 Args:
- 449 access_token: string, access token.
- 450 client_id: string, client identifier.
- 451 client_secret: string, client secret.
- 452 refresh_token: string, refresh token.
- 453 token_expiry: datetime, when the access_token expires.
- 454 token_uri: string, URI of token endpoint.
- 455 user_agent: string, The HTTP User-Agent to provide for this application.
- 456 revoke_uri: string, URI for revoke endpoint. Defaults to None; a token
- 457 can't be revoked if this is None.
- 458 id_token: object, The identity of the resource owner.
- 459 token_response: dict, the decoded response to the token request. None
- 460 if a token hasn't been requested yet. Stored because some providers
- 461 (e.g. wordpress.com) include extra fields that clients may want.
- 462
- 463 Notes:
- 464 store: callable, A callable that when passed a Credential
- 465 will store the credential back to where it came from.
- 466 This is needed to store the latest access_token if it
- 467 has expired and been refreshed.
- 468 """
- 469self.access_token=access_token
- 470self.client_id=client_id
- 471self.client_secret=client_secret
- 472self.refresh_token=refresh_token
- 473self.store=None
- 474self.token_expiry=token_expiry
- 475self.token_uri=token_uri
- 476self.user_agent=user_agent
- 477self.revoke_uri=revoke_uri
- 478self.id_token=id_token
- 479self.token_response=token_response
- 480
- 481# True if the credentials have been revoked or expired and can't be
- 482# refreshed.
- 483self.invalid=False
-
486"""Authorize an httplib2.Http instance with these credentials.
- 487
- 488 The modified http.request method will add authentication headers to each
- 489 request and will refresh access_tokens when a 401 is received on a
- 490 request. In addition the http.request method has a credentials property,
- 491 http.request.credentials, which is the Credentials object that authorized
- 492 it.
- 493
- 494 Args:
- 495 http: An instance of httplib2.Http
- 496 or something that acts like it.
- 497
- 498 Returns:
- 499 A modified instance of http that was passed in.
- 500
- 501 Example:
- 502
- 503 h = httplib2.Http()
- 504 h = credentials.authorize(h)
- 505
- 506 You can't create a new OAuth subclass of httplib2.Authentication
- 507 because it never gets passed the absolute URI, which is needed for
- 508 signing. So instead we have to overload 'request' with a closure
- 509 that adds in the Authorization header and then calls the original
- 510 version of 'request()'.
- 511 """
- 512request_orig=http.request
- 513
- 514# The closure that will replace 'httplib2.Http.request'.
- 515@util.positional(1)
- 516defnew_request(uri,method='GET',body=None,headers=None,
- 517redirections=httplib2.DEFAULT_MAX_REDIRECTS,
- 518connection_type=None):
- 519ifnotself.access_token:
- 520logger.info('Attempting refresh to obtain initial access_token')
- 521self._refresh(request_orig)
- 522
- 523# Clone and modify the request headers to add the appropriate
- 524# Authorization header.
- 525ifheadersisNone:
- 526headers={}
- 527else:
- 528headers=dict(headers)
- 529self.apply(headers)
- 530
- 531ifself.user_agentisnotNone:
- 532if'user-agent'inheaders:
- 533headers['user-agent']=self.user_agent+' '+headers['user-agent']
- 534else:
- 535headers['user-agent']=self.user_agent
- 536
- 537resp,content=request_orig(uri,method,body,clean_headers(headers),
- 538redirections,connection_type)
- 539
- 540ifresp.statusinREFRESH_STATUS_CODES:
- 541logger.info('Refreshing due to a %s',resp.status)
- 542self._refresh(request_orig)
- 543self.apply(headers)
- 544returnrequest_orig(uri,method,body,clean_headers(headers),
- 545redirections,connection_type)
- 546else:
- 547return(resp,content)
-
548
- 549# Replace the request method with our own closure.
- 550http.request=new_request
- 551
- 552# Set credentials as a property of the request method.
- 553setattr(http.request,'credentials',self)
- 554
- 555returnhttp
-
558"""Forces a refresh of the access_token.
- 559
- 560 Args:
- 561 http: httplib2.Http, an http object to be used to make the refresh
- 562 request.
- 563 """
- 564self._refresh(http.request)
-
567"""Revokes a refresh_token and makes the credentials void.
- 568
- 569 Args:
- 570 http: httplib2.Http, an http object to be used to make the revoke
- 571 request.
- 572 """
- 573self._revoke(http.request)
-
576"""Add the authorization to the headers.
- 577
- 578 Args:
- 579 headers: dict, the headers to add the Authorization header to.
- 580 """
- 581headers['Authorization']='Bearer '+self.access_token
-
641"""Return the access token and its expiration information.
- 642
- 643 If the token does not exist, get one.
- 644 If the token expired, refresh it.
- 645 """
- 646ifnotself.access_tokenorself.access_token_expired:
- 647ifnothttp:
- 648http=httplib2.Http()
- 649self.refresh(http)
- 650returnAccessTokenInfo(access_token=self.access_token,
- 651expires_in=self._expires_in())
-
654"""Set the Storage for the credential.
- 655
- 656 Args:
- 657 store: Storage, an implementation of Storage object.
- 658 This is needed to store the latest access_token if it
- 659 has expired and been refreshed. This implementation uses
- 660 locking to check for updates before updating the
- 661 access_token.
- 662 """
- 663self.store=store
-
666"""Return the number of seconds until this token expires.
- 667
- 668 If token_expiry is in the past, this method will return 0, meaning the
- 669 token has already expired.
- 670 If token_expiry is None, this method will return None. Note that returning
- 671 0 in such a case would not be fair: the token may still be valid;
- 672 we just don't know anything about it.
- 673 """
- 674ifself.token_expiry:
- 675now=datetime.datetime.utcnow()
- 676ifself.token_expiry>now:
- 677time_delta=self.token_expiry-now
- 678# TODO(orestica): return time_delta.total_seconds()
- 679# once dropping support for Python 2.6
- 680returntime_delta.days*86400+time_delta.seconds
- 681else:
- 682return0
-
700"""Generate the body that will be used in the refresh request."""
- 701body=urllib.parse.urlencode({
- 702'grant_type':'refresh_token',
- 703'client_id':self.client_id,
- 704'client_secret':self.client_secret,
- 705'refresh_token':self.refresh_token,
- 706})
- 707returnbody
-
710"""Generate the headers that will be used in the refresh request."""
- 711headers={
- 712'content-type':'application/x-www-form-urlencoded',
- 713}
- 714
- 715ifself.user_agentisnotNone:
- 716headers['user-agent']=self.user_agent
- 717
- 718returnheaders
-
721"""Refreshes the access_token.
- 722
- 723 This method first checks by reading the Storage object if available.
- 724 If a refresh is still needed, it holds the Storage lock until the
- 725 refresh is completed.
- 726
- 727 Args:
- 728 http_request: callable, a callable that matches the method signature of
- 729 httplib2.Http.request, used to make the refresh request.
- 730
- 731 Raises:
- 732 AccessTokenRefreshError: When the refresh fails.
- 733 """
- 734ifnotself.store:
- 735self._do_refresh_request(http_request)
- 736else:
- 737self.store.acquire_lock()
- 738try:
- 739new_cred=self.store.locked_get()
- 740if(new_credandnotnew_cred.invalidand
- 741new_cred.access_token!=self.access_token):
- 742logger.info('Updated access_token read from Storage')
- 743self._updateFromCredential(new_cred)
- 744else:
- 745self._do_refresh_request(http_request)
- 746finally:
- 747self.store.release_lock()
-
801"""Revokes the refresh_token and deletes the store if available.
- 802
- 803 Args:
- 804 http_request: callable, a callable that matches the method signature of
- 805 httplib2.Http.request, used to make the revoke request.
- 806 """
- 807self._do_revoke(http_request,self.refresh_token)
-
810"""Revokes the credentials and deletes the store if available.
- 811
- 812 Args:
- 813 http_request: callable, a callable that matches the method signature of
- 814 httplib2.Http.request, used to make the refresh request.
- 815 token: A string used as the token to be revoked. Can be either an
- 816 access_token or refresh_token.
- 817
- 818 Raises:
- 819 TokenRevokeError: If the revoke request does not return with a 200 OK.
- 820 """
- 821logger.info('Revoking token')
- 822query_params={'token':token}
- 823token_revoke_uri=_update_query_params(self.revoke_uri,query_params)
- 824resp,content=http_request(token_revoke_uri)
- 825ifresp.status==200:
- 826self.invalid=True
- 827else:
- 828error_msg='Invalid response %s.'%resp.status
- 829try:
- 830d=json.loads(content)
- 831if'error'ind:
- 832error_msg=d['error']
- 833except(TypeError,ValueError):
- 834pass
- 835raiseTokenRevokeError(error_msg)
- 836
- 837ifself.store:
- 838self.store.delete()
-
842"""Credentials object for OAuth 2.0.
- 843
- 844 Credentials can be applied to an httplib2.Http object using the
- 845 authorize() method, which then signs each request from that object
- 846 with the OAuth 2.0 access token. This set of credentials is for the
- 847 use case where you have acquired an OAuth 2.0 access_token from
- 848 another place such as a JavaScript client or another web
- 849 application, and wish to use it from Python. Because only the
- 850 access_token is present it can not be refreshed and will in time
- 851 expire.
- 852
- 853 AccessTokenCredentials objects may be safely pickled and unpickled.
- 854
- 855 Usage:
- 856 credentials = AccessTokenCredentials('<an access token>',
- 857 'my-user-agent/1.0')
- 858 http = httplib2.Http()
- 859 http = credentials.authorize(http)
- 860
- 861 Exceptions:
- 862 AccessTokenCredentialsExpired: raised when the access_token expires or is
- 863 revoked.
- 864 """
- 865
-
867"""Create an instance of OAuth2Credentials
- 868
- 869 This is one of the few types if Credentials that you should contrust,
- 870 Credentials objects are usually instantiated by a Flow.
- 871
- 872 Args:
- 873 access_token: string, access token.
- 874 user_agent: string, The HTTP User-Agent to provide for this application.
- 875 revoke_uri: string, URI for revoke endpoint. Defaults to None; a token
- 876 can't be revoked if this is None.
- 877 """
- 878super(AccessTokenCredentials,self).__init__(
- 879access_token,
- 880None,
- 881None,
- 882None,
- 883None,
- 884None,
- 885user_agent,
- 886revoke_uri=revoke_uri)
-
904"""Revokes the access_token and deletes the store if available.
- 905
- 906 Args:
- 907 http_request: callable, a callable that matches the method signature of
- 908 httplib2.Http.request, used to make the revoke request.
- 909 """
- 910self._do_revoke(http_request,self.access_token)
-
945"""Application Default Credentials for use in calling Google APIs.
- 946
- 947 The Application Default Credentials are being constructed as a function of
- 948 the environment where the code is being run.
- 949 More details can be found on this page:
- 950 https://developers.google.com/accounts/docs/application-default-credentials
- 951
- 952 Here is an example of how to use the Application Default Credentials for a
- 953 service that requires authentication:
- 954
- 955 <code>
- 956 from __future__ import print_function # unnecessary in python3
- 957 from googleapiclient.discovery import build
- 958 from oauth2client.client import GoogleCredentials
- 959
- 960 PROJECT = 'bamboo-machine-422' # replace this with one of your projects
- 961 ZONE = 'us-central1-a' # replace this with the zone you care about
- 962
- 963 credentials = GoogleCredentials.get_application_default()
- 964 service = build('compute', 'v1', credentials=credentials)
- 965
- 966 request = service.instances().list(project=PROJECT, zone=ZONE)
- 967 response = request.execute()
- 968
- 969 print(response)
- 970 </code>
- 971
- 972 A service that does not require authentication does not need credentials
- 973 to be passed in:
- 974
- 975 <code>
- 976 from googleapiclient.discovery import build
- 977
- 978 service = build('discovery', 'v1')
- 979
- 980 request = service.apis().list()
- 981 response = request.execute()
- 982
- 983 print(response)
- 984 </code>
- 985 """
- 986
-
990"""Create an instance of GoogleCredentials.
- 991
- 992 This constructor is not usually called by the user, instead
- 993 GoogleCredentials objects are instantiated by
- 994 GoogleCredentials.from_stream() or
- 995 GoogleCredentials.get_application_default().
- 996
- 997 Args:
- 998 access_token: string, access token.
- 999 client_id: string, client identifier.
-1000 client_secret: string, client secret.
-1001 refresh_token: string, refresh token.
-1002 token_expiry: datetime, when the access_token expires.
-1003 token_uri: string, URI of token endpoint.
-1004 user_agent: string, The HTTP User-Agent to provide for this application.
-1005 revoke_uri: string, URI for revoke endpoint.
-1006 Defaults to GOOGLE_REVOKE_URI; a token can't be revoked if this is None.
-1007 """
-1008super(GoogleCredentials,self).__init__(
-1009access_token,client_id,client_secret,refresh_token,token_expiry,
-1010token_uri,user_agent,revoke_uri=revoke_uri)
-
1013"""Whether this Credentials object is scopeless.
-1014
-1015 create_scoped(scopes) method needs to be called in order to create
-1016 a Credentials object for API calls.
-1017 """
-1018returnFalse
-
1029"""Get the fields and their values identifying the current credentials."""
-1030return{
-1031'type':'authorized_user',
-1032'client_id':self.client_id,
-1033'client_secret':self.client_secret,
-1034'refresh_token':self.refresh_token
-1035}
-
1084"""Create a Credentials object by reading the information from a given file.
-1085
-1086 It returns an object of type GoogleCredentials.
-1087
-1088 Args:
-1089 credential_filename: the path to the file from where the credentials
-1090 are to be read
-1091
-1092 Exceptions:
-1093 ApplicationDefaultCredentialsError: raised when the credentials fail
-1094 to be retrieved.
-1095 """
-1096
-1097ifcredential_filenameandos.path.isfile(credential_filename):
-1098try:
-1099return_get_application_default_credential_from_file(
-1100credential_filename)
-1101except(ApplicationDefaultCredentialsError,ValueError)aserror:
-1102extra_help=' (provided as parameter to the from_stream() method)'
-1103_raise_exception_for_reading_json(credential_filename,
-1104extra_help,
-1105error)
-1106else:
-1107raiseApplicationDefaultCredentialsError(
-1108'The parameter passed to the from_stream() '
-1109'method should point to a file.')
-
1113"""Save the provided GoogleCredentials to the well known file.
-1114
-1115 Args:
-1116 credentials:
-1117 the credentials to be saved to the well known file;
-1118 it should be an instance of GoogleCredentials
-1119 well_known_file:
-1120 the name of the file where the credentials are to be saved;
-1121 this parameter is supposed to be used for testing only
-1122 """
-1123# TODO(orestica): move this method to tools.py
-1124# once the argparse import gets fixed (it is not present in Python 2.6)
-1125
-1126ifwell_known_fileisNone:
-1127well_known_file=_get_well_known_file()
-1128
-1129credentials_data=credentials.serialization_data
-1130
-1131withopen(well_known_file,'w')asf:
-1132json.dump(credentials_data,f,sort_keys=True,indent=2,separators=(',',': '))
-
1151"""Get the well known file produced by command 'gcloud auth login'."""
-1152# TODO(orestica): Revisit this method once gcloud provides a better way
-1153# of pinpointing the exact location of the file.
-1154
-1155WELL_KNOWN_CREDENTIALS_FILE='application_default_credentials.json'
-1156CLOUDSDK_CONFIG_DIRECTORY='gcloud'
-1157
-1158ifos.name=='nt':
-1159try:
-1160default_config_path=os.path.join(os.environ['APPDATA'],
-1161CLOUDSDK_CONFIG_DIRECTORY)
-1162exceptKeyError:
-1163# This should never happen unless someone is really messing with things.
-1164drive=os.environ.get('SystemDrive','C:')
-1165default_config_path=os.path.join(drive,'\\',CLOUDSDK_CONFIG_DIRECTORY)
-1166else:
-1167default_config_path=os.path.join(os.path.expanduser('~'),
-1168'.config',
-1169CLOUDSDK_CONFIG_DIRECTORY)
-1170
-1171default_config_path=os.path.join(default_config_path,
-1172WELL_KNOWN_CREDENTIALS_FILE)
-1173
-1174returndefault_config_path
-
1248"""Abstract Credentials object used for OAuth 2.0 assertion grants.
-1249
-1250 This credential does not require a flow to instantiate because it
-1251 represents a two legged flow, and therefore has all of the required
-1252 information to generate and refresh its own access tokens. It must
-1253 be subclassed to generate the appropriate assertion string.
-1254
-1255 AssertionCredentials objects may be safely pickled and unpickled.
-1256 """
-1257
-1258@util.positional(2)
-
1263"""Constructor for AssertionFlowCredentials.
-1264
-1265 Args:
-1266 assertion_type: string, assertion type that will be declared to the auth
-1267 server
-1268 user_agent: string, The HTTP User-Agent to provide for this application.
-1269 token_uri: string, URI for token endpoint. For convenience
-1270 defaults to Google's endpoints but any OAuth 2.0 provider can be used.
-1271 revoke_uri: string, URI for revoke endpoint.
-1272 """
-1273super(AssertionCredentials,self).__init__(
-1274None,
-1275None,
-1276None,
-1277None,
-1278None,
-1279token_uri,
-1280user_agent,
-1281revoke_uri=revoke_uri)
-1282self.assertion_type=assertion_type
-
1301"""Revokes the access_token and deletes the store if available.
-1302
-1303 Args:
-1304 http_request: callable, a callable that matches the method signature of
-1305 httplib2.Http.request, used to make the revoke request.
-1306 """
-1307self._do_revoke(http_request,self.access_token)
-
1311"""Ensure we have a crypto library, or throw CryptoUnavailableError.
-1312
-1313 The oauth2client.crypt module requires either PyCrypto or PyOpenSSL
-1314 to be available in order to function, but these are optional
-1315 dependencies.
-1316 """
-1317ifnotHAS_CRYPTO:
-1318raiseCryptoUnavailableError('No crypto library available')
-
1322"""Credentials object used for OAuth 2.0 Signed JWT assertion grants.
-1323
-1324 This credential does not require a flow to instantiate because it
-1325 represents a two legged flow, and therefore has all of the required
-1326 information to generate and refresh its own access tokens.
-1327
-1328 SignedJwtAssertionCredentials requires either PyOpenSSL, or PyCrypto
-1329 2.6 or later. For App Engine you may also consider using
-1330 AppAssertionCredentials.
-1331 """
-1332
-1333MAX_TOKEN_LIFETIME_SECS=3600# 1 hour in seconds
-1334
-1335@util.positional(4)
-
1345"""Constructor for SignedJwtAssertionCredentials.
-1346
-1347 Args:
-1348 service_account_name: string, id for account, usually an email address.
-1349 private_key: string, private key in PKCS12 or PEM format.
-1350 scope: string or iterable of strings, scope(s) of the credentials being
-1351 requested.
-1352 private_key_password: string, password for private_key, unused if
-1353 private_key is in PEM format.
-1354 user_agent: string, HTTP User-Agent to provide for this application.
-1355 token_uri: string, URI for token endpoint. For convenience
-1356 defaults to Google's endpoints but any OAuth 2.0 provider can be used.
-1357 revoke_uri: string, URI for revoke endpoint.
-1358 kwargs: kwargs, Additional parameters to add to the JWT token, for
-1359 example sub=joe@xample.org.
-1360
-1361 Raises:
-1362 CryptoUnavailableError if no crypto library is available.
-1363 """
-1364_RequireCryptoOrDie()
-1365super(SignedJwtAssertionCredentials,self).__init__(
-1366None,
-1367user_agent=user_agent,
-1368token_uri=token_uri,
-1369revoke_uri=revoke_uri,
-1370)
-1371
-1372self.scope=util.scopes_to_string(scope)
-1373
-1374# Keep base64 encoded so it can be stored in JSON.
-1375self.private_key=base64.b64encode(private_key)
-1376ifisinstance(self.private_key,six.text_type):
-1377self.private_key=self.private_key.encode('utf-8')
-1378
-1379self.private_key_password=private_key_password
-1380self.service_account_name=service_account_name
-1381self.kwargs=kwargs
-
1400"""Generate the assertion that will be used in the request."""
-1401now=int(time.time())
-1402payload={
-1403'aud':self.token_uri,
-1404'scope':self.scope,
-1405'iat':now,
-1406'exp':now+SignedJwtAssertionCredentials.MAX_TOKEN_LIFETIME_SECS,
-1407'iss':self.service_account_name
-1408}
-1409payload.update(self.kwargs)
-1410logger.debug(str(payload))
-1411
-1412private_key=base64.b64decode(self.private_key)
-1413returncrypt.make_signed_jwt(crypt.Signer.from_string(
-1414private_key,self.private_key_password),payload)
-
1415
-1416# Only used in verify_id_token(), which is always calling to the same URI
-1417# for the certs.
-1418_cached_http=httplib2.Http(MemoryCache())
-
1423"""Verifies a signed JWT id_token.
-1424
-1425 This function requires PyOpenSSL and because of that it does not work on
-1426 App Engine.
-1427
-1428 Args:
-1429 id_token: string, A Signed JWT.
-1430 audience: string, The audience 'aud' that the token should be for.
-1431 http: httplib2.Http, instance to use to make the HTTP request. Callers
-1432 should supply an instance that has caching enabled.
-1433 cert_uri: string, URI of the certificates in JSON format to
-1434 verify the JWT against.
-1435
-1436 Returns:
-1437 The deserialized JSON in the JWT.
-1438
-1439 Raises:
-1440 oauth2client.crypt.AppIdentityError: if the JWT fails to verify.
-1441 CryptoUnavailableError: if no crypto library is available.
-1442 """
-1443_RequireCryptoOrDie()
-1444ifhttpisNone:
-1445http=_cached_http
-1446
-1447resp,content=http.request(cert_uri)
-1448
-1449ifresp.status==200:
-1450certs=json.loads(content.decode('utf-8'))
-1451returncrypt.verify_signed_jwt_with_certs(id_token,certs,audience)
-1452else:
-1453raiseVerifyJwtTokenError('Status code: %d'%resp.status)
-
1465"""Extract the JSON payload from a JWT.
-1466
-1467 Does the extraction w/o checking the signature.
-1468
-1469 Args:
-1470 id_token: string, OAuth 2.0 id_token.
-1471
-1472 Returns:
-1473 object, The deserialized JSON payload.
-1474 """
-1475segments=id_token.split('.')
-1476
-1477iflen(segments)!=3:
-1478raiseVerifyJwtTokenError(
-1479'Wrong number of segments in token: %s'%id_token)
-1480
-1481returnjson.loads(_urlsafe_b64decode(segments[1]).decode('utf-8'))
-
1485"""Parses response of an exchange token request.
-1486
-1487 Most providers return JSON but some (e.g. Facebook) return a
-1488 url-encoded string.
-1489
-1490 Args:
-1491 content: The body of a response
-1492
-1493 Returns:
-1494 Content as a dictionary object. Note that the dict could be empty,
-1495 i.e. {}. That basically indicates a failure.
-1496 """
-1497resp={}
-1498try:
-1499resp=json.loads(content.decode('utf-8'))
-1500exceptException:
-1501# different JSON libs raise different exceptions,
-1502# so we just do a catch-all here
-1503resp=dict(urllib.parse.parse_qsl(content))
-1504
-1505# some providers respond with 'expires', others with 'expires_in'
-1506ifrespand'expires'inresp:
-1507resp['expires_in']=resp.pop('expires')
-1508
-1509returnresp
-
1519"""Exchanges an authorization code for an OAuth2Credentials object.
-1520
-1521 Args:
-1522 client_id: string, client identifier.
-1523 client_secret: string, client secret.
-1524 scope: string or iterable of strings, scope(s) to request.
-1525 code: string, An authroization code, most likely passed down from
-1526 the client
-1527 redirect_uri: string, this is generally set to 'postmessage' to match the
-1528 redirect_uri that the client specified
-1529 http: httplib2.Http, optional http instance to use to do the fetch
-1530 token_uri: string, URI for token endpoint. For convenience
-1531 defaults to Google's endpoints but any OAuth 2.0 provider can be used.
-1532 auth_uri: string, URI for authorization endpoint. For convenience
-1533 defaults to Google's endpoints but any OAuth 2.0 provider can be used.
-1534 revoke_uri: string, URI for revoke endpoint. For convenience
-1535 defaults to Google's endpoints but any OAuth 2.0 provider can be used.
-1536 device_uri: string, URI for device authorization endpoint. For convenience
-1537 defaults to Google's endpoints but any OAuth 2.0 provider can be used.
-1538
-1539 Returns:
-1540 An OAuth2Credentials object.
-1541
-1542 Raises:
-1543 FlowExchangeError if the authorization code cannot be exchanged for an
-1544 access token
-1545 """
-1546flow=OAuth2WebServerFlow(client_id,client_secret,scope,
-1547redirect_uri=redirect_uri,user_agent=user_agent,
-1548auth_uri=auth_uri,token_uri=token_uri,
-1549revoke_uri=revoke_uri,device_uri=device_uri)
-1550
-1551credentials=flow.step2_exchange(code,http=http)
-1552returncredentials
-
1562"""Returns OAuth2Credentials from a clientsecrets file and an auth code.
-1563
-1564 Will create the right kind of Flow based on the contents of the clientsecrets
-1565 file or will raise InvalidClientSecretsError for unknown types of Flows.
-1566
-1567 Args:
-1568 filename: string, File name of clientsecrets.
-1569 scope: string or iterable of strings, scope(s) to request.
-1570 code: string, An authorization code, most likely passed down from
-1571 the client
-1572 message: string, A friendly string to display to the user if the
-1573 clientsecrets file is missing or invalid. If message is provided then
-1574 sys.exit will be called in the case of an error. If message in not
-1575 provided then clientsecrets.InvalidClientSecretsError will be raised.
-1576 redirect_uri: string, this is generally set to 'postmessage' to match the
-1577 redirect_uri that the client specified
-1578 http: httplib2.Http, optional http instance to use to do the fetch
-1579 cache: An optional cache service client that implements get() and set()
-1580 methods. See clientsecrets.loadfile() for details.
-1581 device_uri: string, OAuth 2.0 device authorization endpoint
-1582
-1583 Returns:
-1584 An OAuth2Credentials object.
-1585
-1586 Raises:
-1587 FlowExchangeError if the authorization code cannot be exchanged for an
-1588 access token
-1589 UnknownClientSecretsFlowError if the file describes an unknown kind of Flow.
-1590 clientsecrets.InvalidClientSecretsError if the clientsecrets file is
-1591 invalid.
-1592 """
-1593flow=flow_from_clientsecrets(filename,scope,message=message,cache=cache,
-1594redirect_uri=redirect_uri,
-1595device_uri=device_uri)
-1596credentials=flow.step2_exchange(code,http=http)
-1597returncredentials
-
1607"""Create a DeviceFlowInfo from a server response.
-1608
-1609 The response should be a dict containing entries as described
-1610 here:
-1611 http://tools.ietf.org/html/draft-ietf-oauth-v2-05#section-3.7.1
-1612 """
-1613# device_code, user_code, and verification_url are required.
-1614kwargs={
-1615'device_code':response['device_code'],
-1616'user_code':response['user_code'],
-1617}
-1618# The response may list the verification address as either
-1619# verification_url or verification_uri, so we check for both.
-1620verification_url=response.get(
-1621'verification_url',response.get('verification_uri'))
-1622ifverification_urlisNone:
-1623raiseOAuth2DeviceCodeError(
-1624'No verification_url provided in server response')
-1625kwargs['verification_url']=verification_url
-1626# expires_in and interval are optional.
-1627kwargs.update({
-1628'interval':response.get('interval'),
-1629'user_code_expiry':None,
-1630})
-1631if'expires_in'inresponse:
-1632kwargs['user_code_expiry']=datetime.datetime.now()+datetime.timedelta(
-1633seconds=int(response['expires_in']))
-1634
-1635returncls(**kwargs)
-
1638"""Does the Web Server Flow for OAuth 2.0.
-1639
-1640 OAuth2WebServerFlow objects may be safely pickled and unpickled.
-1641 """
-1642
-1643@util.positional(4)
-
1653"""Constructor for OAuth2WebServerFlow.
-1654
-1655 The kwargs argument is used to set extra query parameters on the
-1656 auth_uri. For example, the access_type and approval_prompt
-1657 query parameters can be set via kwargs.
-1658
-1659 Args:
-1660 client_id: string, client identifier.
-1661 client_secret: string client secret.
-1662 scope: string or iterable of strings, scope(s) of the credentials being
-1663 requested.
-1664 redirect_uri: string, Either the string 'urn:ietf:wg:oauth:2.0:oob' for
-1665 a non-web-based application, or a URI that handles the callback from
-1666 the authorization server.
-1667 user_agent: string, HTTP User-Agent to provide for this application.
-1668 auth_uri: string, URI for authorization endpoint. For convenience
-1669 defaults to Google's endpoints but any OAuth 2.0 provider can be used.
-1670 token_uri: string, URI for token endpoint. For convenience
-1671 defaults to Google's endpoints but any OAuth 2.0 provider can be used.
-1672 revoke_uri: string, URI for revoke endpoint. For convenience
-1673 defaults to Google's endpoints but any OAuth 2.0 provider can be used.
-1674 login_hint: string, Either an email address or domain. Passing this hint
-1675 will either pre-fill the email box on the sign-in form or select the
-1676 proper multi-login session, thereby simplifying the login flow.
-1677 device_uri: string, URI for device authorization endpoint. For convenience
-1678 defaults to Google's endpoints but any OAuth 2.0 provider can be used.
-1679 **kwargs: dict, The keyword arguments are all optional and required
-1680 parameters for the OAuth calls.
-1681 """
-1682self.client_id=client_id
-1683self.client_secret=client_secret
-1684self.scope=util.scopes_to_string(scope)
-1685self.redirect_uri=redirect_uri
-1686self.login_hint=login_hint
-1687self.user_agent=user_agent
-1688self.auth_uri=auth_uri
-1689self.token_uri=token_uri
-1690self.revoke_uri=revoke_uri
-1691self.device_uri=device_uri
-1692self.params={
-1693'access_type':'offline',
-1694'response_type':'code',
-1695}
-1696self.params.update(kwargs)
-
1700"""Returns a URI to redirect to the provider.
-1701
-1702 Args:
-1703 redirect_uri: string, Either the string 'urn:ietf:wg:oauth:2.0:oob' for
-1704 a non-web-based application, or a URI that handles the callback from
-1705 the authorization server. This parameter is deprecated, please move to
-1706 passing the redirect_uri in via the constructor.
-1707
-1708 Returns:
-1709 A URI as a string to redirect the user to begin the authorization flow.
-1710 """
-1711ifredirect_uriisnotNone:
-1712logger.warning((
-1713'The redirect_uri parameter for '
-1714'OAuth2WebServerFlow.step1_get_authorize_url is deprecated. Please '
-1715'move to passing the redirect_uri in via the constructor.'))
-1716self.redirect_uri=redirect_uri
-1717
-1718ifself.redirect_uriisNone:
-1719raiseValueError('The value of redirect_uri must not be None.')
-1720
-1721query_params={
-1722'client_id':self.client_id,
-1723'redirect_uri':self.redirect_uri,
-1724'scope':self.scope,
-1725}
-1726ifself.login_hintisnotNone:
-1727query_params['login_hint']=self.login_hint
-1728query_params.update(self.params)
-1729return_update_query_params(self.auth_uri,query_params)
-
1733"""Returns a user code and the verification URL where to enter it
-1734
-1735 Returns:
-1736 A user code as a string for the user to authorize the application
-1737 An URL as a string where the user has to enter the code
-1738 """
-1739ifself.device_uriisNone:
-1740raiseValueError('The value of device_uri must not be None.')
-1741
-1742body=urllib.urlencode({
-1743'client_id':self.client_id,
-1744'scope':self.scope,
-1745})
-1746headers={
-1747'content-type':'application/x-www-form-urlencoded',
-1748}
-1749
-1750ifself.user_agentisnotNone:
-1751headers['user-agent']=self.user_agent
-1752
-1753ifhttpisNone:
-1754http=httplib2.Http()
-1755
-1756resp,content=http.request(self.device_uri,method='POST',body=body,
-1757headers=headers)
-1758ifresp.status==200:
-1759try:
-1760flow_info=json.loads(content)
-1761exceptValueErrorase:
-1762raiseOAuth2DeviceCodeError(
-1763'Could not parse server response as JSON: "%s", error: "%s"'%(
-1764content,e))
-1765returnDeviceFlowInfo.FromResponse(flow_info)
-1766else:
-1767error_msg='Invalid response %s.'%resp.status
-1768try:
-1769d=json.loads(content)
-1770if'error'ind:
-1771error_msg+=' Error: %s'%d['error']
-1772exceptValueError:
-1773# Couldn't decode a JSON response, stick with the default message.
-1774pass
-1775raiseOAuth2DeviceCodeError(error_msg)
-
1779"""Exchanges a code for OAuth2Credentials.
-1780
-1781 Args:
-1782
-1783 code: string, a dict-like object, or None. For a non-device
-1784 flow, this is either the response code as a string, or a
-1785 dictionary of query parameters to the redirect_uri. For a
-1786 device flow, this should be None.
-1787 http: httplib2.Http, optional http instance to use when fetching
-1788 credentials.
-1789 device_flow_info: DeviceFlowInfo, return value from step1 in the
-1790 case of a device flow.
-1791
-1792 Returns:
-1793 An OAuth2Credentials object that can be used to authorize requests.
-1794
-1795 Raises:
-1796 FlowExchangeError: if a problem occurred exchanging the code for a
-1797 refresh_token.
-1798 ValueError: if code and device_flow_info are both provided or both
-1799 missing.
-1800
-1801 """
-1802ifcodeisNoneanddevice_flow_infoisNone:
-1803raiseValueError('No code or device_flow_info provided.')
-1804ifcodeisnotNoneanddevice_flow_infoisnotNone:
-1805raiseValueError('Cannot provide both code and device_flow_info.')
-1806
-1807ifcodeisNone:
-1808code=device_flow_info.device_code
-1809elifnotisinstance(code,basestring):
-1810if'code'notincode:
-1811raiseFlowExchangeError(code.get(
-1812'error','No code was supplied in the query parameters.'))
-1813code=code['code']
-1814
-1815post_data={
-1816'client_id':self.client_id,
-1817'client_secret':self.client_secret,
-1818'code':code,
-1819'scope':self.scope,
-1820}
-1821ifdevice_flow_infoisnotNone:
-1822post_data['grant_type']='http://oauth.net/grant_type/device/1.0'
-1823else:
-1824post_data['grant_type']='authorization_code'
-1825post_data['redirect_uri']=self.redirect_uri
-1826body=urllib.parse.urlencode(post_data)
-1827headers={
-1828'content-type':'application/x-www-form-urlencoded',
-1829}
-1830
-1831ifself.user_agentisnotNone:
-1832headers['user-agent']=self.user_agent
-1833
-1834ifhttpisNone:
-1835http=httplib2.Http()
-1836
-1837resp,content=http.request(self.token_uri,method='POST',body=body,
-1838headers=headers)
-1839d=_parse_exchange_token_response(content)
-1840ifresp.status==200and'access_token'ind:
-1841access_token=d['access_token']
-1842refresh_token=d.get('refresh_token',None)
-1843ifnotrefresh_token:
-1844logger.info(
-1845'Received token response with no refresh_token. Consider '
-1846"reauthenticating with approval_prompt='force'.")
-1847token_expiry=None
-1848if'expires_in'ind:
-1849token_expiry=datetime.datetime.utcnow()+datetime.timedelta(
-1850seconds=int(d['expires_in']))
-1851
-1852extracted_id_token=None
-1853if'id_token'ind:
-1854extracted_id_token=_extract_id_token(d['id_token'])
-1855
-1856logger.info('Successfully retrieved access token')
-1857returnOAuth2Credentials(access_token,self.client_id,
-1858self.client_secret,refresh_token,token_expiry,
-1859self.token_uri,self.user_agent,
-1860revoke_uri=self.revoke_uri,
-1861id_token=extracted_id_token,
-1862token_response=d)
-1863else:
-1864logger.info('Failed to retrieve access token: %s',content)
-1865if'error'ind:
-1866# you never know what those providers got to say
-1867error_msg=str(d['error'])
-1868else:
-1869error_msg='Invalid response: %s.'%str(resp.status)
-1870raiseFlowExchangeError(error_msg)
-
1877"""Create a Flow from a clientsecrets file.
-1878
-1879 Will create the right kind of Flow based on the contents of the clientsecrets
-1880 file or will raise InvalidClientSecretsError for unknown types of Flows.
-1881
-1882 Args:
-1883 filename: string, File name of client secrets.
-1884 scope: string or iterable of strings, scope(s) to request.
-1885 redirect_uri: string, Either the string 'urn:ietf:wg:oauth:2.0:oob' for
-1886 a non-web-based application, or a URI that handles the callback from
-1887 the authorization server.
-1888 message: string, A friendly string to display to the user if the
-1889 clientsecrets file is missing or invalid. If message is provided then
-1890 sys.exit will be called in the case of an error. If message in not
-1891 provided then clientsecrets.InvalidClientSecretsError will be raised.
-1892 cache: An optional cache service client that implements get() and set()
-1893 methods. See clientsecrets.loadfile() for details.
-1894 login_hint: string, Either an email address or domain. Passing this hint
-1895 will either pre-fill the email box on the sign-in form or select the
-1896 proper multi-login session, thereby simplifying the login flow.
-1897 device_uri: string, URI for device authorization endpoint. For convenience
-1898 defaults to Google's endpoints but any OAuth 2.0 provider can be used.
-1899
-1900 Returns:
-1901 A Flow object.
-1902
-1903 Raises:
-1904 UnknownClientSecretsFlowError if the file describes an unknown kind of Flow.
-1905 clientsecrets.InvalidClientSecretsError if the clientsecrets file is
-1906 invalid.
-1907 """
-1908try:
-1909client_type,client_info=clientsecrets.loadfile(filename,cache=cache)
-1910ifclient_typein(clientsecrets.TYPE_WEB,clientsecrets.TYPE_INSTALLED):
-1911constructor_kwargs={
-1912'redirect_uri':redirect_uri,
-1913'auth_uri':client_info['auth_uri'],
-1914'token_uri':client_info['token_uri'],
-1915'login_hint':login_hint,
-1916}
-1917revoke_uri=client_info.get('revoke_uri')
-1918ifrevoke_uriisnotNone:
-1919constructor_kwargs['revoke_uri']=revoke_uri
-1920ifdevice_uriisnotNone:
-1921constructor_kwargs['device_uri']=device_uri
-1922returnOAuth2WebServerFlow(
-1923client_info['client_id'],client_info['client_secret'],
-1924scope,**constructor_kwargs)
-1925
-1926exceptclientsecrets.InvalidClientSecretsError:
-1927ifmessage:
-1928sys.exit(message)
-1929else:
-1930raise
-1931else:
-1932raiseUnknownClientSecretsFlowError(
-1933'This OAuth 2.0 flow is unsupported: %r'%client_type)
-
-Credentials object for OAuth 2.0.
-
-Credentials can be applied to an httplib2.Http object using the
-authorize() method, which then signs each request from that object
-with the OAuth 2.0 access token. This set of credentials is for the
-use case where you have acquired an OAuth 2.0 access_token from
-another place such as a JavaScript client or another web
-application, and wish to use it from Python. Because only the
-access_token is present it can not be refreshed and will in time
-expire.
-
-AccessTokenCredentials objects may be safely pickled and unpickled.
-
-Usage:
- credentials = AccessTokenCredentials('<an access token>',
- 'my-user-agent/1.0')
- http = httplib2.Http()
- http = credentials.authorize(http)
-
-Exceptions:
- AccessTokenCredentialsExpired: raised when the access_token expires or is
- revoked.
-
-
-Create an instance of OAuth2Credentials
-
-This is one of the few types if Credentials that you should contrust,
-Credentials objects are usually instantiated by a Flow.
-
-Args:
- access_token: string, access token.
- user_agent: string, The HTTP User-Agent to provide for this application.
- revoke_uri: string, URI for revoke endpoint. Defaults to None; a token
- can't be revoked if this is None.
-
-
-Instantiate a Credentials object from a JSON description of it. The JSON
-should have been produced by calling .to_json() on the object.
-
-Args:
- data: dict, A deserialized JSON object.
-
-Returns:
- An instance of a Credentials subclass.
-
-
-Refreshes the access_token.
-
-This method first checks by reading the Storage object if available.
-If a refresh is still needed, it holds the Storage lock until the
-refresh is completed.
-
-Args:
- http_request: callable, a callable that matches the method signature of
- httplib2.Http.request, used to make the refresh request.
-
-Raises:
- AccessTokenRefreshError: When the refresh fails.
-
-
-Revokes the access_token and deletes the store if available.
-
-Args:
- http_request: callable, a callable that matches the method signature of
- httplib2.Http.request, used to make the revoke request.
-
-
-Abstract Credentials object used for OAuth 2.0 assertion grants.
-
-This credential does not require a flow to instantiate because it
-represents a two legged flow, and therefore has all of the required
-information to generate and refresh its own access tokens. It must
-be subclassed to generate the appropriate assertion string.
-
-AssertionCredentials objects may be safely pickled and unpickled.
-
-
-Constructor for AssertionFlowCredentials.
-
-Args:
- assertion_type: string, assertion type that will be declared to the auth
- server
- user_agent: string, The HTTP User-Agent to provide for this application.
- token_uri: string, URI for token endpoint. For convenience
- defaults to Google's endpoints but any OAuth 2.0 provider can be used.
- revoke_uri: string, URI for revoke endpoint.
-
-
-Revokes the access_token and deletes the store if available.
-
-Args:
- http_request: callable, a callable that matches the method signature of
- httplib2.Http.request, used to make the revoke request.
-
-
-Base class for all Credentials objects.
-
-Subclasses must define an authorize() method that applies the credentials to
-an HTTP transport.
-
-Subclasses must also specify a classmethod named 'from_json' that takes a JSON
-string as input and returns an instantiated Credentials object.
-
-
-Take an httplib2.Http instance (or equivalent) and authorizes it.
-
-Authorizes it for the set of credentials, usually by replacing
-http.request() with a method that adds in the appropriate headers and then
-delegates to the original Http.request() method.
-
-Args:
- http: httplib2.Http, an http object to be used to make the refresh
- request.
-
-
-Utility function that creates JSON repr. of a Credentials object.
-
-Args:
- strip: array, An array of names of members to not include in the JSON.
-
-Returns:
- string, a JSON representation of this instance, suitable to pass to
- from_json().
-
-
-Creating a JSON representation of an instance of Credentials.
-
-Returns:
- string, a JSON representation of this instance, suitable to pass to
- from_json().
-
-
-Utility class method to instantiate a Credentials subclass from a JSON
-representation produced by to_json().
-
-Args:
- s: string, JSON from to_json().
-
-Returns:
- An instance of the subclass of Credentials that was serialized with
- to_json().
-
-
-Instantiate a Credentials object from a JSON description of it.
-
-The JSON should have been produced by calling .to_json() on the object.
-
-Args:
- unused_data: dict, A deserialized JSON object.
-
-Returns:
- An instance of a Credentials subclass.
-
-
-Create a DeviceFlowInfo from a server response.
-
-The response should be a dict containing entries as described
-here:
- http://tools.ietf.org/html/draft-ietf-oauth-v2-05#section-3.7.1
-
-
-Application Default Credentials for use in calling Google APIs.
-
-The Application Default Credentials are being constructed as a function of
-the environment where the code is being run.
-More details can be found on this page:
-https://developers.google.com/accounts/docs/application-default-credentials
-
-Here is an example of how to use the Application Default Credentials for a
-service that requires authentication:
-
-<code>
-from __future__ import print_function # unnecessary in python3
-from googleapiclient.discovery import build
-from oauth2client.client import GoogleCredentials
-
-PROJECT = 'bamboo-machine-422' # replace this with one of your projects
-ZONE = 'us-central1-a' # replace this with the zone you care about
-
-credentials = GoogleCredentials.get_application_default()
-service = build('compute', 'v1', credentials=credentials)
-
-request = service.instances().list(project=PROJECT, zone=ZONE)
-response = request.execute()
-
-print(response)
-</code>
-
-A service that does not require authentication does not need credentials
-to be passed in:
-
-<code>
-from googleapiclient.discovery import build
-
-service = build('discovery', 'v1')
-
-request = service.apis().list()
-response = request.execute()
-
-print(response)
-</code>
-
-
-Create an instance of GoogleCredentials.
-
-This constructor is not usually called by the user, instead
-GoogleCredentials objects are instantiated by
-GoogleCredentials.from_stream() or
-GoogleCredentials.get_application_default().
-
-Args:
- access_token: string, access token.
- client_id: string, client identifier.
- client_secret: string, client secret.
- refresh_token: string, refresh token.
- token_expiry: datetime, when the access_token expires.
- token_uri: string, URI of token endpoint.
- user_agent: string, The HTTP User-Agent to provide for this application.
- revoke_uri: string, URI for revoke endpoint.
- Defaults to GOOGLE_REVOKE_URI; a token can't be revoked if this is None.
-
-
-Whether this Credentials object is scopeless.
-
-create_scoped(scopes) method needs to be called in order to create
-a Credentials object for API calls.
-
-
-Get the Application Default Credentials for the current environment.
-
-Exceptions:
- ApplicationDefaultCredentialsError: raised when the credentials fail
- to be retrieved.
-
-
-Create a Credentials object by reading the information from a given file.
-
-It returns an object of type GoogleCredentials.
-
-Args:
- credential_filename: the path to the file from where the credentials
- are to be read
-
-Exceptions:
- ApplicationDefaultCredentialsError: raised when the credentials fail
- to be retrieved.
-
-
-Credentials object for OAuth 2.0.
-
-Credentials can be applied to an httplib2.Http object using the authorize()
-method, which then adds the OAuth 2.0 access token to each request.
-
-OAuth2Credentials objects may be safely pickled and unpickled.
-
-
-Create an instance of OAuth2Credentials.
-
-This constructor is not usually called by the user, instead
-OAuth2Credentials objects are instantiated by the OAuth2WebServerFlow.
-
-Args:
- access_token: string, access token.
- client_id: string, client identifier.
- client_secret: string, client secret.
- refresh_token: string, refresh token.
- token_expiry: datetime, when the access_token expires.
- token_uri: string, URI of token endpoint.
- user_agent: string, The HTTP User-Agent to provide for this application.
- revoke_uri: string, URI for revoke endpoint. Defaults to None; a token
- can't be revoked if this is None.
- id_token: object, The identity of the resource owner.
- token_response: dict, the decoded response to the token request. None
- if a token hasn't been requested yet. Stored because some providers
- (e.g. wordpress.com) include extra fields that clients may want.
-
-Notes:
- store: callable, A callable that when passed a Credential
- will store the credential back to where it came from.
- This is needed to store the latest access_token if it
- has expired and been refreshed.
-
-
-Authorize an httplib2.Http instance with these credentials.
-
-The modified http.request method will add authentication headers to each
-request and will refresh access_tokens when a 401 is received on a
-request. In addition the http.request method has a credentials property,
-http.request.credentials, which is the Credentials object that authorized
-it.
-
-Args:
- http: An instance of httplib2.Http
- or something that acts like it.
-
-Returns:
- A modified instance of http that was passed in.
-
-Example:
-
- h = httplib2.Http()
- h = credentials.authorize(h)
-
-You can't create a new OAuth subclass of httplib2.Authentication
-because it never gets passed the absolute URI, which is needed for
-signing. So instead we have to overload 'request' with a closure
-that adds in the Authorization header and then calls the original
-version of 'request()'.
-
-
-Creating a JSON representation of an instance of Credentials.
-
-Returns:
- string, a JSON representation of this instance, suitable to pass to
- from_json().
-
-
-Instantiate a Credentials object from a JSON description of it. The JSON
-should have been produced by calling .to_json() on the object.
-
-Args:
- data: dict, A deserialized JSON object.
-
-Returns:
- An instance of a Credentials subclass.
-
-
-Set the Storage for the credential.
-
-Args:
- store: Storage, an implementation of Storage object.
- This is needed to store the latest access_token if it
- has expired and been refreshed. This implementation uses
- locking to check for updates before updating the
- access_token.
-
-
-Return the number of seconds until this token expires.
-
-If token_expiry is in the past, this method will return 0, meaning the
-token has already expired.
-If token_expiry is None, this method will return None. Note that returning
-0 in such a case would not be fair: the token may still be valid;
-we just don't know anything about it.
-
-
-Refreshes the access_token.
-
-This method first checks by reading the Storage object if available.
-If a refresh is still needed, it holds the Storage lock until the
-refresh is completed.
-
-Args:
- http_request: callable, a callable that matches the method signature of
- httplib2.Http.request, used to make the refresh request.
-
-Raises:
- AccessTokenRefreshError: When the refresh fails.
-
-
-Refresh the access_token using the refresh_token.
-
-Args:
- http_request: callable, a callable that matches the method signature of
- httplib2.Http.request, used to make the refresh request.
-
-Raises:
- AccessTokenRefreshError: When the refresh fails.
-
-
-Revokes the refresh_token and deletes the store if available.
-
-Args:
- http_request: callable, a callable that matches the method signature of
- httplib2.Http.request, used to make the revoke request.
-
-
-Revokes the credentials and deletes the store if available.
-
-Args:
- http_request: callable, a callable that matches the method signature of
- httplib2.Http.request, used to make the refresh request.
- token: A string used as the token to be revoked. Can be either an
- access_token or refresh_token.
-
-Raises:
- TokenRevokeError: If the revoke request does not return with a 200 OK.
-
-
-Constructor for OAuth2WebServerFlow.
-
-The kwargs argument is used to set extra query parameters on the
-auth_uri. For example, the access_type and approval_prompt
-query parameters can be set via kwargs.
-
-Args:
- client_id: string, client identifier.
- client_secret: string client secret.
- scope: string or iterable of strings, scope(s) of the credentials being
- requested.
- redirect_uri: string, Either the string 'urn:ietf:wg:oauth:2.0:oob' for
- a non-web-based application, or a URI that handles the callback from
- the authorization server.
- user_agent: string, HTTP User-Agent to provide for this application.
- auth_uri: string, URI for authorization endpoint. For convenience
- defaults to Google's endpoints but any OAuth 2.0 provider can be used.
- token_uri: string, URI for token endpoint. For convenience
- defaults to Google's endpoints but any OAuth 2.0 provider can be used.
- revoke_uri: string, URI for revoke endpoint. For convenience
- defaults to Google's endpoints but any OAuth 2.0 provider can be used.
- login_hint: string, Either an email address or domain. Passing this hint
- will either pre-fill the email box on the sign-in form or select the
- proper multi-login session, thereby simplifying the login flow.
- device_uri: string, URI for device authorization endpoint. For convenience
- defaults to Google's endpoints but any OAuth 2.0 provider can be used.
- **kwargs: dict, The keyword arguments are all optional and required
- parameters for the OAuth calls.
-
-
-Returns a URI to redirect to the provider.
-
-Args:
- redirect_uri: string, Either the string 'urn:ietf:wg:oauth:2.0:oob' for
- a non-web-based application, or a URI that handles the callback from
- the authorization server. This parameter is deprecated, please move to
- passing the redirect_uri in via the constructor.
-
-Returns:
- A URI as a string to redirect the user to begin the authorization flow.
-
-
-Returns a user code and the verification URL where to enter it
-
-Returns:
- A user code as a string for the user to authorize the application
- An URL as a string where the user has to enter the code
-
-
-Exchanges a code for OAuth2Credentials.
-
-Args:
-
- code: string, a dict-like object, or None. For a non-device
- flow, this is either the response code as a string, or a
- dictionary of query parameters to the redirect_uri. For a
- device flow, this should be None.
- http: httplib2.Http, optional http instance to use when fetching
- credentials.
- device_flow_info: DeviceFlowInfo, return value from step1 in the
- case of a device flow.
-
-Returns:
- An OAuth2Credentials object that can be used to authorize requests.
-
-Raises:
- FlowExchangeError: if a problem occurred exchanging the code for a
- refresh_token.
- ValueError: if code and device_flow_info are both provided or both
- missing.
-
-
-Credentials object used for OAuth 2.0 Signed JWT assertion grants.
-
-This credential does not require a flow to instantiate because it
-represents a two legged flow, and therefore has all of the required
-information to generate and refresh its own access tokens.
-
-SignedJwtAssertionCredentials requires either PyOpenSSL, or PyCrypto
-2.6 or later. For App Engine you may also consider using
-AppAssertionCredentials.
-
-
-Constructor for SignedJwtAssertionCredentials.
-
-Args:
- service_account_name: string, id for account, usually an email address.
- private_key: string, private key in PKCS12 or PEM format.
- scope: string or iterable of strings, scope(s) of the credentials being
- requested.
- private_key_password: string, password for private_key, unused if
- private_key is in PEM format.
- user_agent: string, HTTP User-Agent to provide for this application.
- token_uri: string, URI for token endpoint. For convenience
- defaults to Google's endpoints but any OAuth 2.0 provider can be used.
- revoke_uri: string, URI for revoke endpoint.
- kwargs: kwargs, Additional parameters to add to the JWT token, for
- example sub=joe@xample.org.
-
-Raises:
- CryptoUnavailableError if no crypto library is available.
-
-
-Instantiate a Credentials object from a JSON description of it. The JSON
-should have been produced by calling .to_json() on the object.
-
-Args:
- data: dict, A deserialized JSON object.
-
-Returns:
- An instance of a Credentials subclass.
-
-
-Base class for all Storage objects.
-
-Store and retrieve a single credential. This class supports locking
-such that multiple processes and threads can operate on a single
-store.
-
-
-Delete credential.
-
-Frees any resources associated with storing the credential.
-The Storage lock must *not* be held when this is called.
-
-Returns:
- None
-
-
-Utilities for reading OAuth 2.0 client secret files.
-
-A client_secrets.json file contains all the information needed to interact with
-an OAuth 2.0 protected service.
-
-
-Loading of client_secrets JSON file, optionally backed by a cache.
-
-Typical cache storage would be App Engine memcache service,
-but you can pass in any other cache client that implements
-these methods:
- - get(key, namespace=ns)
- - set(key, value, namespace=ns)
-
-Usage:
- # without caching
- client_type, client_info = loadfile('secrets.json')
- # using App Engine memcache service
- from google.appengine.api import memcache
- client_type, client_info = loadfile('secrets.json', cache=memcache)
-
-Args:
- filename: string, Path to a client_secrets.json file on a filesystem.
- cache: An optional cache service client that implements get() and set()
- methods. If not specified, the file is always being loaded from
- a filesystem.
-
-Raises:
- InvalidClientSecretsError: In case of a validation error or some
- I/O failure. Can happen only on cache miss.
-
-Returns:
- (client_type, client_info) tuple, as _loadfile() normally would.
- JSON contents is validated only during first load. Cache hits are not
- validated.
-
-
- 1# Copyright 2014 Google Inc. All rights reserved.
- 2#
- 3# Licensed under the Apache License, Version 2.0 (the "License");
- 4# you may not use this file except in compliance with the License.
- 5# You may obtain a copy of the License at
- 6#
- 7# http://www.apache.org/licenses/LICENSE-2.0
- 8#
- 9# Unless required by applicable law or agreed to in writing, software
- 10# distributed under the License is distributed on an "AS IS" BASIS,
- 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- 12# See the License for the specific language governing permissions and
- 13# limitations under the License.
- 14
- 15"""Utilities for reading OAuth 2.0 client secret files.
- 16
- 17A client_secrets.json file contains all the information needed to interact with
- 18an OAuth 2.0 protected service.
- 19"""
- 20
- 21__author__='jcgregorio@google.com (Joe Gregorio)'
- 22
- 23importjson
- 24importsix
- 25
- 26
- 27# Properties that make a client_secrets.json file valid.
- 28TYPE_WEB='web'
- 29TYPE_INSTALLED='installed'
- 30
- 31VALID_CLIENT={
- 32TYPE_WEB:{
- 33'required':[
- 34'client_id',
- 35'client_secret',
- 36'redirect_uris',
- 37'auth_uri',
- 38'token_uri',
- 39],
- 40'string':[
- 41'client_id',
- 42'client_secret',
- 43],
- 44},
- 45TYPE_INSTALLED:{
- 46'required':[
- 47'client_id',
- 48'client_secret',
- 49'redirect_uris',
- 50'auth_uri',
- 51'token_uri',
- 52],
- 53'string':[
- 54'client_id',
- 55'client_secret',
- 56],
- 57},
- 58}
- 59
- 60
-
110"""Loading of client_secrets JSON file, optionally backed by a cache.
-111
-112 Typical cache storage would be App Engine memcache service,
-113 but you can pass in any other cache client that implements
-114 these methods:
-115 - get(key, namespace=ns)
-116 - set(key, value, namespace=ns)
-117
-118 Usage:
-119 # without caching
-120 client_type, client_info = loadfile('secrets.json')
-121 # using App Engine memcache service
-122 from google.appengine.api import memcache
-123 client_type, client_info = loadfile('secrets.json', cache=memcache)
-124
-125 Args:
-126 filename: string, Path to a client_secrets.json file on a filesystem.
-127 cache: An optional cache service client that implements get() and set()
-128 methods. If not specified, the file is always being loaded from
-129 a filesystem.
-130
-131 Raises:
-132 InvalidClientSecretsError: In case of a validation error or some
-133 I/O failure. Can happen only on cache miss.
-134
-135 Returns:
-136 (client_type, client_info) tuple, as _loadfile() normally would.
-137 JSON contents is validated only during first load. Cache hits are not
-138 validated.
-139 """
-140_SECRET_NAMESPACE='oauth2client:secrets#ns'
-141
-142ifnotcache:
-143return_loadfile(filename)
-144
-145obj=cache.get(filename,namespace=_SECRET_NAMESPACE)
-146ifobjisNone:
-147client_type,client_info=_loadfile(filename)
-148obj={client_type:client_info}
-149cache.set(filename,obj,namespace=_SECRET_NAMESPACE)
-150
-151returnnext(six.iteritems(obj))
-
-Identify and extract PEM keys.
-
-Determines whether the given key is in the format of PEM key, and extracts
-the relevant part of the key if it is.
-
-Args:
- raw_key_input: The contents of a private key file (either PEM or PKCS12).
-
-Returns:
- string, The actual key if the contents are from a PEM file, or else None.
-
-
-Make a signed JWT.
-
-See http://self-issued.info/docs/draft-jones-json-web-token.html.
-
-Args:
- signer: crypt.Signer, Cryptographic signer.
- payload: dict, Dictionary of data to convert to JSON and then sign.
-
-Returns:
- string, The JWT for the payload.
-
-
-Verify a JWT against public certs.
-
-See http://self-issued.info/docs/draft-jones-json-web-token.html.
-
-Args:
- jwt: string, A JWT.
- certs: dict, Dictionary where values of public keys in PEM format.
- audience: string, The audience, 'aud', that this JWT should contain. If
- None then the JWT's 'aud' parameter is not verified.
-
-Returns:
- dict, The deserialized JSON payload in the JWT.
-
-Raises:
- AppIdentityError if any checks are failed.
-
-
- 1# -*- coding: utf-8 -*-
- 2#
- 3# Copyright 2014 Google Inc. All rights reserved.
- 4#
- 5# Licensed under the Apache License, Version 2.0 (the "License");
- 6# you may not use this file except in compliance with the License.
- 7# You may obtain a copy of the License at
- 8#
- 9# http://www.apache.org/licenses/LICENSE-2.0
- 10#
- 11# Unless required by applicable law or agreed to in writing, software
- 12# distributed under the License is distributed on an "AS IS" BASIS,
- 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- 14# See the License for the specific language governing permissions and
- 15# limitations under the License.
- 16"""Crypto-related routines for oauth2client."""
- 17
- 18importbase64
- 19importjson
- 20importlogging
- 21importsys
- 22importtime
- 23
- 24importsix
- 25
- 26
- 27CLOCK_SKEW_SECS=300# 5 minutes in seconds
- 28AUTH_TOKEN_LIFETIME_SECS=300# 5 minutes in seconds
- 29MAX_TOKEN_LIFETIME_SECS=86400# 1 day in seconds
- 30
- 31
- 32logger=logging.getLogger(__name__)
-
54"""Verifies a message against a signature.
- 55
- 56 Args:
- 57 message: string, The message to verify.
- 58 signature: string, The signature on the message.
- 59
- 60 Returns:
- 61 True if message was signed by the private key associated with the public
- 62 key that this object was constructed with.
- 63 """
- 64try:
- 65ifisinstance(message,six.text_type):
- 66message=message.encode('utf-8')
- 67crypto.verify(self._pubkey,signature,message,'sha256')
- 68returnTrue
- 69except:
- 70returnFalse
-
74"""Construct a Verified instance from a string.
- 75
- 76 Args:
- 77 key_pem: string, public key in PEM format.
- 78 is_x509_cert: bool, True if key_pem is an X509 cert, otherwise it is
- 79 expected to be an RSA key in PEM format.
- 80
- 81 Returns:
- 82 Verifier instance.
- 83
- 84 Raises:
- 85 OpenSSL.crypto.Error if the key_pem can't be parsed.
- 86 """
- 87ifis_x509_cert:
- 88pubkey=crypto.load_certificate(crypto.FILETYPE_PEM,key_pem)
- 89else:
- 90pubkey=crypto.load_privatekey(crypto.FILETYPE_PEM,key_pem)
- 91returnOpenSSLVerifier(pubkey)
-
106"""Signs a message.
-107
-108 Args:
-109 message: bytes, Message to be signed.
-110
-111 Returns:
-112 string, The signature of the message for the given key.
-113 """
-114ifisinstance(message,six.text_type):
-115message=message.encode('utf-8')
-116returncrypto.sign(self._key,message,'sha256')
-
165"""Verifies a message against a signature.
-166
-167 Args:
-168 message: string, The message to verify.
-169 signature: string, The signature on the message.
-170
-171 Returns:
-172 True if message was signed by the private key associated with the public
-173 key that this object was constructed with.
-174 """
-175try:
-176returnPKCS1_v1_5.new(self._pubkey).verify(
-177SHA256.new(message),signature)
-178except:
-179returnFalse
-
183"""Construct a Verified instance from a string.
-184
-185 Args:
-186 key_pem: string, public key in PEM format.
-187 is_x509_cert: bool, True if key_pem is an X509 cert, otherwise it is
-188 expected to be an RSA key in PEM format.
-189
-190 Returns:
-191 Verifier instance.
-192 """
-193ifis_x509_cert:
-194ifisinstance(key_pem,six.text_type):
-195key_pem=key_pem.encode('ascii')
-196pemLines=key_pem.replace(b' ',b'').split()
-197certDer=_urlsafe_b64decode(b''.join(pemLines[1:-1]))
-198certSeq=DerSequence()
-199certSeq.decode(certDer)
-200tbsSeq=DerSequence()
-201tbsSeq.decode(certSeq[0])
-202pubkey=RSA.importKey(tbsSeq[6])
-203else:
-204pubkey=RSA.importKey(key_pem)
-205returnPyCryptoVerifier(pubkey)
-
220"""Signs a message.
-221
-222 Args:
-223 message: string, Message to be signed.
-224
-225 Returns:
-226 string, The signature of the message for the given key.
-227 """
-228ifisinstance(message,six.text_type):
-229message=message.encode('utf-8')
-230returnPKCS1_v1_5.new(self._key).sign(SHA256.new(message))
-
234"""Construct a Signer instance from a string.
-235
-236 Args:
-237 key: string, private key in PEM format.
-238 password: string, password for private key file. Unused for PEM files.
-239
-240 Returns:
-241 Signer instance.
-242
-243 Raises:
-244 NotImplementedError if they key isn't in PEM format.
-245 """
-246parsed_pem_key=_parse_pem_key(key)
-247ifparsed_pem_key:
-248pkey=RSA.importKey(parsed_pem_key)
-249else:
-250raiseNotImplementedError(
-251'PKCS12 format is not supported by the PyCrypto library. '
-252'Try converting to a "PEM" '
-253'(openssl pkcs12 -in xxxxx.p12 -nodes -nocerts > privatekey.pem) '
-254'or using PyOpenSSL if native code is an option.')
-255returnPyCryptoSigner(pkey)
-
274"""Identify and extract PEM keys.
-275
-276 Determines whether the given key is in the format of PEM key, and extracts
-277 the relevant part of the key if it is.
-278
-279 Args:
-280 raw_key_input: The contents of a private key file (either PEM or PKCS12).
-281
-282 Returns:
-283 string, The actual key if the contents are from a PEM file, or else None.
-284 """
-285offset=raw_key_input.find(b'-----BEGIN ')
-286ifoffset!=-1:
-287returnraw_key_input[offset:]
-
309"""Make a signed JWT.
-310
-311 See http://self-issued.info/docs/draft-jones-json-web-token.html.
-312
-313 Args:
-314 signer: crypt.Signer, Cryptographic signer.
-315 payload: dict, Dictionary of data to convert to JSON and then sign.
-316
-317 Returns:
-318 string, The JWT for the payload.
-319 """
-320header={'typ':'JWT','alg':'RS256'}
-321
-322segments=[
-323_urlsafe_b64encode(_json_encode(header)),
-324_urlsafe_b64encode(_json_encode(payload)),
-325]
-326signing_input='.'.join(segments)
-327
-328signature=signer.sign(signing_input)
-329segments.append(_urlsafe_b64encode(signature))
-330
-331logger.debug(str(segments))
-332
-333return'.'.join(segments)
-
337"""Verify a JWT against public certs.
-338
-339 See http://self-issued.info/docs/draft-jones-json-web-token.html.
-340
-341 Args:
-342 jwt: string, A JWT.
-343 certs: dict, Dictionary where values of public keys in PEM format.
-344 audience: string, The audience, 'aud', that this JWT should contain. If
-345 None then the JWT's 'aud' parameter is not verified.
-346
-347 Returns:
-348 dict, The deserialized JSON payload in the JWT.
-349
-350 Raises:
-351 AppIdentityError if any checks are failed.
-352 """
-353segments=jwt.split('.')
-354
-355iflen(segments)!=3:
-356raiseAppIdentityError('Wrong number of segments in token: %s'%jwt)
-357signed='%s.%s'%(segments[0],segments[1])
-358
-359signature=_urlsafe_b64decode(segments[2])
-360
-361# Parse token.
-362json_body=_urlsafe_b64decode(segments[1])
-363try:
-364parsed=json.loads(json_body.decode('utf-8'))
-365except:
-366raiseAppIdentityError('Can\'t parse token: %s'%json_body)
-367
-368# Check signature.
-369verified=False
-370forpemincerts.values():
-371verifier=Verifier.from_string(pem,True)
-372ifverifier.verify(signed,signature):
-373verified=True
-374break
-375ifnotverified:
-376raiseAppIdentityError('Invalid token signature: %s'%jwt)
-377
-378# Check creation timestamp.
-379iat=parsed.get('iat')
-380ifiatisNone:
-381raiseAppIdentityError('No iat field in token: %s'%json_body)
-382earliest=iat-CLOCK_SKEW_SECS
-383
-384# Check expiration timestamp.
-385now=int(time.time())
-386exp=parsed.get('exp')
-387ifexpisNone:
-388raiseAppIdentityError('No exp field in token: %s'%json_body)
-389ifexp>=now+MAX_TOKEN_LIFETIME_SECS:
-390raiseAppIdentityError('exp field too far in future: %s'%json_body)
-391latest=exp+CLOCK_SKEW_SECS
-392
-393ifnow<earliest:
-394raiseAppIdentityError('Token used too early, %d < %d: %s'%
-395(now,earliest,json_body))
-396ifnow>latest:
-397raiseAppIdentityError('Token used too late, %d > %d: %s'%
-398(now,latest,json_body))
-399
-400# Check audience.
-401ifaudienceisnotNone:
-402aud=parsed.get('aud')
-403ifaudisNone:
-404raiseAppIdentityError('No aud field in token: %s'%json_body)
-405ifaud!=audience:
-406raiseAppIdentityError('Wrong recipient, %s != %s: %s'%
-407(aud,audience,json_body))
-408
-409returnparsed
-
- 1# Copyright 2014 Google Inc. All rights reserved.
- 2#
- 3# Licensed under the Apache License, Version 2.0 (the "License");
- 4# you may not use this file except in compliance with the License.
- 5# You may obtain a copy of the License at
- 6#
- 7# http://www.apache.org/licenses/LICENSE-2.0
- 8#
- 9# Unless required by applicable law or agreed to in writing, software
- 10# distributed under the License is distributed on an "AS IS" BASIS,
- 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- 12# See the License for the specific language governing permissions and
- 13# limitations under the License.
- 14
- 15"""OAuth 2.0 utilities for Django.
- 16
- 17Utilities for using OAuth 2.0 in conjunction with
- 18the Django datastore.
- 19"""
- 20
- 21__author__='jcgregorio@google.com (Joe Gregorio)'
- 22
- 23importoauth2client
- 24importbase64
- 25importpickle
- 26
- 27fromdjango.dbimportmodels
- 28fromoauth2client.clientimportStorageasBaseStorage
- 29
-
81"""Store and retrieve a single credential to and from
- 82 the datastore.
- 83
- 84 This Storage helper presumes the Credentials
- 85 have been stored as a CredenialsField
- 86 on a db model class.
- 87 """
- 88
-
90"""Constructor for Storage.
- 91
- 92 Args:
- 93 model: db.Model, model class
- 94 key_name: string, key name for the entity that has the credentials
- 95 key_value: string, key value for the entity that has the credentials
- 96 property_name: string, name of the property that is an CredentialsProperty
- 97 """
- 98self.model_class=model_class
- 99self.key_name=key_name
-100self.key_value=key_value
-101self.property_name=property_name
-
120"""Write a Credentials to the datastore.
-121
-122 Args:
-123 credentials: Credentials, the credentials to store.
-124 overwrite: Boolean, indicates whether you would like these credentials to
-125 overwrite any existing stored credentials.
-126 """
-127args={self.key_name:self.key_value}
-128
-129ifoverwrite:
-130entity,unused_is_new=self.model_class.objects.get_or_create(**args)
-131else:
-132entity=self.model_class(**args)
-133
-134setattr(entity,self.property_name,credentials)
-135entity.save()
-
138"""Delete Credentials from the datastore."""
-139
-140query={self.key_name:self.key_value}
-141entities=self.model_class.objects.filter(**query).delete()
-
-Store and retrieve a single credential to and from
-the datastore.
-
-This Storage helper presumes the Credentials
-have been stored as a CredenialsField
-on a db model class.
-
-
-Constructor for Storage.
-
-Args:
- model: db.Model, model class
- key_name: string, key name for the entity that has the credentials
- key_value: string, key value for the entity that has the credentials
- property_name: string, name of the property that is an CredentialsProperty
-
-
-Write a Credentials to the datastore.
-
-Args:
- credentials: Credentials, the credentials to store.
- overwrite: Boolean, indicates whether you would like these credentials to
- overwrite any existing stored credentials.
-
-
- 1# Copyright 2014 Google Inc. All rights reserved.
- 2#
- 3# Licensed under the Apache License, Version 2.0 (the "License");
- 4# you may not use this file except in compliance with the License.
- 5# You may obtain a copy of the License at
- 6#
- 7# http://www.apache.org/licenses/LICENSE-2.0
- 8#
- 9# Unless required by applicable law or agreed to in writing, software
- 10# distributed under the License is distributed on an "AS IS" BASIS,
- 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- 12# See the License for the specific language governing permissions and
- 13# limitations under the License.
- 14
- 15"""Utilities for OAuth.
- 16
- 17Utilities for making it easier to work with OAuth 2.0
- 18credentials.
- 19"""
- 20
- 21__author__='jcgregorio@google.com (Joe Gregorio)'
- 22
- 23importos
- 24importthreading
- 25
- 26fromoauth2client.clientimportCredentials
- 27fromoauth2client.clientimportStorageasBaseStorage
- 28
- 29
-
53"""Release the Storage lock.
- 54
- 55 Trying to release a lock that isn't held will result in a
- 56 RuntimeError.
- 57 """
- 58self._lock.release()
-
87"""Create an empty file if necessary.
- 88
- 89 This method will not initialize the file. Instead it implements a
- 90 simple version of "touch" to ensure the file has been created.
- 91 """
- 92ifnotos.path.exists(self._filename):
- 93old_umask=os.umask(0o177)
- 94try:
- 95open(self._filename,'a+b').close()
- 96finally:
- 97os.umask(old_umask)
-
100"""Write Credentials to file.
-101
-102 Args:
-103 credentials: Credentials, the credentials to store.
-104
-105 Raises:
-106 CredentialsFileSymbolicLinkError if the file is a symbolic link.
-107 """
-108
-109self._create_file_if_needed()
-110self._validate_file()
-111f=open(self._filename,'w')
-112f.write(credentials.to_json())
-113f.close()
-
-Retrieve Credential from file.
-
-Returns:
- oauth2client.client.Credentials
-
-Raises:
- CredentialsFileSymbolicLinkError if the file is a symbolic link.
-
-
-Create an empty file if necessary.
-
-This method will not initialize the file. Instead it implements a
-simple version of "touch" to ensure the file has been created.
-
-
-Write Credentials to file.
-
-Args:
- credentials: Credentials, the credentials to store.
-
-Raises:
- CredentialsFileSymbolicLinkError if the file is a symbolic link.
-
-
- 1# Copyright 2014 Google Inc. All rights reserved.
- 2#
- 3# Licensed under the Apache License, Version 2.0 (the "License");
- 4# you may not use this file except in compliance with the License.
- 5# You may obtain a copy of the License at
- 6#
- 7# http://www.apache.org/licenses/LICENSE-2.0
- 8#
- 9# Unless required by applicable law or agreed to in writing, software
- 10# distributed under the License is distributed on an "AS IS" BASIS,
- 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- 12# See the License for the specific language governing permissions and
- 13# limitations under the License.
- 14
- 15"""Utilities for Google Compute Engine
- 16
- 17Utilities for making it easier to use OAuth 2.0 on Google Compute Engine.
- 18"""
- 19
- 20__author__='jcgregorio@google.com (Joe Gregorio)'
- 21
- 22importjson
- 23importlogging
- 24fromsix.movesimporturllib
- 25
- 26fromoauth2clientimportutil
- 27fromoauth2client.clientimportAccessTokenRefreshError
- 28fromoauth2client.clientimportAssertionCredentials
- 29
- 30logger=logging.getLogger(__name__)
- 31
- 32# URI Template for the endpoint that returns access_tokens.
- 33META=('http://metadata.google.internal/0.1/meta-data/service-accounts/'
- 34'default/acquire{?scope}')
-
38"""Credentials object for Compute Engine Assertion Grants
- 39
- 40 This object will allow a Compute Engine instance to identify itself to
- 41 Google and other OAuth 2.0 servers that can verify assertions. It can be used
- 42 for the purpose of accessing data stored under an account assigned to the
- 43 Compute Engine instance itself.
- 44
- 45 This credential does not require a flow to instantiate because it represents
- 46 a two legged flow, and therefore has all of the required information to
- 47 generate and refresh its own access tokens.
- 48 """
- 49
- 50@util.positional(2)
-
52"""Constructor for AppAssertionCredentials
- 53
- 54 Args:
- 55 scope: string or iterable of strings, scope(s) of the credentials being
- 56 requested.
- 57 """
- 58self.scope=util.scopes_to_string(scope)
- 59self.kwargs=kwargs
- 60
- 61# Assertion type is no longer used, but still in the parent class signature.
- 62super(AppAssertionCredentials,self).__init__(None)
-
70"""Refreshes the access_token.
- 71
- 72 Skip all the storage hoops and just refresh using the API.
- 73
- 74 Args:
- 75 http_request: callable, a callable that matches the method signature of
- 76 httplib2.Http.request, used to make the refresh request.
- 77
- 78 Raises:
- 79 AccessTokenRefreshError: When the refresh fails.
- 80 """
- 81query='?scope=%s'%urllib.parse.quote(self.scope,'')
- 82uri=META.replace('{?scope}',query)
- 83response,content=http_request(uri)
- 84ifresponse.status==200:
- 85try:
- 86d=json.loads(content)
- 87exceptExceptionase:
- 88raiseAccessTokenRefreshError(str(e))
- 89self.access_token=d['accessToken']
- 90else:
- 91ifresponse.status==404:
- 92content+=(' This can occur if a VM was created'
- 93' with no service account or scopes.')
- 94raiseAccessTokenRefreshError(content)
-
-Credentials object for Compute Engine Assertion Grants
-
-This object will allow a Compute Engine instance to identify itself to
-Google and other OAuth 2.0 servers that can verify assertions. It can be used
-for the purpose of accessing data stored under an account assigned to the
-Compute Engine instance itself.
-
-This credential does not require a flow to instantiate because it represents
-a two legged flow, and therefore has all of the required information to
-generate and refresh its own access tokens.
-
-
-Instantiate a Credentials object from a JSON description of it. The JSON
-should have been produced by calling .to_json() on the object.
-
-Args:
- data: dict, A deserialized JSON object.
-
-Returns:
- An instance of a Credentials subclass.
-
-
-Refreshes the access_token.
-
-Skip all the storage hoops and just refresh using the API.
-
-Args:
- http_request: callable, a callable that matches the method signature of
- httplib2.Http.request, used to make the refresh request.
-
-Raises:
- AccessTokenRefreshError: When the refresh fails.
-
-
-Whether this Credentials object is scopeless.
-
-create_scoped(scopes) method needs to be called in order to create
-a Credentials object for API calls.
-
-
- 1# Copyright 2014 Google Inc. All rights reserved.
- 2#
- 3# Licensed under the Apache License, Version 2.0 (the "License");
- 4# you may not use this file except in compliance with the License.
- 5# You may obtain a copy of the License at
- 6#
- 7# http://www.apache.org/licenses/LICENSE-2.0
- 8#
- 9# Unless required by applicable law or agreed to in writing, software
- 10# distributed under the License is distributed on an "AS IS" BASIS,
- 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- 12# See the License for the specific language governing permissions and
- 13# limitations under the License.
- 14
- 15"""A keyring based Storage.
- 16
- 17A Storage for Credentials that uses the keyring module.
- 18"""
- 19
- 20__author__='jcgregorio@google.com (Joe Gregorio)'
- 21
- 22importthreading
- 23
- 24importkeyring
- 25
- 26fromoauth2client.clientimportCredentials
- 27fromoauth2client.clientimportStorageasBaseStorage
- 28
- 29
-
31"""Store and retrieve a single credential to and from the keyring.
- 32
- 33 To use this module you must have the keyring module installed. See
- 34 <http://pypi.python.org/pypi/keyring/>. This is an optional module and is not
- 35 installed with oauth2client by default because it does not work on all the
- 36 platforms that oauth2client supports, such as Google App Engine.
- 37
- 38 The keyring module <http://pypi.python.org/pypi/keyring/> is a cross-platform
- 39 library for access the keyring capabilities of the local system. The user will
- 40 be prompted for their keyring password when this module is used, and the
- 41 manner in which the user is prompted will vary per platform.
- 42
- 43 Usage:
- 44 from oauth2client.keyring_storage import Storage
- 45
- 46 s = Storage('name_of_application', 'user1')
- 47 credentials = s.get()
- 48
- 49 """
- 50
-
52"""Constructor.
- 53
- 54 Args:
- 55 service_name: string, The name of the service under which the credentials
- 56 are stored.
- 57 user_name: string, The name of the user to store credentials for.
- 58 """
- 59self._service_name=service_name
- 60self._user_name=user_name
- 61self._lock=threading.Lock()
-
70"""Release the Storage lock.
- 71
- 72 Trying to release a lock that isn't held will result in a
- 73 RuntimeError.
- 74 """
- 75self._lock.release()
-
-Store and retrieve a single credential to and from the keyring.
-
-To use this module you must have the keyring module installed. See
-<http://pypi.python.org/pypi/keyring/>. This is an optional module and is not
-installed with oauth2client by default because it does not work on all the
-platforms that oauth2client supports, such as Google App Engine.
-
-The keyring module <http://pypi.python.org/pypi/keyring/> is a cross-platform
-library for access the keyring capabilities of the local system. The user will
-be prompted for their keyring password when this module is used, and the
-manner in which the user is prompted will vary per platform.
-
-Usage:
- from oauth2client.keyring_storage import Storage
-
- s = Storage('name_of_application', 'user1')
- credentials = s.get()
-
-
-Constructor.
-
-Args:
- service_name: string, The name of the service under which the credentials
- are stored.
- user_name: string, The name of the user to store credentials for.
-
-
-Locked file interface that should work on Unix and Windows pythons.
-
-This module first tries to use fcntl locking to ensure serialized access
-to a file, then falls back on a lock file if that is unavialable.
-
-Usage:
- f = LockedFile('filename', 'r+b', 'rb')
- f.open_and_lock()
- if f.is_locked():
- print('Acquired filename with r+b mode')
- f.file_handle().write('locked data')
- else:
- print('Acquired filename with rb mode')
- f.unlock_and_close()
-
-
- 1# Copyright 2014 Google Inc. All rights reserved.
- 2#
- 3# Licensed under the Apache License, Version 2.0 (the "License");
- 4# you may not use this file except in compliance with the License.
- 5# You may obtain a copy of the License at
- 6#
- 7# http://www.apache.org/licenses/LICENSE-2.0
- 8#
- 9# Unless required by applicable law or agreed to in writing, software
- 10# distributed under the License is distributed on an "AS IS" BASIS,
- 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- 12# See the License for the specific language governing permissions and
- 13# limitations under the License.
- 14
- 15"""Locked file interface that should work on Unix and Windows pythons.
- 16
- 17This module first tries to use fcntl locking to ensure serialized access
- 18to a file, then falls back on a lock file if that is unavialable.
- 19
- 20Usage:
- 21 f = LockedFile('filename', 'r+b', 'rb')
- 22 f.open_and_lock()
- 23 if f.is_locked():
- 24 print('Acquired filename with r+b mode')
- 25 f.file_handle().write('locked data')
- 26 else:
- 27 print('Acquired filename with rb mode')
- 28 f.unlock_and_close()
- 29"""
- 30
- 31from__future__importprint_function
- 32
- 33__author__='cache@google.com (David T McWherter)'
- 34
- 35importerrno
- 36importlogging
- 37importos
- 38importtime
- 39
- 40fromoauth2clientimportutil
- 41
- 42logger=logging.getLogger(__name__)
-
90"""Open the file and lock it.
- 91
- 92 Args:
- 93 timeout: float, How long to try to lock for.
- 94 delay: float, How long to wait between retries.
- 95 """
- 96pass
-
107"""Open the file and lock it.
-108
-109 Tries to create a .lock file next to the file we're trying to open.
-110
-111 Args:
-112 timeout: float, How long to try to lock for.
-113 delay: float, How long to wait between retries.
-114
-115 Raises:
-116 AlreadyLockedException: if the lock is already acquired.
-117 IOError: if the open fails.
-118 CredentialsFileSymbolicLinkError if the file is a symbolic link.
-119 """
-120ifself._locked:
-121raiseAlreadyLockedException('File %s is already locked'%
-122self._filename)
-123self._locked=False
-124
-125validate_file(self._filename)
-126try:
-127self._fh=open(self._filename,self._mode)
-128exceptIOErrorase:
-129# If we can't access with _mode, try _fallback_mode and don't lock.
-130ife.errno==errno.EACCES:
-131self._fh=open(self._filename,self._fallback_mode)
-132return
-133
-134lock_filename=self._posix_lockfile(self._filename)
-135start_time=time.time()
-136whileTrue:
-137try:
-138self._lock_fd=os.open(lock_filename,
-139os.O_CREAT|os.O_EXCL|os.O_RDWR)
-140self._locked=True
-141break
-142
-143exceptOSErrorase:
-144ife.errno!=errno.EEXIST:
-145raise
-146if(time.time()-start_time)>=timeout:
-147logger.warn('Could not acquire lock %s in %s seconds',
-148lock_filename,timeout)
-149# Close the file and open in fallback_mode.
-150ifself._fh:
-151self._fh.close()
-152self._fh=open(self._filename,self._fallback_mode)
-153return
-154time.sleep(delay)
-
157"""Unlock a file by removing the .lock file, and close the handle."""
-158ifself._locked:
-159lock_filename=self._posix_lockfile(self._filename)
-160os.close(self._lock_fd)
-161os.unlink(lock_filename)
-162self._locked=False
-163self._lock_fd=None
-164ifself._fh:
-165self._fh.close()
-
179"""Open the file and lock it.
-180
-181 Args:
-182 timeout: float, How long to try to lock for.
-183 delay: float, How long to wait between retries
-184
-185 Raises:
-186 AlreadyLockedException: if the lock is already acquired.
-187 IOError: if the open fails.
-188 CredentialsFileSymbolicLinkError if the file is a symbolic link.
-189 """
-190ifself._locked:
-191raiseAlreadyLockedException('File %s is already locked'%
-192self._filename)
-193start_time=time.time()
-194
-195validate_file(self._filename)
-196try:
-197self._fh=open(self._filename,self._mode)
-198exceptIOErrorase:
-199# If we can't access with _mode, try _fallback_mode and don't lock.
-200ife.errnoin(errno.EPERM,errno.EACCES):
-201self._fh=open(self._filename,self._fallback_mode)
-202return
-203
-204# We opened in _mode, try to lock the file.
-205whileTrue:
-206try:
-207fcntl.lockf(self._fh.fileno(),fcntl.LOCK_EX)
-208self._locked=True
-209return
-210exceptIOErrorase:
-211# If not retrying, then just pass on the error.
-212iftimeout==0:
-213raisee
-214ife.errno!=errno.EACCES:
-215raisee
-216# We could not acquire the lock. Try again.
-217if(time.time()-start_time)>=timeout:
-218logger.warn('Could not lock %s in %s seconds',
-219self._filename,timeout)
-220ifself._fh:
-221self._fh.close()
-222self._fh=open(self._filename,self._fallback_mode)
-223return
-224time.sleep(delay)
-
227"""Close and unlock the file using the fcntl.lockf primitive."""
-228ifself._locked:
-229fcntl.lockf(self._fh.fileno(),fcntl.LOCK_UN)
-230self._locked=False
-231ifself._fh:
-232self._fh.close()
-
243"""Open, lock, and unlock a file using windows primitives."""
-244
-245# Error #33:
-246# 'The process cannot access the file because another process'
-247FILE_IN_USE_ERROR=33
-248
-249# Error #158:
-250# 'The segment is already unlocked.'
-251FILE_ALREADY_UNLOCKED_ERROR=158
-252
-
254"""Open the file and lock it.
-255
-256 Args:
-257 timeout: float, How long to try to lock for.
-258 delay: float, How long to wait between retries
-259
-260 Raises:
-261 AlreadyLockedException: if the lock is already acquired.
-262 IOError: if the open fails.
-263 CredentialsFileSymbolicLinkError if the file is a symbolic link.
-264 """
-265ifself._locked:
-266raiseAlreadyLockedException('File %s is already locked'%
-267self._filename)
-268start_time=time.time()
-269
-270validate_file(self._filename)
-271try:
-272self._fh=open(self._filename,self._mode)
-273exceptIOErrorase:
-274# If we can't access with _mode, try _fallback_mode and don't lock.
-275ife.errno==errno.EACCES:
-276self._fh=open(self._filename,self._fallback_mode)
-277return
-278
-279# We opened in _mode, try to lock the file.
-280whileTrue:
-281try:
-282hfile=win32file._get_osfhandle(self._fh.fileno())
-283win32file.LockFileEx(
-284hfile,
-285(win32con.LOCKFILE_FAIL_IMMEDIATELY|
-286win32con.LOCKFILE_EXCLUSIVE_LOCK),0,-0x10000,
-287pywintypes.OVERLAPPED())
-288self._locked=True
-289return
-290exceptpywintypes.errorase:
-291iftimeout==0:
-292raisee
-293
-294# If the error is not that the file is already in use, raise.
-295ife[0]!=_Win32Opener.FILE_IN_USE_ERROR:
-296raise
-297
-298# We could not acquire the lock. Try again.
-299if(time.time()-start_time)>=timeout:
-300logger.warn('Could not lock %s in %s seconds'%(
-301self._filename,timeout))
-302ifself._fh:
-303self._fh.close()
-304self._fh=open(self._filename,self._fallback_mode)
-305return
-306time.sleep(delay)
-
309"""Close and unlock the file using the win32 primitive."""
-310ifself._locked:
-311try:
-312hfile=win32file._get_osfhandle(self._fh.fileno())
-313win32file.UnlockFileEx(hfile,0,-0x10000,pywintypes.OVERLAPPED())
-314exceptpywintypes.errorase:
-315ife[0]!=_Win32Opener.FILE_ALREADY_UNLOCKED_ERROR:
-316raise
-317self._locked=False
-318ifself._fh:
-319self._fh.close()
-
329"""Construct a LockedFile.
-330
-331 Args:
-332 filename: string, The path of the file to open.
-333 mode: string, The mode to try to open the file with.
-334 fallback_mode: string, The mode to use if locking fails.
-335 use_native_locking: bool, Whether or not fcntl/win32 locking is used.
-336 """
-337opener=None
-338ifnotopeneranduse_native_locking:
-339if_Win32Opener:
-340opener=_Win32Opener(filename,mode,fallback_mode)
-341if_FcntlOpener:
-342opener=_FcntlOpener(filename,mode,fallback_mode)
-343
-344ifnotopener:
-345opener=_PosixOpener(filename,mode,fallback_mode)
-346
-347self._opener=opener
-
362"""Open the file, trying to lock it.
-363
-364 Args:
-365 timeout: float, The number of seconds to try to acquire the lock.
-366 delay: float, The number of seconds to wait between retry attempts.
-367
-368 Raises:
-369 AlreadyLockedException: if the lock is already acquired.
-370 IOError: if the open fails.
-371 """
-372self._opener.open_and_lock(timeout,delay)
-
-Construct a LockedFile.
-
-Args:
- filename: string, The path of the file to open.
- mode: string, The mode to try to open the file with.
- fallback_mode: string, The mode to use if locking fails.
- use_native_locking: bool, Whether or not fcntl/win32 locking is used.
-
-
-Open the file, trying to lock it.
-
-Args:
- timeout: float, The number of seconds to try to acquire the lock.
- delay: float, The number of seconds to wait between retry attempts.
-
-Raises:
- AlreadyLockedException: if the lock is already acquired.
- IOError: if the open fails.
-
-
-Create an Opener.
-
-Args:
- filename: string, The pathname of the file.
- mode: string, The preferred mode to access the file with.
- fallback_mode: string, The mode to use if locking fails.
-
-
-Open the file and lock it.
-
-Tries to create a .lock file next to the file we're trying to open.
-
-Args:
- timeout: float, How long to try to lock for.
- delay: float, How long to wait between retries.
-
-Raises:
- AlreadyLockedException: if the lock is already acquired.
- IOError: if the open fails.
- CredentialsFileSymbolicLinkError if the file is a symbolic link.
-
-
-Multi-credential file store with lock support.
-
-This module implements a JSON credential store where multiple
-credentials can be stored in one file. That file supports locking
-both in a single process and across processes.
-
-The credential themselves are keyed off of:
-* client_id
-* user_agent
-* scope
-
-The format of the stored data is like so:
-{
- 'file_version': 1,
- 'data': [
- {
- 'key': {
- 'clientId': '<client id>',
- 'userAgent': '<user agent>',
- 'scope': '<scope>'
- },
- 'credential': {
- # JSON serialized Credentials.
- }
- }
- ]
-}
-
-
get_credential_storage_custom_string_key(filename,
- key_string,
- warn_on_readonly=True)
- Get a Storage instance for a credential using a single string as a key.
get_credential_storage_custom_key(filename,
- key_dict,
- warn_on_readonly=True)
- Get a Storage instance for a credential using a dictionary as a key.
-Get a Storage instance for a credential.
-
-Args:
- filename: The JSON file storing a set of credentials
- client_id: The client_id for the credential
- user_agent: The user agent for the credential
- scope: string or iterable of strings, Scope(s) being requested
- warn_on_readonly: if True, log a warning if the store is readonly
-
-Returns:
- An object derived from client.Storage for getting/setting the
- credential.
-
-
-Get a Storage instance for a credential using a single string as a key.
-
-Allows you to provide a string as a custom key that will be used for
-credential storage and retrieval.
-
-Args:
- filename: The JSON file storing a set of credentials
- key_string: A string to use as the key for storing this credential.
- warn_on_readonly: if True, log a warning if the store is readonly
-
-Returns:
- An object derived from client.Storage for getting/setting the
- credential.
-
-
-Get a Storage instance for a credential using a dictionary as a key.
-
-Allows you to provide a dictionary as a custom key that will be used for
-credential storage and retrieval.
-
-Args:
- filename: The JSON file storing a set of credentials
- key_dict: A dictionary to use as the key for storing this credential. There
- is no ordering of the keys in the dictionary. Logically equivalent
- dictionaries will produce equivalent storage keys.
- warn_on_readonly: if True, log a warning if the store is readonly
-
-Returns:
- An object derived from client.Storage for getting/setting the
- credential.
-
-
-Gets all the registered credential keys in the given Multistore.
-
-Args:
- filename: The JSON file storing a set of credentials
- warn_on_readonly: if True, log a warning if the store is readonly
-
-Returns:
- A list of the credential keys present in the file. They are returned as
- dictionaries that can be passed into get_credential_storage_custom_key to
- get the actual credentials.
-
-
-A helper method to initialize the multistore with proper locking.
-
-Args:
- filename: The JSON file storing a set of credentials
- warn_on_readonly: if True, log a warning if the store is readonly
-
-Returns:
- A multistore object
-
-
- 1# Copyright 2014 Google Inc. All rights reserved.
- 2#
- 3# Licensed under the Apache License, Version 2.0 (the "License");
- 4# you may not use this file except in compliance with the License.
- 5# You may obtain a copy of the License at
- 6#
- 7# http://www.apache.org/licenses/LICENSE-2.0
- 8#
- 9# Unless required by applicable law or agreed to in writing, software
- 10# distributed under the License is distributed on an "AS IS" BASIS,
- 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- 12# See the License for the specific language governing permissions and
- 13# limitations under the License.
- 14
- 15"""Multi-credential file store with lock support.
- 16
- 17This module implements a JSON credential store where multiple
- 18credentials can be stored in one file. That file supports locking
- 19both in a single process and across processes.
- 20
- 21The credential themselves are keyed off of:
- 22* client_id
- 23* user_agent
- 24* scope
- 25
- 26The format of the stored data is like so:
- 27{
- 28 'file_version': 1,
- 29 'data': [
- 30 {
- 31 'key': {
- 32 'clientId': '<client id>',
- 33 'userAgent': '<user agent>',
- 34 'scope': '<scope>'
- 35 },
- 36 'credential': {
- 37 # JSON serialized Credentials.
- 38 }
- 39 }
- 40 ]
- 41}
- 42"""
- 43
- 44__author__='jbeda@google.com (Joe Beda)'
- 45
- 46importjson
- 47importlogging
- 48importos
- 49importthreading
- 50
- 51fromoauth2client.clientimportCredentials
- 52fromoauth2client.clientimportStorageasBaseStorage
- 53fromoauth2clientimportutil
- 54fromoauth2client.locked_fileimportLockedFile
- 55
- 56logger=logging.getLogger(__name__)
- 57
- 58# A dict from 'filename'->_MultiStore instances
- 59_multistores={}
- 60_multistores_lock=threading.Lock()
-
76"""Get a Storage instance for a credential.
- 77
- 78 Args:
- 79 filename: The JSON file storing a set of credentials
- 80 client_id: The client_id for the credential
- 81 user_agent: The user agent for the credential
- 82 scope: string or iterable of strings, Scope(s) being requested
- 83 warn_on_readonly: if True, log a warning if the store is readonly
- 84
- 85 Returns:
- 86 An object derived from client.Storage for getting/setting the
- 87 credential.
- 88 """
- 89# Recreate the legacy key with these specific parameters
- 90key={'clientId':client_id,'userAgent':user_agent,
- 91'scope':util.scopes_to_string(scope)}
- 92returnget_credential_storage_custom_key(
- 93filename,key,warn_on_readonly=warn_on_readonly)
-
99"""Get a Storage instance for a credential using a single string as a key.
-100
-101 Allows you to provide a string as a custom key that will be used for
-102 credential storage and retrieval.
-103
-104 Args:
-105 filename: The JSON file storing a set of credentials
-106 key_string: A string to use as the key for storing this credential.
-107 warn_on_readonly: if True, log a warning if the store is readonly
-108
-109 Returns:
-110 An object derived from client.Storage for getting/setting the
-111 credential.
-112 """
-113# Create a key dictionary that can be used
-114key_dict={'key':key_string}
-115returnget_credential_storage_custom_key(
-116filename,key_dict,warn_on_readonly=warn_on_readonly)
-
122"""Get a Storage instance for a credential using a dictionary as a key.
-123
-124 Allows you to provide a dictionary as a custom key that will be used for
-125 credential storage and retrieval.
-126
-127 Args:
-128 filename: The JSON file storing a set of credentials
-129 key_dict: A dictionary to use as the key for storing this credential. There
-130 is no ordering of the keys in the dictionary. Logically equivalent
-131 dictionaries will produce equivalent storage keys.
-132 warn_on_readonly: if True, log a warning if the store is readonly
-133
-134 Returns:
-135 An object derived from client.Storage for getting/setting the
-136 credential.
-137 """
-138multistore=_get_multistore(filename,warn_on_readonly=warn_on_readonly)
-139key=util.dict_to_tuple_key(key_dict)
-140returnmultistore._get_storage(key)
-
145"""Gets all the registered credential keys in the given Multistore.
-146
-147 Args:
-148 filename: The JSON file storing a set of credentials
-149 warn_on_readonly: if True, log a warning if the store is readonly
-150
-151 Returns:
-152 A list of the credential keys present in the file. They are returned as
-153 dictionaries that can be passed into get_credential_storage_custom_key to
-154 get the actual credentials.
-155 """
-156multistore=_get_multistore(filename,warn_on_readonly=warn_on_readonly)
-157multistore._lock()
-158try:
-159returnmultistore._get_all_credential_keys()
-160finally:
-161multistore._unlock()
-
166"""A helper method to initialize the multistore with proper locking.
-167
-168 Args:
-169 filename: The JSON file storing a set of credentials
-170 warn_on_readonly: if True, log a warning if the store is readonly
-171
-172 Returns:
-173 A multistore object
-174 """
-175filename=os.path.expanduser(filename)
-176_multistores_lock.acquire()
-177try:
-178multistore=_multistores.setdefault(
-179filename,_MultiStore(filename,warn_on_readonly=warn_on_readonly))
-180finally:
-181_multistores_lock.release()
-182returnmultistore
-
190"""Initialize the class.
-191
-192 This will create the file if necessary.
-193 """
-194self._file=LockedFile(filename,'r+','r')
-195self._thread_lock=threading.Lock()
-196self._read_only=False
-197self._warn_on_readonly=warn_on_readonly
-198
-199self._create_file_if_needed()
-200
-201# Cache of deserialized store. This is only valid after the
-202# _MultiStore is locked or _refresh_data_cache is called. This is
-203# of the form of:
-204#
-205# ((key, value), (key, value)...) -> OAuth2Credential
-206#
-207# If this is None, then the store hasn't been read yet.
-208self._data=None
-
225"""Release the Storage lock.
-226
-227 Trying to release a lock that isn't held will result in a
-228 RuntimeError.
-229 """
-230self._multistore._unlock()
-
233"""Retrieve credential.
-234
-235 The Storage lock must be held when this is called.
-236
-237 Returns:
-238 oauth2client.client.Credentials
-239 """
-240credential=self._multistore._get_credential(self._key)
-241ifcredential:
-242credential.set_store(self)
-243returncredential
-
246"""Write a credential.
-247
-248 The Storage lock must be held when this is called.
-249
-250 Args:
-251 credentials: Credentials, the credentials to store.
-252 """
-253self._multistore._update_credential(self._key,credentials)
-
256"""Delete a credential.
-257
-258 The Storage lock must be held when this is called.
-259
-260 Args:
-261 credentials: Credentials, the credentials to store.
-262 """
-263self._multistore._delete_credential(self._key)
-
266"""Create an empty file if necessary.
-267
-268 This method will not initialize the file. Instead it implements a
-269 simple version of "touch" to ensure the file has been created.
-270 """
-271ifnotos.path.exists(self._file.filename()):
-272old_umask=os.umask(0o177)
-273try:
-274open(self._file.filename(),'a+b').close()
-275finally:
-276os.umask(old_umask)
-
279"""Lock the entire multistore."""
-280self._thread_lock.acquire()
-281self._file.open_and_lock()
-282ifnotself._file.is_locked():
-283self._read_only=True
-284ifself._warn_on_readonly:
-285logger.warn('The credentials file (%s) is not writable. Opening in '
-286'read-only mode. Any refreshed credentials will only be '
-287'valid for this run.',self._file.filename())
-288ifos.path.getsize(self._file.filename())==0:
-289logger.debug('Initializing empty multistore file')
-290# The multistore is empty so write out an empty file.
-291self._data={}
-292self._write()
-293elifnotself._read_onlyorself._dataisNone:
-294# Only refresh the data if we are read/write or we haven't
-295# cached the data yet. If we are readonly, we assume is isn't
-296# changing out from under us and that we only have to read it
-297# once. This prevents us from whacking any new access keys that
-298# we have cached in memory but were unable to write out.
-299self._refresh_data_cache()
-
307"""Get the raw content of the multistore file.
-308
-309 The multistore must be locked when this is called.
-310
-311 Returns:
-312 The contents of the multistore decoded as JSON.
-313 """
-314assertself._thread_lock.locked()
-315self._file.file_handle().seek(0)
-316returnjson.load(self._file.file_handle())
-
319"""Write a JSON serializable data structure to the multistore.
-320
-321 The multistore must be locked when this is called.
-322
-323 Args:
-324 data: The data to be serialized and written.
-325 """
-326assertself._thread_lock.locked()
-327ifself._read_only:
-328return
-329self._file.file_handle().seek(0)
-330json.dump(data,self._file.file_handle(),sort_keys=True,indent=2,separators=(',',': '))
-331self._file.file_handle().truncate()
-
334"""Refresh the contents of the multistore.
-335
-336 The multistore must be locked when this is called.
-337
-338 Raises:
-339 NewerCredentialStoreError: Raised when a newer client has written the
-340 store.
-341 """
-342self._data={}
-343try:
-344raw_data=self._locked_json_read()
-345exceptException:
-346logger.warn('Credential data store could not be loaded. '
-347'Will ignore and overwrite.')
-348return
-349
-350version=0
-351try:
-352version=raw_data['file_version']
-353exceptException:
-354logger.warn('Missing version for credential data store. It may be '
-355'corrupt or an old version. Overwriting.')
-356ifversion>1:
-357raiseNewerCredentialStoreError(
-358'Credential file has file_version of %d. '
-359'Only file_version of 1 is supported.'%version)
-360
-361credentials=[]
-362try:
-363credentials=raw_data['data']
-364except(TypeError,KeyError):
-365pass
-366
-367forcred_entryincredentials:
-368try:
-369(key,credential)=self._decode_credential_from_json(cred_entry)
-370self._data[key]=credential
-371except:
-372# If something goes wrong loading a credential, just ignore it
-373logger.info('Error decoding credential, skipping',exc_info=True)
-
376"""Load a credential from our JSON serialization.
-377
-378 Args:
-379 cred_entry: A dict entry from the data member of our format
-380
-381 Returns:
-382 (key, cred) where the key is the key tuple and the cred is the
-383 OAuth2Credential object.
-384 """
-385raw_key=cred_entry['key']
-386key=util.dict_to_tuple_key(raw_key)
-387credential=None
-388credential=Credentials.new_from_json(json.dumps(cred_entry['credential']))
-389return(key,credential)
-
392"""Write the cached data back out.
-393
-394 The multistore must be locked.
-395 """
-396raw_data={'file_version':1}
-397raw_creds=[]
-398raw_data['data']=raw_creds
-399for(cred_key,cred)inself._data.items():
-400raw_key=dict(cred_key)
-401raw_cred=json.loads(cred.to_json())
-402raw_creds.append({'key':raw_key,'credential':raw_cred})
-403self._locked_json_write(raw_data)
-
406"""Gets all the registered credential keys in the multistore.
-407
-408 Returns:
-409 A list of dictionaries corresponding to all the keys currently registered
-410 """
-411return[dict(key)forkeyinself._data.keys()]
-
414"""Get a credential from the multistore.
-415
-416 The multistore must be locked.
-417
-418 Args:
-419 key: The key used to retrieve the credential
-420
-421 Returns:
-422 The credential specified or None if not present
-423 """
-424returnself._data.get(key,None)
-
427"""Update a credential and write the multistore.
-428
-429 This must be called when the multistore is locked.
-430
-431 Args:
-432 key: The key used to retrieve the credential
-433 cred: The OAuth2Credential to update/set
-434 """
-435self._data[key]=cred
-436self._write()
-
439"""Delete a credential and write the multistore.
-440
-441 This must be called when the multistore is locked.
-442
-443 Args:
-444 key: The key used to retrieve the credential
-445 """
-446try:
-447delself._data[key]
-448exceptKeyError:
-449pass
-450self._write()
-
453"""Get a Storage object to get/set a credential.
-454
-455 This Storage is a 'view' into the multistore.
-456
-457 Args:
-458 key: The key used to retrieve the credential
-459
-460 Returns:
-461 A Storage object that can be used to get/set this cred
-462 """
-463returnself._Storage(self,key)
-
-Create an empty file if necessary.
-
-This method will not initialize the file. Instead it implements a
-simple version of "touch" to ensure the file has been created.
-
-
-Get the raw content of the multistore file.
-
-The multistore must be locked when this is called.
-
-Returns:
- The contents of the multistore decoded as JSON.
-
-
-Write a JSON serializable data structure to the multistore.
-
-The multistore must be locked when this is called.
-
-Args:
- data: The data to be serialized and written.
-
-
-Refresh the contents of the multistore.
-
-The multistore must be locked when this is called.
-
-Raises:
- NewerCredentialStoreError: Raised when a newer client has written the
- store.
-
-
-Load a credential from our JSON serialization.
-
-Args:
- cred_entry: A dict entry from the data member of our format
-
-Returns:
- (key, cred) where the key is the key tuple and the cred is the
- OAuth2Credential object.
-
-
-Gets all the registered credential keys in the multistore.
-
-Returns:
- A list of dictionaries corresponding to all the keys currently registered
-
-
-Get a credential from the multistore.
-
-The multistore must be locked.
-
-Args:
- key: The key used to retrieve the credential
-
-Returns:
- The credential specified or None if not present
-
-
-Update a credential and write the multistore.
-
-This must be called when the multistore is locked.
-
-Args:
- key: The key used to retrieve the credential
- cred: The OAuth2Credential to update/set
-
-
-Delete a credential and write the multistore.
-
-This must be called when the multistore is locked.
-
-Args:
- key: The key used to retrieve the credential
-
-
-Get a Storage object to get/set a credential.
-
-This Storage is a 'view' into the multistore.
-
-Args:
- key: The key used to retrieve the credential
-
-Returns:
- A Storage object that can be used to get/set this cred
-
-
-Core code for a command-line application.
-
-The run() function is called from your application and runs through all
-the steps to obtain credentials. It takes a Flow argument and attempts to
-open an authorization server page in the user's default web browser. The
-server asks the user to grant your application access to the user's data.
-If the user grants access, the run() function returns new credentials. The
-new credentials are also stored in the Storage argument, which updates the
-file associated with the Storage object.
-
-It presumes it is run from a command-line application and supports the
-following flags:
-
- --auth_host_name: Host name to use when running a local web server
- to handle redirects during OAuth authorization.
- (default: 'localhost')
-
- --auth_host_port: Port to use when running a local web server to handle
- redirects during OAuth authorization.;
- repeat this option to specify a list of values
- (default: '[8080, 8090]')
- (an integer)
-
- --[no]auth_local_webserver: Run a local web server to handle redirects
- during OAuth authorization.
- (default: 'true')
-
-Since it uses flags make sure to initialize the gflags module before
-calling run().
-
-Args:
- flow: Flow, an OAuth 2.0 Flow to step through.
- storage: Storage, a Storage to store the credential in.
- http: An instance of httplib2.Http.request
- or something that acts like it.
-
-Returns:
- Credentials, the obtained credential.
-
-
- 1# Copyright 2014 Google Inc. All rights reserved.
- 2#
- 3# Licensed under the Apache License, Version 2.0 (the "License");
- 4# you may not use this file except in compliance with the License.
- 5# You may obtain a copy of the License at
- 6#
- 7# http://www.apache.org/licenses/LICENSE-2.0
- 8#
- 9# Unless required by applicable law or agreed to in writing, software
- 10# distributed under the License is distributed on an "AS IS" BASIS,
- 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- 12# See the License for the specific language governing permissions and
- 13# limitations under the License.
- 14
- 15"""This module holds the old run() function which is deprecated, the
- 16tools.run_flow() function should be used in its place."""
- 17
- 18from__future__importprint_function
- 19
- 20importlogging
- 21importsocket
- 22importsys
- 23importwebbrowser
- 24
- 25importgflags
- 26
- 27fromoauth2clientimportclient
- 28fromoauth2clientimportutil
- 29fromoauth2client.toolsimportClientRedirectHandler
- 30fromoauth2client.toolsimportClientRedirectServer
- 31
- 32
- 33FLAGS=gflags.FLAGS
- 34
- 35gflags.DEFINE_boolean('auth_local_webserver',True,
- 36('Run a local web server to handle redirects during '
- 37'OAuth authorization.'))
- 38
- 39gflags.DEFINE_string('auth_host_name','localhost',
- 40('Host name to use when running a local web server to '
- 41'handle redirects during OAuth authorization.'))
- 42
- 43gflags.DEFINE_multi_int('auth_host_port',[8080,8090],
- 44('Port to use when running a local web server to '
- 45'handle redirects during OAuth authorization.'))
-
50"""Core code for a command-line application.
- 51
- 52 The run() function is called from your application and runs through all
- 53 the steps to obtain credentials. It takes a Flow argument and attempts to
- 54 open an authorization server page in the user's default web browser. The
- 55 server asks the user to grant your application access to the user's data.
- 56 If the user grants access, the run() function returns new credentials. The
- 57 new credentials are also stored in the Storage argument, which updates the
- 58 file associated with the Storage object.
- 59
- 60 It presumes it is run from a command-line application and supports the
- 61 following flags:
- 62
- 63 --auth_host_name: Host name to use when running a local web server
- 64 to handle redirects during OAuth authorization.
- 65 (default: 'localhost')
- 66
- 67 --auth_host_port: Port to use when running a local web server to handle
- 68 redirects during OAuth authorization.;
- 69 repeat this option to specify a list of values
- 70 (default: '[8080, 8090]')
- 71 (an integer)
- 72
- 73 --[no]auth_local_webserver: Run a local web server to handle redirects
- 74 during OAuth authorization.
- 75 (default: 'true')
- 76
- 77 Since it uses flags make sure to initialize the gflags module before
- 78 calling run().
- 79
- 80 Args:
- 81 flow: Flow, an OAuth 2.0 Flow to step through.
- 82 storage: Storage, a Storage to store the credential in.
- 83 http: An instance of httplib2.Http.request
- 84 or something that acts like it.
- 85
- 86 Returns:
- 87 Credentials, the obtained credential.
- 88 """
- 89logging.warning('This function, oauth2client.tools.run(), and the use of '
- 90'the gflags library are deprecated and will be removed in a future '
- 91'version of the library.')
- 92ifFLAGS.auth_local_webserver:
- 93success=False
- 94port_number=0
- 95forportinFLAGS.auth_host_port:
- 96port_number=port
- 97try:
- 98httpd=ClientRedirectServer((FLAGS.auth_host_name,port),
- 99ClientRedirectHandler)
-100exceptsocket.errorase:
-101pass
-102else:
-103success=True
-104break
-105FLAGS.auth_local_webserver=success
-106ifnotsuccess:
-107print('Failed to start a local webserver listening on either port 8080')
-108print('or port 9090. Please check your firewall settings and locally')
-109print('running programs that may be blocking or using those ports.')
-110print()
-111print('Falling back to --noauth_local_webserver and continuing with')
-112print('authorization.')
-113print()
-114
-115ifFLAGS.auth_local_webserver:
-116oauth_callback='http://%s:%s/'%(FLAGS.auth_host_name,port_number)
-117else:
-118oauth_callback=client.OOB_CALLBACK_URN
-119flow.redirect_uri=oauth_callback
-120authorize_url=flow.step1_get_authorize_url()
-121
-122ifFLAGS.auth_local_webserver:
-123webbrowser.open(authorize_url,new=1,autoraise=True)
-124print('Your browser has been opened to visit:')
-125print()
-126print(' '+authorize_url)
-127print()
-128print('If your browser is on a different machine then exit and re-run')
-129print('this application with the command-line parameter ')
-130print()
-131print(' --noauth_local_webserver')
-132print()
-133else:
-134print('Go to the following link in your browser:')
-135print()
-136print(' '+authorize_url)
-137print()
-138
-139code=None
-140ifFLAGS.auth_local_webserver:
-141httpd.handle_request()
-142if'error'inhttpd.query_params:
-143sys.exit('Authentication request was rejected.')
-144if'code'inhttpd.query_params:
-145code=httpd.query_params['code']
-146else:
-147print('Failed to find "code" in the query parameters of the redirect.')
-148sys.exit('Try running with --noauth_local_webserver.')
-149else:
-150code=raw_input('Enter verification code: ').strip()
-151
-152try:
-153credential=flow.step2_exchange(code,http=http)
-154exceptclient.FlowExchangeErrorase:
-155sys.exit('Authentication has failed: %s'%e)
-156
-157storage.put(credential)
-158credential.set_store(storage)
-159print('Authentication successful.')
-160
-161returncredential
-
- 1# Copyright 2014 Google Inc. All rights reserved.
- 2#
- 3# Licensed under the Apache License, Version 2.0 (the "License");
- 4# you may not use this file except in compliance with the License.
- 5# You may obtain a copy of the License at
- 6#
- 7# http://www.apache.org/licenses/LICENSE-2.0
- 8#
- 9# Unless required by applicable law or agreed to in writing, software
- 10# distributed under the License is distributed on an "AS IS" BASIS,
- 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- 12# See the License for the specific language governing permissions and
- 13# limitations under the License.
- 14
- 15"""A service account credentials class.
- 16
- 17This credentials class is implemented on top of rsa library.
- 18"""
- 19
- 20importbase64
- 21importjson
- 22importtime
- 23
- 24frompyasn1.codec.berimportdecoder
- 25frompyasn1_modules.rfc5208importPrivateKeyInfo
- 26importrsa
- 27
- 28fromoauth2clientimportGOOGLE_REVOKE_URI
- 29fromoauth2clientimportGOOGLE_TOKEN_URI
- 30fromoauth2clientimportutil
- 31fromoauth2client.clientimportAssertionCredentials
-
-Constructor for AssertionFlowCredentials.
-
-Args:
- assertion_type: string, assertion type that will be declared to the auth
- server
- user_agent: string, The HTTP User-Agent to provide for this application.
- token_uri: string, URI for token endpoint. For convenience
- defaults to Google's endpoints but any OAuth 2.0 provider can be used.
- revoke_uri: string, URI for revoke endpoint.
-
-
-Whether this Credentials object is scopeless.
-
-create_scoped(scopes) method needs to be called in order to create
-a Credentials object for API calls.
-
-
-Command-line tools for authenticating via OAuth 2.0
-
-Do the OAuth 2.0 Web Server dance for a command line application. Stores the
-generated credentials in a common file that is used by other example apps in
-the same directory.
-
-
-Core code for a command-line application.
-
-The run() function is called from your application and runs through all the
-steps to obtain credentials. It takes a Flow argument and attempts to open an
-authorization server page in the user's default web browser. The server asks
-the user to grant your application access to the user's data. If the user
-grants access, the run() function returns new credentials. The new credentials
-are also stored in the Storage argument, which updates the file associated
-with the Storage object.
-
-It presumes it is run from a command-line application and supports the
-following flags:
-
- --auth_host_name: Host name to use when running a local web server
- to handle redirects during OAuth authorization.
- (default: 'localhost')
-
- --auth_host_port: Port to use when running a local web server to handle
- redirects during OAuth authorization.;
- repeat this option to specify a list of values
- (default: '[8080, 8090]')
- (an integer)
-
- --[no]auth_local_webserver: Run a local web server to handle redirects
- during OAuth authorization.
- (default: 'true')
-
-The tools module defines an ArgumentParser the already contains the flag
-definitions that run() requires. You can pass that ArgumentParser to your
-ArgumentParser constructor:
-
- parser = argparse.ArgumentParser(description=__doc__,
- formatter_class=argparse.RawDescriptionHelpFormatter,
- parents=[tools.argparser])
- flags = parser.parse_args(argv)
-
-Args:
- flow: Flow, an OAuth 2.0 Flow to step through.
- storage: Storage, a Storage to store the credential in.
- flags: argparse.ArgumentParser, the command-line flags.
- http: An instance of httplib2.Http.request
- or something that acts like it.
-
-Returns:
- Credentials, the obtained credential.
-
-
- 1# Copyright 2014 Google Inc. All rights reserved.
- 2#
- 3# Licensed under the Apache License, Version 2.0 (the "License");
- 4# you may not use this file except in compliance with the License.
- 5# You may obtain a copy of the License at
- 6#
- 7# http://www.apache.org/licenses/LICENSE-2.0
- 8#
- 9# Unless required by applicable law or agreed to in writing, software
- 10# distributed under the License is distributed on an "AS IS" BASIS,
- 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- 12# See the License for the specific language governing permissions and
- 13# limitations under the License.
- 14
- 15"""Command-line tools for authenticating via OAuth 2.0
- 16
- 17Do the OAuth 2.0 Web Server dance for a command line application. Stores the
- 18generated credentials in a common file that is used by other example apps in
- 19the same directory.
- 20"""
- 21
- 22from__future__importprint_function
- 23
- 24__author__='jcgregorio@google.com (Joe Gregorio)'
- 25__all__=['argparser','run_flow','run','message_if_missing']
- 26
- 27importBaseHTTPServer
- 28importlogging
- 29importsocket
- 30importsys
- 31importwebbrowser
- 32
- 33fromsix.movesimporturllib
- 34
- 35fromoauth2clientimportclient
- 36fromoauth2clientimportutil
- 37
- 38
- 39_CLIENT_SECRETS_MESSAGE="""WARNING: Please configure OAuth 2.0
- 40
- 41To make this sample run you will need to populate the client_secrets.json file
- 42found at:
- 43
- 44 %s
- 45
- 46with information from the APIs Console <https://code.google.com/apis/console>.
- 47
- 48"""
- 49
- 50# argparser is an ArgumentParser that contains command-line options expected
- 51# by tools.run(). Pass it in as part of the 'parents' argument to your own
- 52# ArgumentParser.
- 53argparser=_CreateArgumentParser()
-
56try:
- 57importargparse
- 58exceptImportError:
- 59returnNone
- 60argparser=argparse.ArgumentParser(add_help=False)
- 61argparser.add_argument('--auth_host_name',default='localhost',
- 62help='Hostname when running a local web server.')
- 63argparser.add_argument('--noauth_local_webserver',action='store_true',
- 64default=False,help='Do not run a local web server.')
- 65argparser.add_argument('--auth_host_port',default=[8080,8090],type=int,
- 66nargs='*',help='Port web server should listen on.')
- 67argparser.add_argument('--logging_level',default='ERROR',
- 68choices=['DEBUG','INFO','WARNING','ERROR',
- 69'CRITICAL'],
- 70help='Set the logging level of detail.')
- 71returnargparser
-
75"""A server to handle OAuth 2.0 redirects back to localhost.
- 76
- 77 Waits for a single request and parses the query parameters
- 78 into query_params and then stops serving.
- 79 """
- 80query_params={}
-
84"""A handler for OAuth 2.0 redirects back to localhost.
- 85
- 86 Waits for a single request and parses the query parameters
- 87 into the servers query_params and then stops serving.
- 88 """
- 89
-
91"""Handle a GET request.
- 92
- 93 Parses the query parameters and prints a message
- 94 if the flow has completed. Note that we can't detect
- 95 if an error occurred.
- 96 """
- 97self.send_response(200)
- 98self.send_header("Content-type","text/html")
- 99self.end_headers()
-100query=self.path.split('?',1)[-1]
-101query=dict(urllib.parse.parse_qsl(query))
-102self.server.query_params=query
-103self.wfile.write("<html><head><title>Authentication Status</title></head>")
-104self.wfile.write("<body><p>The authentication flow has completed.</p>")
-105self.wfile.write("</body></html>")
-
113"""Core code for a command-line application.
-114
-115 The run() function is called from your application and runs through all the
-116 steps to obtain credentials. It takes a Flow argument and attempts to open an
-117 authorization server page in the user's default web browser. The server asks
-118 the user to grant your application access to the user's data. If the user
-119 grants access, the run() function returns new credentials. The new credentials
-120 are also stored in the Storage argument, which updates the file associated
-121 with the Storage object.
-122
-123 It presumes it is run from a command-line application and supports the
-124 following flags:
-125
-126 --auth_host_name: Host name to use when running a local web server
-127 to handle redirects during OAuth authorization.
-128 (default: 'localhost')
-129
-130 --auth_host_port: Port to use when running a local web server to handle
-131 redirects during OAuth authorization.;
-132 repeat this option to specify a list of values
-133 (default: '[8080, 8090]')
-134 (an integer)
-135
-136 --[no]auth_local_webserver: Run a local web server to handle redirects
-137 during OAuth authorization.
-138 (default: 'true')
-139
-140 The tools module defines an ArgumentParser the already contains the flag
-141 definitions that run() requires. You can pass that ArgumentParser to your
-142 ArgumentParser constructor:
-143
-144 parser = argparse.ArgumentParser(description=__doc__,
-145 formatter_class=argparse.RawDescriptionHelpFormatter,
-146 parents=[tools.argparser])
-147 flags = parser.parse_args(argv)
-148
-149 Args:
-150 flow: Flow, an OAuth 2.0 Flow to step through.
-151 storage: Storage, a Storage to store the credential in.
-152 flags: argparse.ArgumentParser, the command-line flags.
-153 http: An instance of httplib2.Http.request
-154 or something that acts like it.
-155
-156 Returns:
-157 Credentials, the obtained credential.
-158 """
-159logging.getLogger().setLevel(getattr(logging,flags.logging_level))
-160ifnotflags.noauth_local_webserver:
-161success=False
-162port_number=0
-163forportinflags.auth_host_port:
-164port_number=port
-165try:
-166httpd=ClientRedirectServer((flags.auth_host_name,port),
-167ClientRedirectHandler)
-168exceptsocket.error:
-169pass
-170else:
-171success=True
-172break
-173flags.noauth_local_webserver=notsuccess
-174ifnotsuccess:
-175print('Failed to start a local webserver listening on either port 8080')
-176print('or port 9090. Please check your firewall settings and locally')
-177print('running programs that may be blocking or using those ports.')
-178print()
-179print('Falling back to --noauth_local_webserver and continuing with')
-180print('authorization.')
-181print()
-182
-183ifnotflags.noauth_local_webserver:
-184oauth_callback='http://%s:%s/'%(flags.auth_host_name,port_number)
-185else:
-186oauth_callback=client.OOB_CALLBACK_URN
-187flow.redirect_uri=oauth_callback
-188authorize_url=flow.step1_get_authorize_url()
-189
-190ifnotflags.noauth_local_webserver:
-191webbrowser.open(authorize_url,new=1,autoraise=True)
-192print('Your browser has been opened to visit:')
-193print()
-194print(' '+authorize_url)
-195print()
-196print('If your browser is on a different machine then exit and re-run this')
-197print('application with the command-line parameter ')
-198print()
-199print(' --noauth_local_webserver')
-200print()
-201else:
-202print('Go to the following link in your browser:')
-203print()
-204print(' '+authorize_url)
-205print()
-206
-207code=None
-208ifnotflags.noauth_local_webserver:
-209httpd.handle_request()
-210if'error'inhttpd.query_params:
-211sys.exit('Authentication request was rejected.')
-212if'code'inhttpd.query_params:
-213code=httpd.query_params['code']
-214else:
-215print('Failed to find "code" in the query parameters of the redirect.')
-216sys.exit('Try running with --noauth_local_webserver.')
-217else:
-218code=raw_input('Enter verification code: ').strip()
-219
-220try:
-221credential=flow.step2_exchange(code,http=http)
-222exceptclient.FlowExchangeErrorase:
-223sys.exit('Authentication has failed: %s'%e)
-224
-225storage.put(credential)
-226credential.set_store(storage)
-227print('Authentication successful.')
-228
-229returncredential
-
242raiseNotImplementedError(
-243'The gflags library must be installed to use tools.run(). '
-244'Please install gflags or preferrably switch to using '
-245'tools.run_flow().')
-
-A handler for OAuth 2.0 redirects back to localhost.
-
-Waits for a single request and parses the query parameters
-into the servers query_params and then stops serving.
-
-
-Handle a GET request.
-
-Parses the query parameters and prints a message
-if the flow has completed. Note that we can't detect
-if an error occurred.
-
-
-A server to handle OAuth 2.0 redirects back to localhost.
-
-Waits for a single request and parses the query parameters
-into query_params and then stops serving.
-
-
-A decorator to declare that only the first N arguments my be positional.
-
-This decorator makes it easy to support Python 3 style key-word only
-parameters. For example, in Python 3 it is possible to write:
-
- def fn(pos1, *, kwonly1=None, kwonly1=None):
- ...
-
-All named parameters after * must be a keyword:
-
- fn(10, 'kw1', 'kw2') # Raises exception.
- fn(10, kwonly1='kw1') # Ok.
-
-Example:
- To define a function like above, do:
-
- @positional(1)
- def fn(pos1, kwonly1=None, kwonly2=None):
- ...
-
- If no default value is provided to a keyword argument, it becomes a required
- keyword argument:
-
- @positional(0)
- def fn(required_kw):
- ...
-
- This must be called with the keyword parameter:
-
- fn() # Raises exception.
- fn(10) # Raises exception.
- fn(required_kw=10) # Ok.
-
- When defining instance or class methods always remember to account for
- 'self' and 'cls':
-
- class MyClass(object):
-
- @positional(2)
- def my_method(self, pos1, kwonly1=None):
- ...
-
- @classmethod
- @positional(2)
- def my_method(cls, pos1, kwonly1=None):
- ...
-
-The positional decorator behavior is controlled by
-util.positional_parameters_enforcement, which may be set to
-POSITIONAL_EXCEPTION, POSITIONAL_WARNING or POSITIONAL_IGNORE to raise an
-exception, log a warning, or do nothing, respectively, if a declaration is
-violated.
-
-Args:
- max_positional_arguments: Maximum number of positional arguments. All
- parameters after the this index must be keyword only.
-
-Returns:
- A decorator that prevents using arguments after max_positional_args from
- being used as positional parameters.
-
-Raises:
- TypeError if a key-word only argument is provided as a positional
- parameter, but only if util.positional_parameters_enforcement is set to
- POSITIONAL_EXCEPTION.
-
-
-Converts scope value to a string.
-
-If scopes is a string then it is simply passed through. If scopes is an
-iterable then a string is returned that is all the individual scopes
-concatenated with spaces.
-
-Args:
- scopes: string or iterable of strings, the scopes.
-
-Returns:
- The scopes formatted as a single string.
-
-
-Converts a dictionary to a tuple that can be used as an immutable key.
-
-The resulting key is always sorted so that logically equivalent dictionaries
-always produce an identical tuple for a key.
-
-Args:
- dictionary: the dictionary to use as the key.
-
-Returns:
- A tuple representing the dictionary in it's naturally sorted ordering.
-
-
-Adds a query parameter to a url.
-
-Replaces the current value if it already exists in the URL.
-
-Args:
- url: string, url to add the query parameter to.
- name: string, query parameter name.
- value: string, query parameter value.
-
-Returns:
- Updated query parameter. Does not update the url if value is None.
-
-
- 1#!/usr/bin/env python
- 2#
- 3# Copyright 2014 Google Inc. All rights reserved.
- 4#
- 5# Licensed under the Apache License, Version 2.0 (the "License");
- 6# you may not use this file except in compliance with the License.
- 7# You may obtain a copy of the License at
- 8#
- 9# http://www.apache.org/licenses/LICENSE-2.0
- 10#
- 11# Unless required by applicable law or agreed to in writing, software
- 12# distributed under the License is distributed on an "AS IS" BASIS,
- 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- 14# See the License for the specific language governing permissions and
- 15# limitations under the License.
- 16#
- 17
- 18"""Common utility library."""
- 19
- 20__author__=[
- 21'rafek@google.com (Rafe Kaplan)',
- 22'guido@google.com (Guido van Rossum)',
- 23]
- 24
- 25__all__=[
- 26'positional',
- 27'POSITIONAL_WARNING',
- 28'POSITIONAL_EXCEPTION',
- 29'POSITIONAL_IGNORE',
- 30]
- 31
- 32importinspect
- 33importlogging
- 34importsys
- 35importtypes
- 36
- 37importsix
- 38fromsix.movesimporturllib
- 39
- 40
- 41logger=logging.getLogger(__name__)
- 42
- 43POSITIONAL_WARNING='WARNING'
- 44POSITIONAL_EXCEPTION='EXCEPTION'
- 45POSITIONAL_IGNORE='IGNORE'
- 46POSITIONAL_SET=frozenset([POSITIONAL_WARNING,POSITIONAL_EXCEPTION,
- 47POSITIONAL_IGNORE])
- 48
- 49positional_parameters_enforcement=POSITIONAL_WARNING
- 50
-
52"""A decorator to declare that only the first N arguments my be positional.
- 53
- 54 This decorator makes it easy to support Python 3 style key-word only
- 55 parameters. For example, in Python 3 it is possible to write:
- 56
- 57 def fn(pos1, *, kwonly1=None, kwonly1=None):
- 58 ...
- 59
- 60 All named parameters after * must be a keyword:
- 61
- 62 fn(10, 'kw1', 'kw2') # Raises exception.
- 63 fn(10, kwonly1='kw1') # Ok.
- 64
- 65 Example:
- 66 To define a function like above, do:
- 67
- 68 @positional(1)
- 69 def fn(pos1, kwonly1=None, kwonly2=None):
- 70 ...
- 71
- 72 If no default value is provided to a keyword argument, it becomes a required
- 73 keyword argument:
- 74
- 75 @positional(0)
- 76 def fn(required_kw):
- 77 ...
- 78
- 79 This must be called with the keyword parameter:
- 80
- 81 fn() # Raises exception.
- 82 fn(10) # Raises exception.
- 83 fn(required_kw=10) # Ok.
- 84
- 85 When defining instance or class methods always remember to account for
- 86 'self' and 'cls':
- 87
- 88 class MyClass(object):
- 89
- 90 @positional(2)
- 91 def my_method(self, pos1, kwonly1=None):
- 92 ...
- 93
- 94 @classmethod
- 95 @positional(2)
- 96 def my_method(cls, pos1, kwonly1=None):
- 97 ...
- 98
- 99 The positional decorator behavior is controlled by
-100 util.positional_parameters_enforcement, which may be set to
-101 POSITIONAL_EXCEPTION, POSITIONAL_WARNING or POSITIONAL_IGNORE to raise an
-102 exception, log a warning, or do nothing, respectively, if a declaration is
-103 violated.
-104
-105 Args:
-106 max_positional_arguments: Maximum number of positional arguments. All
-107 parameters after the this index must be keyword only.
-108
-109 Returns:
-110 A decorator that prevents using arguments after max_positional_args from
-111 being used as positional parameters.
-112
-113 Raises:
-114 TypeError if a key-word only argument is provided as a positional
-115 parameter, but only if util.positional_parameters_enforcement is set to
-116 POSITIONAL_EXCEPTION.
-117 """
-118defpositional_decorator(wrapped):
-119defpositional_wrapper(*args,**kwargs):
-120iflen(args)>max_positional_args:
-121plural_s=''
-122ifmax_positional_args!=1:
-123plural_s='s'
-124message='%s() takes at most %d positional argument%s (%d given)'%(
-125wrapped.__name__,max_positional_args,plural_s,len(args))
-126ifpositional_parameters_enforcement==POSITIONAL_EXCEPTION:
-127raiseTypeError(message)
-128elifpositional_parameters_enforcement==POSITIONAL_WARNING:
-129logger.warning(message)
-130else:# IGNORE
-131pass
-132returnwrapped(*args,**kwargs)
-
143"""Converts scope value to a string.
-144
-145 If scopes is a string then it is simply passed through. If scopes is an
-146 iterable then a string is returned that is all the individual scopes
-147 concatenated with spaces.
-148
-149 Args:
-150 scopes: string or iterable of strings, the scopes.
-151
-152 Returns:
-153 The scopes formatted as a single string.
-154 """
-155try:
-156is_string=isinstance(scopes,basestring)
-157exceptNameError:
-158is_string=isinstance(scopes,str)
-159ifis_string:
-160returnscopes
-161else:
-162return' '.join(scopes)
-
166"""Converts a dictionary to a tuple that can be used as an immutable key.
-167
-168 The resulting key is always sorted so that logically equivalent dictionaries
-169 always produce an identical tuple for a key.
-170
-171 Args:
-172 dictionary: the dictionary to use as the key.
-173
-174 Returns:
-175 A tuple representing the dictionary in it's naturally sorted ordering.
-176 """
-177returntuple(sorted(dictionary.items()))
-
181"""Adds a query parameter to a url.
-182
-183 Replaces the current value if it already exists in the URL.
-184
-185 Args:
-186 url: string, url to add the query parameter to.
-187 name: string, query parameter name.
-188 value: string, query parameter value.
-189
-190 Returns:
-191 Updated query parameter. Does not update the url if value is None.
-192 """
-193ifvalueisNone:
-194returnurl
-195else:
-196parsed=list(urllib.parse.urlparse(url))
-197q=dict(urllib.parse.parse_qsl(parsed[4]))
-198q[name]=value
-199parsed[4]=urllib.parse.urlencode(q)
-200returnurllib.parse.urlunparse(parsed)
-
-Generates a URL-safe token for the given user, action, time tuple.
-
-Args:
- key: secret key to use.
- user_id: the user ID of the authenticated user.
- action_id: a string identifier of the action they requested
- authorization for.
- when: the time in seconds since the epoch at which the user was
- authorized for this action. If not set the current time is used.
-
-Returns:
- A string XSRF protection token.
-
-
-Validates that the given token authorizes the user for the action.
-
-Tokens are invalid if the time of issue is too old or if the token
-does not match what generateToken outputs (i.e. the token was forged).
-
-Args:
- key: secret key to use.
- token: a string of the token generated by generateToken.
- user_id: the user ID of the authenticated user.
- action_id: a string identifier of the action they requested
- authorization for.
-
-Returns:
- A boolean - True if the user is authorized for the action, False
- otherwise.
-
-
- 1#
- 2# Copyright 2014 the Melange authors.
- 3#
- 4# Licensed under the Apache License, Version 2.0 (the "License");
- 5# you may not use this file except in compliance with the License.
- 6# You may obtain a copy of the License at
- 7#
- 8# http://www.apache.org/licenses/LICENSE-2.0
- 9#
- 10# Unless required by applicable law or agreed to in writing, software
- 11# distributed under the License is distributed on an "AS IS" BASIS,
- 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- 13# See the License for the specific language governing permissions and
- 14# limitations under the License.
- 15
- 16"""Helper methods for creating & verifying XSRF tokens."""
- 17
- 18__authors__=[
- 19'"Doug Coker" <dcoker@google.com>',
- 20'"Joe Gregorio" <jcgregorio@google.com>',
- 21]
- 22
- 23
- 24importbase64
- 25importhmac
- 26importtime
- 27
- 28importsix
- 29fromoauth2clientimportutil
- 30
- 31
- 32# Delimiter character
- 33DELIMITER=b':'
- 34
- 35
- 36# 1 hour in seconds
- 37DEFAULT_TIMEOUT_SECS=1*60*60
-
51"""Generates a URL-safe token for the given user, action, time tuple.
- 52
- 53 Args:
- 54 key: secret key to use.
- 55 user_id: the user ID of the authenticated user.
- 56 action_id: a string identifier of the action they requested
- 57 authorization for.
- 58 when: the time in seconds since the epoch at which the user was
- 59 authorized for this action. If not set the current time is used.
- 60
- 61 Returns:
- 62 A string XSRF protection token.
- 63 """
- 64when=_force_bytes(whenorint(time.time()))
- 65digester=hmac.new(_force_bytes(key))
- 66digester.update(_force_bytes(user_id))
- 67digester.update(DELIMITER)
- 68digester.update(_force_bytes(action_id))
- 69digester.update(DELIMITER)
- 70digester.update(when)
- 71digest=digester.digest()
- 72
- 73token=base64.urlsafe_b64encode(digest+DELIMITER+when)
- 74returntoken
-
79"""Validates that the given token authorizes the user for the action.
- 80
- 81 Tokens are invalid if the time of issue is too old or if the token
- 82 does not match what generateToken outputs (i.e. the token was forged).
- 83
- 84 Args:
- 85 key: secret key to use.
- 86 token: a string of the token generated by generateToken.
- 87 user_id: the user ID of the authenticated user.
- 88 action_id: a string identifier of the action they requested
- 89 authorization for.
- 90
- 91 Returns:
- 92 A boolean - True if the user is authorized for the action, False
- 93 otherwise.
- 94 """
- 95ifnottoken:
- 96returnFalse
- 97try:
- 98decoded=base64.urlsafe_b64decode(token)
- 99token_time=int(decoded.split(DELIMITER)[-1])
-100except(TypeError,ValueError):
-101returnFalse
-102ifcurrent_timeisNone:
-103current_time=time.time()
-104# If the token is too old it's not valid.
-105ifcurrent_time-token_time>DEFAULT_TIMEOUT_SECS:
-106returnFalse
-107
-108# The given token should match the generated one with the same time.
-109expected_token=generate_token(key,user_id,action_id=action_id,
-110when=token_time)
-111iflen(token)!=len(expected_token):
-112returnFalse
-113
-114# Perform constant time comparison to avoid timing attacks
-115different=0
-116forx,yinzip(bytearray(token),bytearray(expected_token)):
-117different|=x^y
-118returnnotdifferent
-
When javascript is enabled, this page will redirect URLs of
-the form redirect.html#dotted.name to the
-documentation for the object with the given fully-qualified
-dotted name.