Compare commits

..

32 Commits

Author SHA1 Message Date
mdipierro
7e96ecafd7 R-2.15.3 2017-08-07 07:41:11 -05:00
mdipierro
86ea728f4f R-2.15.3 2017-08-07 07:32:58 -05:00
mdipierro
a29947f298 websocket_messaging in Python 3 #1696, thanks hirolau 2017-08-07 07:27:28 -05:00
mdipierro
dc29f33365 addressed __ssl.pyd issue #1716, thanks Jhleite 2017-08-07 07:18:42 -05:00
mdipierro
834460f0cc Merge pull request #1723 from leonelcamara/patch-6
Small unicode compatibility py3 fixes
2017-08-07 07:09:30 -05:00
mdipierro
5353e17c66 Merge pull request #1720 from leonelcamara/patch-4
Fix BEAUTIFY trying to display uploaded file contents
2017-08-07 07:08:58 -05:00
mdipierro
d2022ca500 Merge pull request #1719 from leonelcamara/patch-3
Fix response.download with nonasccii filenames
2017-08-07 07:08:33 -05:00
mdipierro
c560f607af Merge pull request #1710 from willimoa/issue/1704
Fix Issue 1709
2017-08-07 07:08:10 -05:00
Leonel Câmara
3eea1f68f4 Make sure you return bytes when you str(body) 2017-08-07 00:20:29 +01:00
Leonel Câmara
10b6b93cb2 minor py3 compatibility change 2017-08-07 00:17:43 +01:00
Leonel Câmara
90e20a4f39 Fix BEAUTIFY trying to display uploaded file contents
Fixes #1717
2017-08-06 17:01:27 +01:00
Leonel Câmara
a744835f21 Fix response.download with nonasccii filenames
Fixes #1718
2017-08-05 13:27:44 +01:00
mdipierro
ebc614bf91 fixed has_ssl, thanks leonel 2017-08-04 10:21:43 -05:00
mdipierro
b7270df9c3 fixed issue topic/web2py/UnN6AyOh2Lg thanks Paul 2017-08-04 09:58:05 -05:00
Andrew Willimott
4a47bb8e83 d3_graph_model fixed, no longer hardcoded for a “db” database 2017-08-02 21:47:26 +12:00
mdipierro
213c4ee7d1 fixed use of whitespaces 2017-08-01 10:26:33 -05:00
mdipierro
7088b74d42 Merge pull request #1705 from josedesoto/enhancement/1557
Enhancement/1557
2017-08-01 09:46:55 -05:00
mdipierro
752b5a8cdb Merge pull request #1704 from josedesoto/enhancement/response_json_sort_paramenter
Allow response.json to be sortable or not
2017-08-01 09:46:11 -05:00
mdipierro
56e6d682d6 Merge pull request #1702 from leonelcamara/i1699
Fix autocomplete called after user_signature chk
2017-08-01 09:46:00 -05:00
mdipierro
c1881fa205 Merge pull request #1701 from jkotyz/issue/1700
Fixes 1700 - utc and local time inconsistency in AuthJWT
2017-08-01 09:45:46 -05:00
mdipierro
cc2cae5de4 Merge pull request #1692 from timnyborg/master
Fix #1691: Passing tables to SQLFORM.factory() fails
2017-08-01 09:45:21 -05:00
mdipierro
cedd74c1ad Merge pull request #1690 from josedesoto/issue/1689
del_membership prevent update self user if user_id has a value
2017-08-01 09:44:41 -05:00
Jose de Soto
d5167f2ed6 change_password_url parameter for alternate login methods 2017-07-31 19:00:24 +02:00
Jose de Soto
1014d3e86e new parameter to auto create or not users with alternate login methods 2017-07-31 18:33:15 +02:00
Jose de Soto
f3bba29287 Allow response.json to be sortable or not 2017-07-31 17:46:46 +02:00
mdipierro
cd2ec98a26 grid.dbset 2017-07-29 01:31:25 -05:00
mdipierro
0e613f2d7f allow DAL(..., adapter_args=dict(migrator=InDBMigrator)) 2017-07-28 16:05:35 -05:00
Leonel Câmara
561828fa60 Fix autocomplete called after user_signature chk
Fixes #1699 which was caused by the autocomplete widget only being called after a user_signature check and the signature not being there.  

