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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
|
# -*- coding: utf-8 -*-
#
# Copyright © 2008 Ricky Zhou
# Copyright © 2008-2014 Red Hat, Inc.
#
# This copyrighted material is made available to anyone wishing to use, modify,
# copy, or redistribute it subject to the terms and conditions of the GNU
# General Public License v.2. This program is distributed in the hope that it
# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU General Public License for more details. You should have
# received a copy of the GNU General Public License along with this program;
# if not, write to the Free Software Foundation, Inc., 51 Franklin Street,
# Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat trademarks that are
# incorporated in the source code or documentation are not subject to the GNU
# General Public License and may only be used or replicated with the express
# permission of Red Hat, Inc.
#
# Author(s): Ricky Zhou <ricky@fedoraproject.org>
# Mike McGrath <mmcgrath@redhat.com>
# Toshio Kuratomi <toshio@redhat.com>
#
from bunch import Bunch
from turbogears import expose, config, identity, redirect
from turbogears.database import session
from cherrypy import request
import turbogears
import cherrypy
import time
from fedora.tg import controllers as f_ctrlers
from fedora.tg.utils import request_format
from fas import release
from fas.user import User
from fas.group import Group
from fas.configs import Config
from fas.fpca import FPCA
from fas.json_request import JsonRequest
from fas.help import Help
from fas.model import Session, People
from fas.model import SessionTable
from fas.auth import undeprecated_cla_done
from fas.util import available_languages
from fas import plugin
import os
import datetime
import socket
try:
import cPickle as pickle
except ImportError:
import pickle
class SQLAlchemyStorage:
def __init__(self):
pass
def load(self, session_id):
s = Session.query.get(session_id)
if not s:
return None
expiration_time = s.expiration_time
pickled_data = s.data
data = pickle.loads(pickled_data.encode('utf-8'))
return (data, expiration_time)
# This is an iffy one. CherryPy's built in session
# storage classes use delete(self, id=None), but it
# isn't called from anywhere in cherrypy. I think we
# can do this as long as we're careful about how we call it.
def delete(self, session_id=None):
if session_id is None:
session_id = cherrypy.session.id
s = Session.query.get(session_id)
session.delete(s)
session.flush()
def save(self, session_id, data, expiration_time):
pickled_data = pickle.dumps(data)
s = Session.query.get(session_id)
if not s:
s = Session()
s.id = session_id
s.data = pickled_data
s.expiration_time = expiration_time
session.flush()
def acquire_lock(self):
pass
def release_lock(self):
pass
def clean_up(self, sess):
# This is to make sure that only one server cleans up sessions
if socket.gethostname() != 'fas01.phx2.fedoraproject.org':
return
result = SessionTable.delete(
SessionTable.c.expiration_time.__lt__(datetime.datetime.now())
).execute()
config.update({'session_filter.storage_class': SQLAlchemyStorage})
def get_locale(locale=None):
if locale:
return locale
try:
return turbogears.identity.current.user.locale
except AttributeError:
pass
try:
return cherrypy.request.simple_cookie['fas_locale'].value
except KeyError:
pass
default_language = config.get('default_language',
turbogears.i18n.utils._get_locale())
return default_language
config.update({'i18n.get_locale': get_locale})
def add_custom_stdvars(variables):
return variables.update({'gettext': _, "lang": get_locale(),
'available_languages': available_languages(),
'fas_version': release.VERSION,
'webmaster_email': config.get('webmaster_email')})
turbogears.view.variable_providers.append(add_custom_stdvars)
# from fas import json
# import logging
# log = logging.getLogger("fas.controllers")
#TODO: Appropriate flash icons for errors, etc.
# mmcgrath wonders if it will be handy to expose an encrypted mailer with fas
# over json for our apps
class Root(plugin.RootController):
user = User()
group = Group()
fpca = FPCA()
json = JsonRequest()
config = Config()
help = Help()
def __init__(self):
# TODO: Find a better place for this.
os.environ['GNUPGHOME'] = config.get('gpghome')
plugin.RootController.__init__(self)
def getpluginident(self):
return 'fas'
@expose(template="fas.templates.welcome", allow_json=True)
def index(self):
if turbogears.identity.not_anonymous():
if request_format() == 'json':
# redirects don't work with JSON calls. This is a bit of a
# hack until we can figure out something better.
return dict()
turbogears.redirect('/home')
return dict(now=time.ctime())
@identity.require(identity.not_anonymous())
@expose(template="fas.templates.home", allow_json=True)
def home(self):
user_name = turbogears.identity.current.user_name
person = People.by_username(user_name)
(cla_done, undeprecated_cla) = undeprecated_cla_done(person)
person = person.filter_private()
return dict(person=person, memberships=person['memberships'], cla=undeprecated_cla)
@expose(template="fas.templates.about")
def about(self):
return dict()
@expose(template="fas.templates.login", allow_json=True)
def login(self, forward_url=None, *args, **kwargs):
'''Page to become authenticated to the Account System.
This shows a small login box to type in your username and password
from the Fedora Account System.
:kwarg forward_url: The url to send to once authentication succeeds
'''
actual_login_dict = f_ctrlers.login(forward_url=forward_url, *args, **kwargs)
try:
login_dict = Bunch()
login_dict['user'] = Bunch()
for field in People.allow_fields['complete']:
login_dict['user'][field] = None
for field in People.allow_fields['self']:
login_dict['user'][field] = getattr(actual_login_dict['user'], field)
# Strip out things that the user shouldn't see about their own
# login
login_dict['user']['internal_comments'] = None
login_dict['user']['emailtoken'] = None
login_dict['user']['security_answer'] = None
login_dict['user']['alias_enabled'] = None
login_dict['user']['passwordtoken'] = None
# Add things that are needed by some other apps
login_dict['user'].approved_memberships = list(
actual_login_dict['user'].approved_memberships)
login_dict['user'].memberships = list(actual_login_dict['user'].memberships)
login_dict['user'].unapproved_memberships = list(
actual_login_dict['user'].unapproved_memberships)
login_dict['user'].group_roles = list(actual_login_dict['user'].group_roles)
login_dict['user'].roles = list(actual_login_dict['user'].roles)
login_dict['user'].groups = [g.name for g in actual_login_dict['user'].approved_memberships]
return login_dict
except KeyError, e:
# No problem, this usually means that we failed to login and
# therefore we don't have a user field.
login_dict = actual_login_dict
if not identity.current.anonymous and identity.was_login_attempted() \
and not identity.get_identity_errors():
# Success that needs to be passed back via json
return login_dict
if identity.was_login_attempted() and request.fas_provided_username:
if request.fas_identity_failure_reason == 'status_inactive':
turbogears.flash(_('Your old password has expired. Please'
' reset your password below.'))
if request_format() != 'json':
redirect('/user/resetpass')
if request.fas_identity_failure_reason == 'status_account_disabled':
turbogears.flash(_('Your account is currently disabled. For'
' more information, please contact %(admin_email)s' %
{'admin_email': config.get('accounts_email')}))
if request_format() != 'json':
redirect('/login')
return login_dict
@expose(allow_json=True)
def logout(self):
return f_ctrlers.logout()
@expose()
def language(self, locale):
if locale not in available_languages():
turbogears.flash(_('The language \'%s\' is not available.') % locale)
redirect(request.headers.get("Referer", "/"))
return dict()
#turbogears.i18n.set_session_locale(locale)
cherrypy.response.simple_cookie['fas_locale'] = locale
redirect(request.headers.get("Referer", "/"))
return dict()
|