1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
|
<html>
<head>
<title>pylons.decorators.secure</title>
</head>
<body>
pylons.decorators.secure
<style>
.coverage pre {float: left; margin: 0px 1em; border: none;
padding: 0px; }
.num pre { margin: 0px }
.nocov, .nocov pre {background-color: #faa}
.cov, .cov pre {background-color: #cfc}
div.coverage div { clear: both; height: 1.1em}
</style>
<div class="stats">
Covered: 80 lines<br/>
Missed: 0 lines<br/>
Skipped 31 lines<br/>
Percent: 100 %<br/>
</div>
<div class="coverage">
<div class="cov"><span class="num"><pre> 1</pre></span><pre>"""Security related decorators"""</pre></div>
<div class="cov"><span class="num"><pre> 2</pre></span><pre>import logging</pre></div>
<div class="cov"><span class="num"><pre> 3</pre></span><pre>import urlparse</pre></div>
<div class="skip"><span class="num"><pre> 4</pre></span><pre></pre></div>
<div class="cov"><span class="num"><pre> 5</pre></span><pre>from decorator import decorator</pre></div>
<div class="cov"><span class="num"><pre> 6</pre></span><pre>try:</pre></div>
<div class="cov"><span class="num"><pre> 7</pre></span><pre> import webhelpers.html.secure_form as secure_form</pre></div>
<div class="cov"><span class="num"><pre> 8</pre></span><pre>except ImportError:</pre></div>
<div class="cov"><span class="num"><pre> 9</pre></span><pre> import webhelpers.pylonslib.secure_form as secure_form</pre></div>
<div class="skip"><span class="num"><pre> 10</pre></span><pre></pre></div>
<div class="cov"><span class="num"><pre> 11</pre></span><pre>from pylons.controllers.util import abort, redirect</pre></div>
<div class="cov"><span class="num"><pre> 12</pre></span><pre>from pylons.decorators.util import get_pylons</pre></div>
<div class="skip"><span class="num"><pre> 13</pre></span><pre></pre></div>
<div class="cov"><span class="num"><pre> 14</pre></span><pre>__all__ = ['authenticate_form', 'https']</pre></div>
<div class="skip"><span class="num"><pre> 15</pre></span><pre></pre></div>
<div class="cov"><span class="num"><pre> 16</pre></span><pre>log = logging.getLogger(__name__)</pre></div>
<div class="skip"><span class="num"><pre> 17</pre></span><pre></pre></div>
<div class="cov"><span class="num"><pre> 18</pre></span><pre>csrf_detected_message = (</pre></div>
<div class="cov"><span class="num"><pre> 19</pre></span><pre> "Cross-site request forgery detected, request denied. See "</pre></div>
<div class="cov"><span class="num"><pre> 20</pre></span><pre> "http://en.wikipedia.org/wiki/Cross-site_request_forgery for more "</pre></div>
<div class="cov"><span class="num"><pre> 21</pre></span><pre> "information.")</pre></div>
<div class="skip"><span class="num"><pre> 22</pre></span><pre></pre></div>
<div class="cov"><span class="num"><pre> 23</pre></span><pre>def authenticated_form(params):</pre></div>
<div class="cov"><span class="num"><pre> 24</pre></span><pre> submitted_token = params.get(secure_form.token_key)</pre></div>
<div class="cov"><span class="num"><pre> 25</pre></span><pre> return submitted_token is not None and \</pre></div>
<div class="cov"><span class="num"><pre> 26</pre></span><pre> submitted_token == secure_form.authentication_token()</pre></div>
<div class="skip"><span class="num"><pre> 27</pre></span><pre></pre></div>
<div class="skip"><span class="num"><pre> 28</pre></span><pre></pre></div>
<div class="cov"><span class="num"><pre> 29</pre></span><pre>@decorator</pre></div>
<div class="cov"><span class="num"><pre> 30</pre></span><pre>def authenticate_form(func, *args, **kwargs):</pre></div>
<div class="cov"><span class="num"><pre> 31</pre></span><pre> """Decorator for authenticating a form</pre></div>
<div class="skip"><span class="num"><pre> 32</pre></span><pre></pre></div>
<div class="cov"><span class="num"><pre> 33</pre></span><pre> This decorator uses an authorization token stored in the client's</pre></div>
<div class="cov"><span class="num"><pre> 34</pre></span><pre> session for prevention of certain Cross-site request forgery (CSRF)</pre></div>
<div class="cov"><span class="num"><pre> 35</pre></span><pre> attacks (See</pre></div>
<div class="cov"><span class="num"><pre> 36</pre></span><pre> http://en.wikipedia.org/wiki/Cross-site_request_forgery for more</pre></div>
<div class="cov"><span class="num"><pre> 37</pre></span><pre> information).</pre></div>
<div class="skip"><span class="num"><pre> 38</pre></span><pre></pre></div>
<div class="cov"><span class="num"><pre> 39</pre></span><pre> For use with the ``webhelpers.html.secure_form`` helper functions.</pre></div>
<div class="skip"><span class="num"><pre> 40</pre></span><pre></pre></div>
<div class="cov"><span class="num"><pre> 41</pre></span><pre> """</pre></div>
<div class="cov"><span class="num"><pre> 42</pre></span><pre> request = get_pylons(args).request</pre></div>
<div class="cov"><span class="num"><pre> 43</pre></span><pre> if authenticated_form(request.POST):</pre></div>
<div class="cov"><span class="num"><pre> 44</pre></span><pre> del request.POST[secure_form.token_key]</pre></div>
<div class="cov"><span class="num"><pre> 45</pre></span><pre> return func(*args, **kwargs)</pre></div>
<div class="cov"><span class="num"><pre> 46</pre></span><pre> else:</pre></div>
<div class="cov"><span class="num"><pre> 47</pre></span><pre> log.warn('Cross-site request forgery detected, request denied: %r '</pre></div>
<div class="cov"><span class="num"><pre> 48</pre></span><pre> 'REMOTE_ADDR: %s' % (request, request.remote_addr))</pre></div>
<div class="cov"><span class="num"><pre> 49</pre></span><pre> abort(403, detail=csrf_detected_message)</pre></div>
<div class="skip"><span class="num"><pre> 50</pre></span><pre></pre></div>
<div class="skip"><span class="num"><pre> 51</pre></span><pre></pre></div>
<div class="cov"><span class="num"><pre> 52</pre></span><pre>def https(url_or_callable=None):</pre></div>
<div class="cov"><span class="num"><pre> 53</pre></span><pre> """Decorator to redirect to the SSL version of a page if not</pre></div>
<div class="cov"><span class="num"><pre> 54</pre></span><pre> currently using HTTPS. Apply this decorator to controller methods</pre></div>
<div class="cov"><span class="num"><pre> 55</pre></span><pre> (actions).</pre></div>
<div class="skip"><span class="num"><pre> 56</pre></span><pre></pre></div>
<div class="cov"><span class="num"><pre> 57</pre></span><pre> Takes a url argument: either a string url, or a callable returning a</pre></div>
<div class="cov"><span class="num"><pre> 58</pre></span><pre> string url. The callable will be called with no arguments when the</pre></div>
<div class="cov"><span class="num"><pre> 59</pre></span><pre> decorated method is called. The url's scheme will be rewritten to</pre></div>
<div class="cov"><span class="num"><pre> 60</pre></span><pre> https if necessary.</pre></div>
<div class="skip"><span class="num"><pre> 61</pre></span><pre></pre></div>
<div class="cov"><span class="num"><pre> 62</pre></span><pre> Non-HTTPS POST requests are aborted (405 response code) by this</pre></div>
<div class="cov"><span class="num"><pre> 63</pre></span><pre> decorator.</pre></div>
<div class="skip"><span class="num"><pre> 64</pre></span><pre></pre></div>
<div class="cov"><span class="num"><pre> 65</pre></span><pre> Example:</pre></div>
<div class="skip"><span class="num"><pre> 66</pre></span><pre></pre></div>
<div class="cov"><span class="num"><pre> 67</pre></span><pre> .. code-block:: python</pre></div>
<div class="skip"><span class="num"><pre> 68</pre></span><pre></pre></div>
<div class="skip"><span class="num"><pre> 69</pre></span><pre> # redirect to HTTPS /pylons</pre></div>
<div class="cov"><span class="num"><pre> 70</pre></span><pre> @https('/pylons')</pre></div>
<div class="cov"><span class="num"><pre> 71</pre></span><pre> def index(self):</pre></div>
<div class="cov"><span class="num"><pre> 72</pre></span><pre> do_secure()</pre></div>
<div class="skip"><span class="num"><pre> 73</pre></span><pre></pre></div>
<div class="skip"><span class="num"><pre> 74</pre></span><pre> # redirect to HTTPS /auth/login, delaying the url() call until</pre></div>
<div class="skip"><span class="num"><pre> 75</pre></span><pre> # later (as the url object may not be functional when the</pre></div>
<div class="skip"><span class="num"><pre> 76</pre></span><pre> # decorator/method are defined)</pre></div>
<div class="cov"><span class="num"><pre> 77</pre></span><pre> @https(lambda: url(controller='auth', action='login'))</pre></div>
<div class="cov"><span class="num"><pre> 78</pre></span><pre> def login(self):</pre></div>
<div class="cov"><span class="num"><pre> 79</pre></span><pre> do_secure()</pre></div>
<div class="skip"><span class="num"><pre> 80</pre></span><pre></pre></div>
<div class="skip"><span class="num"><pre> 81</pre></span><pre> # redirect to HTTPS version of myself</pre></div>
<div class="cov"><span class="num"><pre> 82</pre></span><pre> @https()</pre></div>
<div class="cov"><span class="num"><pre> 83</pre></span><pre> def get(self):</pre></div>
<div class="cov"><span class="num"><pre> 84</pre></span><pre> do_secure()</pre></div>
<div class="skip"><span class="num"><pre> 85</pre></span><pre></pre></div>
<div class="cov"><span class="num"><pre> 86</pre></span><pre> """</pre></div>
<div class="cov"><span class="num"><pre> 87</pre></span><pre> def wrapper(func, *args, **kwargs):</pre></div>
<div class="cov"><span class="num"><pre> 88</pre></span><pre> """Decorator Wrapper function"""</pre></div>
<div class="cov"><span class="num"><pre> 89</pre></span><pre> request = get_pylons(args).request</pre></div>
<div class="cov"><span class="num"><pre> 90</pre></span><pre> if request.scheme.lower() == 'https':</pre></div>
<div class="cov"><span class="num"><pre> 91</pre></span><pre> return func(*args, **kwargs)</pre></div>
<div class="cov"><span class="num"><pre> 92</pre></span><pre> if request.method.upper() == 'POST':</pre></div>
<div class="skip"><span class="num"><pre> 93</pre></span><pre> # don't allow POSTs (raises an exception)</pre></div>
<div class="cov"><span class="num"><pre> 94</pre></span><pre> abort(405, headers=[('Allow', 'GET')])</pre></div>
<div class="skip"><span class="num"><pre> 95</pre></span><pre></pre></div>
<div class="cov"><span class="num"><pre> 96</pre></span><pre> if url_or_callable is None:</pre></div>
<div class="cov"><span class="num"><pre> 97</pre></span><pre> url = request.url</pre></div>
<div class="cov"><span class="num"><pre> 98</pre></span><pre> elif callable(url_or_callable):</pre></div>
<div class="cov"><span class="num"><pre> 99</pre></span><pre> url = url_or_callable()</pre></div>
<div class="cov"><span class="num"><pre>100</pre></span><pre> else:</pre></div>
<div class="cov"><span class="num"><pre>101</pre></span><pre> url = url_or_callable</pre></div>
<div class="skip"><span class="num"><pre>102</pre></span><pre> # Ensure an https scheme, which also needs a host</pre></div>
<div class="cov"><span class="num"><pre>103</pre></span><pre> parts = urlparse.urlparse(url)</pre></div>
<div class="cov"><span class="num"><pre>104</pre></span><pre> url = urlparse.urlunparse(('https', parts[1] or request.host) +</pre></div>
<div class="cov"><span class="num"><pre>105</pre></span><pre> parts[2:])</pre></div>
<div class="skip"><span class="num"><pre>106</pre></span><pre></pre></div>
<div class="cov"><span class="num"><pre>107</pre></span><pre> log.debug('Redirecting non-https request: %s to: %s',</pre></div>
<div class="cov"><span class="num"><pre>108</pre></span><pre> request.path_info, url)</pre></div>
<div class="cov"><span class="num"><pre>109</pre></span><pre> redirect(url)</pre></div>
<div class="cov"><span class="num"><pre>110</pre></span><pre> return decorator(wrapper)</pre></div>
<div class="skip"><span class="num"><pre>111</pre></span><pre></pre></div>
</div>
</body>
</html>
|