The default values for user_signature and hash_vars make this work with the grid when it has the user_signature=True option without any problem when that isn't the case. Users can disable it if they want or change it according to what they're verifying in their controllers.   
  
The autocomplete widget will still fail with the default kwargs in a situation where it is called in a controller function that first verifies the URL signature with hash_vars=True.  
  
For users having that problem, there's no way around this without inserting a security flaw, so they must sadly change their application code to give the autocomplete widget the needed user_signature and hash_vars argument.
2017-07-28 02:20:54 +01:00
Jan Kotyz
19efbfecfa Fixes 1700 2017-07-27 11:27:41 +02:00
Tim Nyborg
d43604c3ff List comprehension rather than loop
I've now learned this is quicker
2017-07-24 10:15:12 +01:00
Tim Nyborg
ec21f72ce3 Fixes #1691
Clone fields, but leave tables untouched in factory()
2017-07-21 10:20:08 +01:00
Jose de Soto
aa0313c59b del_membership prevent update self user if user_id has a value 2017-07-21 10:41:58 +02:00
22 changed files with 183 additions and 137 deletions

View File

@@ -1,4 +1,4 @@
## 2.15.0b1
## 2.15.1-3
- dropped support for python 2.6
- dropped web shell
- experimental python 3 support

View File

@@ -32,7 +32,7 @@ update:
echo "remember that pymysql was tweaked"
src:
### Use semantic versioning
echo 'Version 2.15.2-stable+timestamp.'`date +%Y.%m.%d.%H.%M.%S` > VERSION
echo 'Version 2.15.3-stable+timestamp.'`date +%Y.%m.%d.%H.%M.%S` > VERSION
### rm -f all junk files
make clean
### clean up baisc apps
@@ -97,6 +97,8 @@ win:
cp -r applications/welcome ../web2py_win/web2py/applications
cp -r applications/examples ../web2py_win/web2py/applications
cp applications/__init__.py ../web2py_win/web2py/applications
# per https://github.com/web2py/web2py/issues/1716
mv ../web2py_win/web2py/_ssl.pyd ../web2py_win/web2py/_ssl.pyd.legacy
cd ../web2py_win; zip -r web2py_win.zip web2py
mv ../web2py_win/web2py_win.zip .
run:

View File

@@ -1 +1 @@
Version 2.15.2-stable+timestamp.2017.07.19.01.21.31
Version 2.15.3-stable+timestamp.2017.08.07.07.32.04

View File

