1
|
# mandayejs - saml reverse proxy
|
2
|
# Copyright (C) 2015 Entr'ouvert
|
3
|
#
|
4
|
# This program is free software: you can redistribute it and/or modify it
|
5
|
# under the terms of the GNU Affero General Public License as published
|
6
|
# by the Free Software Foundation, either version 3 of the License, or
|
7
|
# (at your option) any later version.
|
8
|
#
|
9
|
# This program is distributed in the hope that it will be useful,
|
10
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
# GNU Affero General Public License for more details.
|
13
|
#
|
14
|
# You should have received a copy of the GNU Affero General Public License
|
15
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
|
17
|
import os
|
18
|
import re
|
19
|
import json
|
20
|
import subprocess
|
21
|
import logging
|
22
|
import multiprocessing
|
23
|
|
24
|
from django.conf import settings
|
25
|
from django.http import SimpleCookie
|
26
|
from django.shortcuts import resolve_url
|
27
|
from django.utils.six.moves.urllib import parse as urlparse
|
28
|
|
29
|
|
30
|
|
31
|
logger = logging.getLogger(__name__)
|
32
|
|
33
|
|
34
|
def run(send_end, data, script):
|
35
|
phantom = subprocess.Popen([
|
36
|
settings.PHANTOM_JS_BINARY,
|
37
|
'--ignore-ssl-errors=yes', '--ssl-protocol=any',
|
38
|
os.path.join(settings.BASE_DIR, 'mandayejs', script)],
|
39
|
close_fds=True,
|
40
|
stdin=subprocess.PIPE,
|
41
|
stdout=subprocess.PIPE,
|
42
|
stderr=subprocess.PIPE
|
43
|
)
|
44
|
stdout, stderr = phantom.communicate(json.dumps(data))
|
45
|
|
46
|
try:
|
47
|
output = re.search('<mandayejs>(.*?)</mandayejs>', stdout, re.DOTALL)
|
48
|
if not output:
|
49
|
raise ValueError
|
50
|
stdout = output.group(1)
|
51
|
result = json.loads(stdout)
|
52
|
except (ValueError,):
|
53
|
result = {"result": "json_error"}
|
54
|
logger.error("invalid json: %s (stderr: %s)", stdout, stderr)
|
55
|
|
56
|
if result.get('stderr'):
|
57
|
logger.warning(result['stderr'])
|
58
|
if result.get('error'):
|
59
|
logger.error('Error occured: %s' % result.get('reason'))
|
60
|
|
61
|
send_end.send(result)
|
62
|
send_end.close()
|
63
|
|
64
|
|
65
|
def exec_phantom(data, script='do_login.js'):
|
66
|
recv_end, send_end = multiprocessing.Pipe(False)
|
67
|
process = multiprocessing.Process(target=run, args=(send_end, data, script))
|
68
|
process.start()
|
69
|
|
70
|
if recv_end.poll(settings.PHANTOM_JS_TIMEOUT):
|
71
|
result = recv_end.recv()
|
72
|
recv_end.close()
|
73
|
else:
|
74
|
process.terminate()
|
75
|
send_end.close()
|
76
|
# Don't log locators, they may contain credentials (passwords)
|
77
|
context = {k: v for k, v in data.items() if k != 'locators'}
|
78
|
logger.error("PhantomJS process timeout, context: %s" % context)
|
79
|
result = {'result': 'timeout'}
|
80
|
|
81
|
return result
|
82
|
|
83
|
|
84
|
def cookie_builder(headers):
|
85
|
"""Build Cookies from list of headers
|
86
|
"""
|
87
|
cookie = SimpleCookie()
|
88
|
for header in headers:
|
89
|
cookie.load('; '.join(header.values()).encode('ascii'))
|
90
|
|
91
|
return cookie
|
92
|
|
93
|
|
94
|
def get_logout_info(request):
|
95
|
"""Returns phantomjs logout prerequis
|
96
|
"""
|
97
|
from mandayejs.applications import get_app_settings
|
98
|
app_settings = get_app_settings()
|
99
|
|
100
|
data = {}
|
101
|
data['logout_locator'] = getattr(app_settings, 'SITE_LOGOUT_LOCATOR')
|
102
|
data['address'] = request.build_absolute_uri(resolve_url('home'))
|
103
|
forced_logout_scheme = getattr(settings, 'PHANTOM_JS_LOGOUT_SCHEME')
|
104
|
if forced_logout_scheme:
|
105
|
url = urlparse.urlparse(data['address'])
|
106
|
url = url._replace(scheme=forced_logout_scheme)
|
107
|
data['address'] = url.geturl()
|
108
|
cookies = SimpleCookie(request.META.get('HTTP_COOKIE'))
|
109
|
domain = request.META.get('SERVER_NAME')
|
110
|
|
111
|
# Phantomjs Cookies Format
|
112
|
data['cookies'] = [{
|
113
|
'name': key,
|
114
|
'value': value.value,
|
115
|
'domain': domain,
|
116
|
'path': '/'
|
117
|
} for key, value in cookies.items()]
|
118
|
|
119
|
return data
|
120
|
|
121
|
|
122
|
def get_password_field():
|
123
|
"""Return name of the password field
|
124
|
"""
|
125
|
from mandayejs.applications import get_app_settings
|
126
|
app_settings = get_app_settings()
|
127
|
try:
|
128
|
field_name = [field.get('name') for field in app_settings.SITE_LOCATORS
|
129
|
if field.get('kind') == 'password']
|
130
|
return field_name[0]
|
131
|
except (IndexError,):
|
132
|
return None
|
133
|
|
134
|
|
135
|
def get_login_info(request, credentials):
|
136
|
"""Returns phantomjs login prerequis
|
137
|
"""
|
138
|
from mandayejs.applications import get_app_settings
|
139
|
app_settings = get_app_settings()
|
140
|
auth_checker = os.path.join(
|
141
|
settings.STATIC_ROOT, app_settings.SITE_AUTH_CHECKER)
|
142
|
return {
|
143
|
'address': request.build_absolute_uri(app_settings.SITE_LOGIN_PATH),
|
144
|
'cookies': [],
|
145
|
'locators': [credentials.to_login_info()],
|
146
|
'auth_checker': auth_checker,
|
147
|
'form_submit_element': app_settings.SITE_FORM_SUBMIT_ELEMENT
|
148
|
}
|
149
|
|
150
|
|
151
|
def get_idp():
|
152
|
"""Return idp
|
153
|
"""
|
154
|
return settings.MELLON_IDENTITY_PROVIDERS[0]['METADATA_URL']
|