EVOLUTION-MANAGER
Edit File: flask_fas.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>FAS Flask Auth Plugin — python-fedora 0.10.0 documentation</title> <link rel="stylesheet" href="_static/default.css" type="text/css" /> <link rel="stylesheet" href="_static/pygments.css" type="text/css" /> <script type="text/javascript"> var DOCUMENTATION_OPTIONS = { URL_ROOT: '', VERSION: '0.10.0', COLLAPSE_INDEX: false, FILE_SUFFIX: '.html', HAS_SOURCE: true }; </script> <script type="text/javascript" src="_static/jquery.js"></script> <script type="text/javascript" src="_static/underscore.js"></script> <script type="text/javascript" src="_static/doctools.js"></script> <link rel="search" type="application/opensearchdescription+xml" title="Search within python-fedora 0.10.0 documentation" href="_static/opensearch.xml"/> <link rel="top" title="python-fedora 0.10.0 documentation" href="index.html" /> <link rel="up" title="Authentication to FAS" href="auth.html" /> <link rel="next" title="FAS Flask OpenID Auth Plugin" href="flask_fas_openid.html" /> <link rel="prev" title="Fedora Django Authentication Backend" href="django.html" /> </head> <body> <div class="related"> <h3>Navigation</h3> <ul> <li class="right" style="margin-right: 10px"> <a href="genindex.html" title="General Index" accesskey="I">index</a></li> <li class="right" > <a href="py-modindex.html" title="Python Module Index" >modules</a> |</li> <li class="right" > <a href="flask_fas_openid.html" title="FAS Flask OpenID Auth Plugin" accesskey="N">next</a> |</li> <li class="right" > <a href="django.html" title="Fedora Django Authentication Backend" accesskey="P">previous</a> |</li> <li><a href="index.html">python-fedora 0.10.0 documentation</a> »</li> <li><a href="auth.html" accesskey="U">Authentication to FAS</a> »</li> </ul> </div> <div class="document"> <div class="documentwrapper"> <div class="bodywrapper"> <div class="body"> <div class="section" id="fas-flask-auth-plugin"> <h1>FAS Flask Auth Plugin<a class="headerlink" href="#fas-flask-auth-plugin" title="Permalink to this headline">¶</a></h1> <table class="docutils field-list" frame="void" rules="none"> <col class="field-name" /> <col class="field-body" /> <tbody valign="top"> <tr class="field-odd field"><th class="field-name">Authors:</th><td class="field-body">Toshio Kuratomi, Ian Weller</td> </tr> <tr class="field-even field"><th class="field-name">Date:</th><td class="field-body">29 October 2012</td> </tr> <tr class="field-odd field"><th class="field-name">For Version:</th><td class="field-body">0.3.x</td> </tr> </tbody> </table> <p>The <a class="reference internal" href="existing.html#fedora-account-system"><em>Fedora Account System</em></a> has a <a class="reference internal" href="glossary.html#term-json"><em class="xref std std-term">JSON</em></a> interface that we make use of to authenticate users in our web apps. For our <a class="reference internal" href="glossary.html#term-flask"><em class="xref std std-term">Flask</em></a> applications we have an identity provider that has <a class="reference internal" href="glossary.html#term-single-sign-on"><em class="xref std std-term">single sign-on</em></a> with our <a class="reference internal" href="glossary.html#term-turbogears"><em class="xref std std-term">TurboGears</em></a> 1 and 2 applications. It does not protect against <a class="reference internal" href="glossary.html#term-csrf"><em class="xref std std-term">CSRF</em></a> attacks in the identity layer. The flask-wtf forms package should be used to provide that.</p> <div class="section" id="configuration"> <h2>Configuration<a class="headerlink" href="#configuration" title="Permalink to this headline">¶</a></h2> <p>The FAS auth plugin has several config values that can be used to control how the auth plugin functions. You can set these in your application’s config file.</p> <dl class="docutils"> <dt>FAS_BASE_URL</dt> <dd>Set this to the URL of the FAS server you are authenticating against. Default is “<a class="reference external" href="https://admin.fedoraproject.org/accounts/">https://admin.fedoraproject.org/accounts/</a>“</dd> <dt>FAS_USER_AGENT</dt> <dd>User agent string to be used when connecting to FAS. You can set this to something specific to your application to aid in debugging a connection to the FAS server as it will show up in the FAS server’s logs. Default is “Flask-FAS/0.3“</dd> <dt>FAS_CHECK_CERT</dt> <dd>When set, this will check the SSL Certificate for the FAS server to make sure that it is who it claims to be. This is useful to set to False when testing against a local FAS server but should always be set to True in production. Default: True</dd> <dt>FAS_COOKIE_NAME</dt> <dd>The name of the cookie used to store the session id across the Fedora Applications that support <a class="reference internal" href="glossary.html#term-single-sign-on"><em class="xref std std-term">single sign-on</em></a>. Default: “tg-visit”</dd> <dt>FAS_FLASK_COOKIE_REQUIRES_HTTPS</dt> <dd>When this is set to True, the session cookie will only be returned to the server via ssl (https). If you connect to the server via plain http, the cookie will not be sent. This prevents sniffing of the cookie contents. This may be set to False when testing your application but should always be set to True in production. Default is True.</dd> </dl> </div> <div class="section" id="sample-application"> <h2>Sample Application<a class="headerlink" href="#sample-application" title="Permalink to this headline">¶</a></h2> <p>The following is a sample, minimal flask application that uses fas_flask for authentication:</p> <div class="highlight-python"><div class="highlight"><pre><span class="c">#!/usr/bin/python -tt</span> <span class="c"># Flask-FAS - A Flask extension for authorizing users with FAS</span> <span class="c"># Primary maintainer: Ian Weller <ianweller@fedoraproject.org></span> <span class="c">#</span> <span class="c"># Copyright (c) 2012, Red Hat, Inc.</span> <span class="c">#</span> <span class="c"># Redistribution and use in source and binary forms, with or without</span> <span class="c"># modification, are permitted provided that the following conditions are met:</span> <span class="c">#</span> <span class="c"># * Redistributions of source code must retain the above copyright notice, this</span> <span class="c"># list of conditions and the following disclaimer.</span> <span class="c"># * Redistributions in binary form must reproduce the above copyright notice,</span> <span class="c"># this list of conditions and the following disclaimer in the documentation</span> <span class="c"># and/or other materials provided with the distribution.</span> <span class="c"># * Neither the name of the Red Hat, Inc. nor the names of its contributors may</span> <span class="c"># be used to endorse or promote products derived from this software without</span> <span class="c"># specific prior written permission.</span> <span class="c">#</span> <span class="c"># THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY</span> <span class="c"># EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED</span> <span class="c"># WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE</span> <span class="c"># DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY</span> <span class="c"># DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES</span> <span class="c"># (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;</span> <span class="c"># LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON</span> <span class="c"># ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT</span> <span class="c"># (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS</span> <span class="c"># SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</span> <span class="c"># This is a sample application. In addition to using Flask-FAS, it uses</span> <span class="c"># Flask-WTF (WTForms) to handle the login form. Use of Flask-WTF is highly</span> <span class="c"># recommended because of its CSRF checking.</span> <span class="kn">import</span> <span class="nn">flask</span> <span class="kn">from</span> <span class="nn">flask.ext</span> <span class="kn">import</span> <span class="n">wtf</span> <span class="kn">from</span> <span class="nn">flask.ext.fas</span> <span class="kn">import</span> <span class="n">FAS</span><span class="p">,</span> <span class="n">fas_login_required</span> <span class="c"># Set up Flask application</span> <span class="n">app</span> <span class="o">=</span> <span class="n">flask</span><span class="o">.</span><span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span> <span class="c"># Set up FAS extension</span> <span class="n">fas</span> <span class="o">=</span> <span class="n">FAS</span><span class="p">(</span><span class="n">app</span><span class="p">)</span> <span class="c"># Application configuration</span> <span class="c"># SECRET_KEY is necessary to CSRF in WTForms. It nees to be secret to</span> <span class="c"># make the csrf tokens unguessable but if you have multiple servers behind</span> <span class="c"># a load balancer, the key needs to be the same on each.</span> <span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="p">[</span><span class="s">'SECRET_KEY'</span><span class="p">]</span> <span class="o">=</span> <span class="s">'change me!'</span> <span class="c"># Other configuration options for Flask-FAS:</span> <span class="c"># FAS_BASE_URL: the base URL for the accounts system</span> <span class="c"># (default https://admin.fedoraproject.org/accounts/)</span> <span class="c"># FAS_CHECK_CERT: check the SSL certificate of FAS (default True)</span> <span class="c"># FAS_FLASK_COOKIE_REQUIRES_HTTPS: send the 'secure' option with</span> <span class="c"># the login cookie (default True)</span> <span class="c"># You should use these options' defaults for production applications!</span> <span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="p">[</span><span class="s">'FAS_BASE_URL'</span><span class="p">]</span> <span class="o">=</span> <span class="s">'https://fakefas.fedoraproject.org/accounts/'</span> <span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="p">[</span><span class="s">'FAS_CHECK_CERT'</span><span class="p">]</span> <span class="o">=</span> <span class="bp">False</span> <span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="p">[</span><span class="s">'FAS_FLASK_COOKIE_REQUIRES_HTTPS'</span><span class="p">]</span> <span class="o">=</span> <span class="bp">False</span> <span class="c"># A basic login form</span> <span class="k">class</span> <span class="nc">LoginForm</span><span class="p">(</span><span class="n">wtf</span><span class="o">.</span><span class="n">Form</span><span class="p">):</span> <span class="n">username</span> <span class="o">=</span> <span class="n">wtf</span><span class="o">.</span><span class="n">TextField</span><span class="p">(</span><span class="s">'Username'</span><span class="p">,</span> <span class="p">[</span><span class="n">wtf</span><span class="o">.</span><span class="n">validators</span><span class="o">.</span><span class="n">Required</span><span class="p">()])</span> <span class="n">password</span> <span class="o">=</span> <span class="n">wtf</span><span class="o">.</span><span class="n">PasswordField</span><span class="p">(</span><span class="s">'Password'</span><span class="p">,</span> <span class="p">[</span><span class="n">wtf</span><span class="o">.</span><span class="n">validators</span><span class="o">.</span><span class="n">Required</span><span class="p">()])</span> <span class="c"># Inline templates keep this test application all in one file. Don't do this in</span> <span class="c"># a real application. Please.</span> <span class="n">TEMPLATE_START</span> <span class="o">=</span> <span class="s">"""</span> <span class="s"><h1>Flask-FAS test app</h1></span> <span class="s">{</span><span class="si">% i</span><span class="s">f g.fas_user %}</span> <span class="s"> <p>Hello, {{ g.fas_user.username }} &mdash;</span> <span class="s"> <a href="{{ url_for("logout") }}">Log out</a></span> <span class="s">{</span><span class="si">% e</span><span class="s">lse %}</span> <span class="s"> <p>You are not logged in &mdash;</span> <span class="s"> <a href="{{ url_for("auth_login", next=request.url) + '' }}">Log in</a></span> <span class="s">{</span><span class="si">% e</span><span class="s">ndif %}</span> <span class="s">&mdash; <a href="{{ url_for("index") }}">Main page</a></p></span> <span class="s">"""</span> <span class="nd">@app.route</span><span class="p">(</span><span class="s">'/'</span><span class="p">)</span> <span class="k">def</span> <span class="nf">index</span><span class="p">():</span> <span class="n">data</span> <span class="o">=</span> <span class="n">TEMPLATE_START</span> <span class="n">data</span> <span class="o">+=</span> <span class="s">'<p><a href="</span><span class="si">%s</span><span class="s">">Check if you are cla+1</a></p>'</span> <span class="o">%</span> \ <span class="n">flask</span><span class="o">.</span><span class="n">url_for</span><span class="p">(</span><span class="s">'claplusone'</span><span class="p">)</span> <span class="n">data</span> <span class="o">+=</span> <span class="s">'<p><a href="</span><span class="si">%s</span><span class="s">">See a secret message (requires login)</a></p>'</span> <span class="o">%</span> \ <span class="n">flask</span><span class="o">.</span><span class="n">url_for</span><span class="p">(</span><span class="s">'secret'</span><span class="p">)</span> <span class="k">return</span> <span class="n">flask</span><span class="o">.</span><span class="n">render_template_string</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="nd">@app.route</span><span class="p">(</span><span class="s">'/login'</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s">'GET'</span><span class="p">,</span> <span class="s">'POST'</span><span class="p">])</span> <span class="k">def</span> <span class="nf">auth_login</span><span class="p">():</span> <span class="c"># Your application should probably do some checking to make sure the URL</span> <span class="c"># given in the next request argument is sane. (For example, having next set</span> <span class="c"># to the login page will cause a redirect loop.) Some more information:</span> <span class="c"># http://flask.pocoo.org/snippets/62/</span> <span class="k">if</span> <span class="s">'next'</span> <span class="ow">in</span> <span class="n">flask</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">args</span><span class="p">:</span> <span class="n">next_url</span> <span class="o">=</span> <span class="n">flask</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">args</span><span class="p">[</span><span class="s">'next'</span><span class="p">]</span> <span class="k">else</span><span class="p">:</span> <span class="n">next_url</span> <span class="o">=</span> <span class="n">flask</span><span class="o">.</span><span class="n">url_for</span><span class="p">(</span><span class="s">'index'</span><span class="p">)</span> <span class="c"># If user is already logged in, return them to where they were last</span> <span class="k">if</span> <span class="n">flask</span><span class="o">.</span><span class="n">g</span><span class="o">.</span><span class="n">fas_user</span><span class="p">:</span> <span class="k">return</span> <span class="n">flask</span><span class="o">.</span><span class="n">redirect</span><span class="p">(</span><span class="n">next_url</span><span class="p">)</span> <span class="c"># Init login form</span> <span class="n">form</span> <span class="o">=</span> <span class="n">LoginForm</span><span class="p">()</span> <span class="c"># Init template</span> <span class="n">data</span> <span class="o">=</span> <span class="n">TEMPLATE_START</span> <span class="n">data</span> <span class="o">+=</span> <span class="p">(</span><span class="s">'<p>Log into the <a href="{{ config.FAS_BASE_URL }}">'</span> <span class="s">'Fedora Accounts System</a>:'</span><span class="p">)</span> <span class="c"># If this is POST, process the form</span> <span class="k">if</span> <span class="n">form</span><span class="o">.</span><span class="n">validate_on_submit</span><span class="p">():</span> <span class="k">if</span> <span class="n">fas</span><span class="o">.</span><span class="n">login</span><span class="p">(</span><span class="n">form</span><span class="o">.</span><span class="n">username</span><span class="o">.</span><span class="n">data</span><span class="p">,</span> <span class="n">form</span><span class="o">.</span><span class="n">password</span><span class="o">.</span><span class="n">data</span><span class="p">):</span> <span class="c"># Login successful, return</span> <span class="k">return</span> <span class="n">flask</span><span class="o">.</span><span class="n">redirect</span><span class="p">(</span><span class="n">next_url</span><span class="p">)</span> <span class="k">else</span><span class="p">:</span> <span class="c"># Login unsuccessful</span> <span class="n">data</span> <span class="o">+=</span> <span class="s">'<p style="color:red">Invalid login</p>'</span> <span class="n">data</span> <span class="o">+=</span> <span class="s">"""</span> <span class="s"><form action="" method="POST"></span> <span class="s">{</span><span class="si">% f</span><span class="s">or field in [form.username, form.password] %}</span> <span class="s"> <p>{{ field.label }}: {{ field|safe }}</p></span> <span class="s"> {</span><span class="si">% i</span><span class="s">f field.errors %}</span> <span class="s"> <ul style="color:red"></span> <span class="s"> {</span><span class="si">% f</span><span class="s">or error in field.errors %}</span> <span class="s"> <li>{{ error }}</li></span> <span class="s"> {</span><span class="si">% e</span><span class="s">ndfor %}</span> <span class="s"> </ul></span> <span class="s"> {</span><span class="si">% e</span><span class="s">ndif %}</span> <span class="s">{</span><span class="si">% e</span><span class="s">ndfor %}</span> <span class="s"><input type="submit" value="Log in"></span> <span class="s">{{ form.csrf_token }}</span> <span class="s"></form>"""</span> <span class="k">return</span> <span class="n">flask</span><span class="o">.</span><span class="n">render_template_string</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">form</span><span class="o">=</span><span class="n">form</span><span class="p">)</span> <span class="nd">@app.route</span><span class="p">(</span><span class="s">'/logout'</span><span class="p">)</span> <span class="k">def</span> <span class="nf">logout</span><span class="p">():</span> <span class="k">if</span> <span class="n">flask</span><span class="o">.</span><span class="n">g</span><span class="o">.</span><span class="n">fas_user</span><span class="p">:</span> <span class="n">fas</span><span class="o">.</span><span class="n">logout</span><span class="p">()</span> <span class="k">return</span> <span class="n">flask</span><span class="o">.</span><span class="n">redirect</span><span class="p">(</span><span class="n">flask</span><span class="o">.</span><span class="n">url_for</span><span class="p">(</span><span class="s">'index'</span><span class="p">))</span> <span class="c"># This demonstrates the use of the fas_login_required decorator. The</span> <span class="c"># secret message can only be viewed by those who are logged in.</span> <span class="nd">@app.route</span><span class="p">(</span><span class="s">'/secret'</span><span class="p">)</span> <span class="nd">@fas_login_required</span> <span class="k">def</span> <span class="nf">secret</span><span class="p">():</span> <span class="n">data</span> <span class="o">=</span> <span class="n">TEMPLATE_START</span> <span class="o">+</span> <span class="s">'<p>Be sure to drink your Ovaltine</p>'</span> <span class="k">return</span> <span class="n">flask</span><span class="o">.</span><span class="n">render_template_string</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="c"># This demonstrates checking for group membership inside of a function.</span> <span class="c"># The flask_fas adapter also provides a cla_plus_one_required decorator that</span> <span class="c"># can restrict a url so that you can only access it from an account that has</span> <span class="c"># cla +1.</span> <span class="nd">@app.route</span><span class="p">(</span><span class="s">'/claplusone'</span><span class="p">)</span> <span class="k">def</span> <span class="nf">claplusone</span><span class="p">():</span> <span class="n">data</span> <span class="o">=</span> <span class="n">TEMPLATE_START</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">flask</span><span class="o">.</span><span class="n">g</span><span class="o">.</span><span class="n">fas_user</span><span class="p">:</span> <span class="c"># Not logged in</span> <span class="k">return</span> <span class="n">flask</span><span class="o">.</span><span class="n">render_template_string</span><span class="p">(</span><span class="n">data</span> <span class="o">+</span> <span class="s">'<p>You must log in to check your cla +1 status</p>'</span><span class="p">)</span> <span class="n">non_cla_groups</span> <span class="o">=</span> <span class="p">[</span><span class="n">x</span><span class="o">.</span><span class="n">name</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">flask</span><span class="o">.</span><span class="n">g</span><span class="o">.</span><span class="n">fas_user</span><span class="o">.</span><span class="n">approved_memberships</span> <span class="k">if</span> <span class="n">x</span><span class="o">.</span><span class="n">group_type</span> <span class="o">!=</span> <span class="s">'cla'</span><span class="p">]</span> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">non_cla_groups</span><span class="p">)</span> <span class="o">></span> <span class="mi">0</span><span class="p">:</span> <span class="n">data</span> <span class="o">+=</span> <span class="s">'<p>Your account is cla+1.</p>'</span> <span class="k">else</span><span class="p">:</span> <span class="n">data</span> <span class="o">+=</span> <span class="s">'<p>Your account is <em>not</em> cla+1.</p>'</span> <span class="k">return</span> <span class="n">flask</span><span class="o">.</span><span class="n">render_template_string</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">'__main__'</span><span class="p">:</span> <span class="n">app</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="n">debug</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span> </pre></div> </div> </div> </div> </div> </div> </div> <div class="sphinxsidebar"> <div class="sphinxsidebarwrapper"> <h3><a href="index.html">Table Of Contents</a></h3> <ul> <li><a class="reference internal" href="#">FAS Flask Auth Plugin</a><ul> <li><a class="reference internal" href="#configuration">Configuration</a></li> <li><a class="reference internal" href="#sample-application">Sample Application</a></li> </ul> </li> </ul> <h4>Previous topic</h4> <p class="topless"><a href="django.html" title="previous chapter">Fedora Django Authentication Backend</a></p> <h4>Next topic</h4> <p class="topless"><a href="flask_fas_openid.html" title="next chapter">FAS Flask OpenID Auth Plugin</a></p> <h3>This Page</h3> <ul class="this-page-menu"> <li><a href="_sources/flask_fas.txt" rel="nofollow">Show Source</a></li> </ul> <div id="searchbox" style="display: none"> <h3>Quick search</h3> <form class="search" action="search.html" method="get"> <input type="text" name="q" /> <input type="submit" value="Go" /> <input type="hidden" name="check_keywords" value="yes" /> <input type="hidden" name="area" value="default" /> </form> <p class="searchtip" style="font-size: 90%"> Enter search terms or a module, class or function name. </p> </div> <script type="text/javascript">$('#searchbox').show(0);</script> </div> </div> <div class="clearer"></div> </div> <div class="related"> <h3>Navigation</h3> <ul> <li class="right" style="margin-right: 10px"> <a href="genindex.html" title="General Index" >index</a></li> <li class="right" > <a href="py-modindex.html" title="Python Module Index" >modules</a> |</li> <li class="right" > <a href="flask_fas_openid.html" title="FAS Flask OpenID Auth Plugin" >next</a> |</li> <li class="right" > <a href="django.html" title="Fedora Django Authentication Backend" >previous</a> |</li> <li><a href="index.html">python-fedora 0.10.0 documentation</a> »</li> <li><a href="auth.html" >Authentication to FAS</a> »</li> </ul> </div> <div class="footer"> © Copyright 2007-2018 Red Hat, Inc.. Last updated on Feb 01, 2018. Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.1.3. </div> </body> </html>