@@ -657,37 +657,36 @@ def d3_graph_model():
Create a list of table dicts, called "nodes"
"""
data = {}
nodes = []
links = []
subgraphs = dict()
for database in databases:
db = eval_in_global_env(database)
for tablename in db.tables:
fields = []
for field in db[tablename]:
f_type = field.type
if not isinstance(f_type,str):
disp = ' '
elif f_type == 'string':
disp = field.length
elif f_type == 'id':
disp = "PK"
elif f_type.startswith('reference') or \
f_type.startswith('list:reference'):
disp = "FK"
else:
disp = ' '
fields.append(dict(name= field.name, type=field.type, disp = disp))
for tablename in db.tables:
fields = []
for field in db[tablename]:
f_type = field.type
if not isinstance(f_type,str):
disp = ' '
elif f_type == 'string':
disp = field.length
elif f_type == 'id':
disp = "PK"
elif f_type.startswith('reference') or \
f_type.startswith('list:reference'):
disp = "FK"
else:
disp = ' '
fields.append(dict(name= field.name, type=field.type, disp = disp))
if isinstance(f_type,str) and (
f_type.startswith('reference') or
f_type.startswith('list:reference')):
referenced_table = f_type.split()[1].split('.')[0]
if isinstance(f_type,str) and (
f_type.startswith('reference') or
f_type.startswith('list:reference')):
referenced_table = f_type.split()[1].split('.')[0]
links.append(dict(source=tablename, target = referenced_table))
links.append(dict(source=tablename, target = referenced_table))
nodes.append(dict(name=tablename, type="table", fields = fields))
nodes.append(dict(name=tablename, type="table", fields = fields))
# d3 v4 allows individual modules to be specified. The complete d3 library is included below.
response.files.append(URL('admin','static','js/d3.min.js'))

View File

@@ -657,37 +657,36 @@ def d3_graph_model():
Create a list of table dicts, called "nodes"
"""
data = {}
nodes = []
links = []
subgraphs = dict()
for database in databases:
db = eval_in_global_env(database)
for tablename in db.tables:
fields = []
for field in db[tablename]:
f_type = field.type
if not isinstance(f_type,str):
disp = ' '
elif f_type == 'string':
disp = field.length
elif f_type == 'id':
disp = "PK"
elif f_type.startswith('reference') or \
f_type.startswith('list:reference'):
disp = "FK"
else:
disp = ' '
fields.append(dict(name= field.name, type=field.type, disp = disp))
for tablename in db.tables:
fields = []
for field in db[tablename]:
f_type = field.type
if not isinstance(f_type,str):
disp = ' '
elif f_type == 'string':
disp = field.length
elif f_type == 'id':
disp = "PK"
elif f_type.startswith('reference') or \
f_type.startswith('list:reference'):
disp = "FK"
else:
disp = ' '
fields.append(dict(name= field.name, type=field.type, disp = disp))
if isinstance(f_type,str) and (
f_type.startswith('reference') or
f_type.startswith('list:reference')):
referenced_table = f_type.split()[1].split('.')[0]
if isinstance(f_type,str) and (
f_type.startswith('reference') or
f_type.startswith('list:reference')):
referenced_table = f_type.split()[1].split('.')[0]
links.append(dict(source=tablename, target = referenced_table))
links.append(dict(source=tablename, target = referenced_table))
nodes.append(dict(name=tablename, type="table", fields = fields))
nodes.append(dict(name=tablename, type="table", fields = fields))
# d3 v4 allows individual modules to be specified. The complete d3 library is included below.
response.files.append(URL('admin','static','js/d3.min.js'))

View File

@@ -657,37 +657,36 @@ def d3_graph_model():
Create a list of table dicts, called "nodes"
"""
data = {}
nodes = []
links = []
subgraphs = dict()
for database in databases:
db = eval_in_global_env(database)
for tablename in db.tables:
fields = []
for field in db[tablename]:
f_type = field.type
if not isinstance(f_type,str):
disp = ' '
elif f_type == 'string':
disp = field.length
elif f_type == 'id':
disp = "PK"
elif f_type.startswith('reference') or \
f_type.startswith('list:reference'):
disp = "FK"
else:
disp = ' '
fields.append(dict(name= field.name, type=field.type, disp = disp))
for tablename in db.tables:
fields = []
for field in db[tablename]:
f_type = field.type
if not isinstance(f_type,str):
disp = ' '
elif f_type == 'string':
disp = field.length
elif f_type == 'id':
disp = "PK"
elif f_type.startswith('reference') or \
f_type.startswith('list:reference'):
disp = "FK"
else:
disp = ' '
fields.append(dict(name= field.name, type=field.type, disp = disp))
if isinstance(f_type,str) and (
f_type.startswith('reference') or
f_type.startswith('list:reference')):
referenced_table = f_type.split()[1].split('.')[0]
if isinstance(f_type,str) and (
f_type.startswith('reference') or
f_type.startswith('list:reference')):
referenced_table = f_type.split()[1].split('.')[0]
links.append(dict(source=tablename, target = referenced_table))
links.append(dict(source=tablename, target = referenced_table))
nodes.append(dict(name=tablename, type="table", fields = fields))
nodes.append(dict(name=tablename, type="table", fields = fields))
# d3 v4 allows individual modules to be specified. The complete d3 library is included below.
response.files.append(URL('admin','static','js/d3.min.js'))

View File

@@ -20,10 +20,10 @@ DEFAULT = lambda: None
class AuthAPI(object):
"""
AuthAPI is a barebones Auth implementation which does not have a concept of
HTML forms or redirects, emailing or even an URL, you are responsible for
HTML forms or redirects, emailing or even an URL, you are responsible for
all that if you use it.
The main Auth functions such as login, logout, register, profile are designed
in a Dict In -> Dict Out logic so, for instance, if you set
in a Dict In -> Dict Out logic so, for instance, if you set
registration_requires_verification you are responsible for sending the key to
the user and even rolling back the transaction if you can't do it.
@@ -245,13 +245,13 @@ class AuthAPI(object):
migrate = db._migrate
if fake_migrate is None:
fake_migrate = db._fake_migrate
settings = self.settings
if username is None:
username = settings.use_username
else:
settings.use_username = username
if not self.signature:
self.define_signature()
if signature is True:
@@ -557,7 +557,7 @@ class AuthAPI(object):
self.log_event(self.messages['del_membership_log'],
dict(user_id=user_id, group_id=group_id))
ret = self.db(membership.user_id == user_id)(membership.group_id == group_id).delete()
if group_id in self.user_groups:
if group_id in self.user_groups and user_id == self.user_id:
del self.user_groups[group_id]
return ret
@@ -1012,7 +1012,7 @@ class AuthAPI(object):
):
"""
Verify a given registration_key actually exists in the user table.
Resets the key to empty string '' or 'pending' if
Resets the key to empty string '' or 'pending' if
setttings.registration_requires_approval is true.
Keyword Args:

View File

@@ -49,7 +49,8 @@ class CasAuth(object):
email=lambda v: v.get('email', None),
user_id=lambda v: v['user']),
casversion=1,
casusername='cas:user'
casusername='cas:user',
change_password_url=None
):
self.urlbase = urlbase
self.cas_login_url = "%s/%s" % (self.urlbase, actions[0])
@@ -64,6 +65,9 @@ class CasAuth(object):
#vars=current.request.vars,
scheme=True)
# URL to let users change their password in the IDP system
self.cas_change_password_url = change_password_url
def login_url(self, next="/"):
current.session.token = self._CAS_login()
return next
@@ -74,6 +78,10 @@ class CasAuth(object):
self._CAS_logout()
return next
def change_password_url(self, next="/"):
self._CAS_change_password()
return next
def get_user(self):
user = current.session.token
if user:
@@ -135,3 +143,6 @@ class CasAuth(object):
redirects to the CAS logout page
"""
redirect("%s?service=%s" % (self.cas_logout_url, self.cas_my_url))
def _CAS_change_password(self):
redirect(self.cas_change_password_url)

View File

@@ -145,10 +145,16 @@ class Saml2Auth(object):
username=lambda v:v['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn'][0],
email=lambda v:v['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn'][0],
user_id=lambda v:v['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn'][0],
)):
), logout_url=None, change_password_url=None):
self.config_file = config_file
self.maps = maps
# URL for redirecting users to when they sign out
self.saml_logout_url = logout_url
# URL to let users change their password in the IDP system
self.saml_change_password_url = change_password_url
def login_url(self, next="/"):
d = saml2_handler(current.session, current.request)
if 'url' in d:
@@ -170,6 +176,12 @@ class Saml2Auth(object):
def logout_url(self, next="/"):
current.session.saml2_info = None
current.session.auth = None
self._SAML_logout()
return next
def change_password_url(self, next="/"):
self._SAML_change_password()
return next
def get_user(self):
@@ -180,3 +192,13 @@ class Saml2Auth(object):
d[key] = self.maps[key](user)
return d
return None
def _SAML_logout(self):
"""
exposed SAML.logout()
redirects to the SAML logout page
"""
redirect(self.saml_logout_url)
def _SAML_change_password(self):
redirect(self.saml_change_password_url)

View File

@@ -94,32 +94,10 @@ import optparse
import time
import sys
import gluon.utils
if (sys.version_info[0] == 2):
from urllib import urlencode, urlopen
def to_bytes(obj, charset='utf-8', errors='strict'):
if obj is None:
return None
if isinstance(obj, (bytes, bytearray, buffer)):
return bytes(obj)
if isinstance(obj, unicode):
return obj.encode(charset, errors)
raise TypeError('Expected bytes')
else:
from urllib.request import urlopen
from urllib.parse import urlencode
def to_bytes(obj, charset='utf-8', errors='strict'):
if obj is None:
return None
if isinstance(obj, (bytes, bytearray, memoryview)):
return bytes(obj)
if isinstance(obj, str):
return obj.encode(charset, errors)
raise TypeError('Expected bytes')
from gluon._compat import to_native, to_bytes, urlencode, urlopen
listeners, names, tokens = {}, {}, {}
def websocket_send(url, message, hmac_key=None, group='default'):
sig = hmac_key and hmac.new(to_bytes(hmac_key), to_bytes(message)).hexdigest() or ''
params = urlencode(
@@ -138,8 +116,8 @@ class PostHandler(tornado.web.RequestHandler):
if hmac_key and not 'signature' in self.request.arguments:
self.send_error(401)
if 'message' in self.request.arguments:
message = self.request.arguments['message'][0]
group = self.request.arguments.get('group', ['default'])[0]
message = self.request.arguments['message'][0].decode(encoding='UTF-8')
group = self.request.arguments.get('group', ['default'])[0].decode(encoding='UTF-8')
print('%s:MESSAGE to %s:%s' % (time.time(), group, message))
if hmac_key:
signature = self.request.arguments['signature'][0]

View File

@@ -14,6 +14,7 @@ from pydal import DAL as DAL
from pydal import Field
from pydal.objects import Row, Rows, Table, Query, Set, Expression
from pydal import SQLCustomType, geoPoint, geoLine, geoPolygon
from pydal.migrator import Migrator, InDBMigrator
from gluon.serializers import custom_json, xml
from gluon.utils import web2py_uuid
from gluon import sqlhtml

View File

@@ -14,7 +14,7 @@ Contains the classes for the global used variables:
"""
from gluon._compat import pickle, StringIO, copyreg, Cookie, urlparse, PY2, iteritems, to_unicode, to_native, \
unicodeT, long, hashlib_md5
unicodeT, long, hashlib_md5, urllib_quote
from gluon.storage import Storage, List
from gluon.streamer import streamer, stream_file_or_304_or_206, DEFAULT_CHUNK_SIZE
from gluon.contenttype import contenttype
@@ -641,6 +641,11 @@ class Response(Storage):
if download_filename is None:
download_filename = filename
if attachment:
# Browsers still don't have a simple uniform way to have non ascii
# characters in the filename so for now we are percent encoding it
if isinstance(download_filename, unicodeT):
download_filename = download_filename.encode('utf-8')
download_filename = urllib_quote(download_filename)
headers['Content-Disposition'] = \
'attachment; filename="%s"' % download_filename.replace('"', '\"')
return self.stream(stream, chunk_size=chunk_size, request=request)

View File

@@ -2431,7 +2431,7 @@ class BEAUTIFY(DIV):
if level == 0:
return
for c in self.components:
if hasattr(c, 'value') and not callable(c.value):
if hasattr(c, 'value') and not callable(c.value) and not isinstance(c, cgi.FieldStorage):
if c.value:
components.append(c.value)
if hasattr(c, 'xml') and callable(c.xml):

View File

@@ -11,7 +11,7 @@ HTTP statuses helpers
"""
import re
from gluon._compat import iteritems, unicodeT
from gluon._compat import iteritems, unicodeT, to_bytes
__all__ = ['HTTP', 'redirect']
@@ -111,6 +111,8 @@ class HTTP(Exception):
if not body:
body = status
if isinstance(body, (str, bytes, bytearray)):
if isinstance(body, unicodeT):
body = to_bytes(body) # This must be done before len
headers['Content-Length'] = len(body)
rheaders = []
for k, v in iteritems(headers):
@@ -123,12 +125,15 @@ class HTTP(Exception):
return ['']
elif isinstance(body, (str, bytes, bytearray)):
if isinstance(body, unicodeT):
body = body.encode('utf-8')
body = to_bytes(body)
return [body]
elif hasattr(body, '__iter__'):
return body
else:
return [str(body)]
body = str(body)
if isinstance(body, unicodeT):
body = to_bytes(body)
return [body]
@property
def message(self):

View File

@@ -745,7 +745,7 @@ class HttpServer(object):
sock_list = [ip, port]
if not ssl_certificate or not ssl_private_key:
logger.info('SSL is off')
elif not rocket.ssl:
elif not rocket.has_ssl:
logger.warning('Python "ssl" module unavailable. SSL is OFF')
elif not exists(ssl_certificate):
logger.warning('unable to open SSL certificate. SSL is OFF')

View File

@@ -225,7 +225,7 @@ def parsecronline(line):
params = line.strip().split(None, 6)
if len(params) < 7:
return None
daysofweek = {'sun': 0, 'mon': 1, 'tue': 2, 'wed': 3,
daysofweek = {'sun': 0, 'mon': 1, 'tue': 2, 'wed': 3,
'thu': 4, 'fri': 5, 'sat': 6}
for (s, id) in zip(params[:5], ['min', 'hr', 'dom', 'mon', 'dow']):
if not s in [None, '*']:

View File

@@ -10,7 +10,7 @@ Restricted environment to execute application's code
"""
import sys
from gluon._compat import pickle, ClassType
from gluon._compat import pickle, ClassType, unicodeT, to_bytes
import traceback
import types
import os
@@ -192,10 +192,10 @@ class RestrictedError(Exception):
# safely show an useful message to the user
try:
output = self.output
if isinstance(output, unicode):
output = output.encode("utf8")
elif not isinstance(output, str):
if not isinstance(output, str, bytes, bytearray):
output = str(output)
if isinstance(output, unicodeT):
output = to_bytes(output)
except:
output = ""
return output

View File

@@ -119,8 +119,8 @@ def xml(value, encoding='UTF-8', key='document', quote=True):
return ('<?xml version="1.0" encoding="%s"?>' % encoding) + str(xml_rec(value, key, quote))
def json(value, default=custom_json, indent=None):
value = json_parser.dumps(value, default=default, sort_keys=True, indent=indent)
def json(value, default=custom_json, indent=None, sort_keys=False):
value = json_parser.dumps(value, default=default, sort_keys=sort_keys, indent=indent)
# replace JavaScript incompatible spacing
# http://timelessrepo.com/json-isnt-a-javascript-subset
# PY3 FIXME

View File

@@ -658,7 +658,8 @@ class AutocompleteWidget(object):
orderby=None, limitby=(0, 10), distinct=False,
keyword='_autocomplete_%(tablename)s_%(fieldname)s',
min_length=2, help_fields=None, help_string=None,
at_beginning=True, default_var='ac'):
at_beginning=True, default_var='ac', user_signature=True,
hash_vars=False):
self.help_fields = help_fields or []
self.help_string = help_string
@@ -683,7 +684,8 @@ class AutocompleteWidget(object):
if hasattr(request, 'application'):
urlvars = request.vars
urlvars[default_var] = 1
self.url = URL(args=request.args, vars=urlvars)
self.url = URL(args=request.args, vars=urlvars,
user_signature=user_signature, hash_vars=hash_vars)
self.run_callback = True
else:
self.url = request
@@ -1919,8 +1921,10 @@ class SQLFORM(FORM):
if 'table_name' in attributes:
del attributes['table_name']
return SQLFORM(DAL(None).define_table(table_name, *[field.clone() for field in fields]),
**attributes)
# Clone fields, while passing tables straight through
fields_with_clones = [f.clone() if isinstance(f, Field) else f for f in fields]
return SQLFORM(DAL(None).define_table(table_name, *fields_with_clones), **attributes)
@staticmethod
def build_query(fields, keywords):
@@ -1935,11 +1939,13 @@ class SQLFORM(FORM):
if settings.global_settings.web2py_runtime_gae:
return reduce(lambda a,b: a|b, [field.contains(key) for field in sfields])
else:
if not (sfields and key and key.split()):
return fields[0].table
return reduce(lambda a,b:a&b,[
reduce(lambda a,b: a|b, [
field.contains(k) for field in sfields]
) for k in key.split()])
# from https://groups.google.com/forum/#!topic/web2py/hKe6lI25Bv4
# needs testing...
#words = key.split(' ') if key else []
@@ -2159,6 +2165,7 @@ class SQLFORM(FORM):
represent_none=None,
showblobs=False):
dbset = None
formstyle = formstyle or current.response.formstyle
if isinstance(query, Set):
query = query.query
@@ -3037,6 +3044,7 @@ class SQLFORM(FORM):
res.view_form = view_form
res.search_form = search_form
res.rows = rows
res.dbset = dbset
return res
@staticmethod
@@ -3155,8 +3163,8 @@ class SQLFORM(FORM):
# if isinstance(linked_tables, dict):
# linked_tables = linked_tables.get(table._tablename, [])
if linked_tables is None or referee in linked_tables:
field.represent = (lambda id, r=None, referee=referee, rep=field.represent:
A(callable(rep) and rep(id) or id,
field.represent = (lambda id, r=None, referee=referee, rep=field.represent:
A(callable(rep) and rep(id) or id,
cid=request.cid, _href=url(args=['view', referee, id])))
except (KeyError, ValueError, TypeError):
redirect(URL(args=table._tablename))

View File

@@ -1320,7 +1320,7 @@ class AuthJWT(object):
# is the following safe or should we use
# calendar.timegm(datetime.datetime.utcnow().timetuple())
# result seem to be the same (seconds since epoch, in UTC)
now = time.mktime(datetime.datetime.now().timetuple())
now = time.mktime(datetime.datetime.utcnow().timetuple())
expires = now + self.expiration
payload = dict(
hmac_key=session_auth['hmac_key'],
@@ -1332,7 +1332,7 @@ class AuthJWT(object):
return payload
def refresh_token(self, orig_payload):
now = time.mktime(datetime.datetime.now().timetuple())
now = time.mktime(datetime.datetime.utcnow().timetuple())
if self.verify_expiration:
orig_exp = orig_payload['exp']
if orig_exp + self.leeway < now:
@@ -1792,6 +1792,7 @@ class Auth(AuthAPI):
servicevalidate='serviceValidate',
proxyvalidate='proxyValidate',
logout='logout'),
cas_create_user=True,
extra_fields={},
actions_disabled=[],
controller=controller,
@@ -2284,6 +2285,7 @@ class Auth(AuthAPI):
If the user doesn't yet exist, then they are created.
"""
table_user = self.table_user()
create_user = self.settings.cas_create_user
user = None
checks = []
# make a guess about who this user is
@@ -2316,6 +2318,11 @@ class Auth(AuthAPI):
update_keys[key] = keys[key]
user.update_record(**update_keys)
elif checks:
if create_user is False:
# Remove current open session a send message
self.logout(next=None, onlogout=None, log=None)
raise HTTP(403, "Forbidden. User need to be created first.")
if 'first_name' not in keys and 'first_name' in table_user.fields:
guess = keys.get('email', 'anonymous').split('@')[0]
keys['first_name'] = keys.get('username', guess)
@@ -2863,7 +2870,7 @@ class Auth(AuthAPI):
auth.settings.auth_two_factor_enabled = True
auth.messages.two_factor_comment = "Verify your OTP Client for the code."
auth.settings.two_factor_methods = [lambda user,
auth.settings.two_factor_methods = [lambda user,
auth_two_factor: _set_two_factor(user, auth_two_factor)]
auth.settings.two_factor_onvalidation = [lambda user, otp: verify_otp(user, otp)]
@@ -3656,6 +3663,16 @@ class Auth(AuthAPI):
if not self.is_logged_in():
redirect(self.settings.login_url,
client_side=self.settings.client_side)
# Go to external link to change the password
if self.settings.login_form != self:
cas = self.settings.login_form
# To prevent error if change_password_url function is not defined in alternate login
if hasattr(cas, 'change_password_url'):
next = cas.change_password_url(next)
if next is not None:
redirect(next)
db = self.db
table_user = self.table_user()
s = db(table_user.id == self.user.id)

View File

@@ -140,7 +140,7 @@ class web2pyDialog(object):
else:
import tkinter
from tkinter import messagebox
bg_color = 'white'
root.withdraw()
@@ -463,7 +463,7 @@ class web2pyDialog(object):
import tkMessageBox as messagebox
else:
from tkinter import messagebox
messagebox.showerror('web2py start server', message)
def start(self):