Révision 6b178829
Ajouté par Jérôme Schneider il y a presque 12 ans
README.rst | ||
---|---|---|
48 | 48 |
* Beaker >= 1.6:: http://pypi.python.org/pypi/Beaker |
49 | 49 |
* Mako >= 0.4:: http://pypi.python.org/pypi/Mako |
50 | 50 |
* lxml >= 2.3:: http://pypi.python.org/pypi/lxml |
51 |
* xtraceback >= 0.3:: http://pypi.python.org/pypi/xtraceback |
|
52 |
* sqlalchemy-migrate:: http://pypi.python.org/pypi/sqlalchemy-migrate |
|
53 |
|
|
51 | 54 |
|
52 | 55 |
You can install all those dependencies quickly using pip:: |
53 | 56 |
|
54 |
pip install gevent poster SQLAlchemy Beaker Mako lxml gunicorn
|
|
57 |
pip install poster SQLAlchemy Beaker Mako lxml gunicorn sqlalchemy-migrate xtraceback
|
|
55 | 58 |
|
56 | 59 |
or easy_install:: |
57 | 60 |
|
58 |
easy_install gevent poster SQLAlchemy Beaker Mako lxml gunicorn
|
|
61 |
easy_install poster SQLAlchemy Beaker Mako lxml gunicorn sqlalchemy-migrate xtraceback
|
|
59 | 62 |
|
60 | 63 |
or apt-get (Debian based distributions):: |
61 | 64 |
|
mandaye/__init__.py | ||
---|---|---|
1 |
VERSION=0.2 |
|
1 |
VERSION=0.3 |
mandaye/auth/authform.py | ||
---|---|---|
3 | 3 |
""" |
4 | 4 |
import Cookie |
5 | 5 |
import base64 |
6 |
import copy |
|
6 | 7 |
import re |
7 | 8 |
import traceback |
8 | 9 |
import urllib |
... | ... | |
14 | 15 |
from lxml.html import fromstring |
15 | 16 |
from urlparse import parse_qs |
16 | 17 |
|
17 |
from mandaye import config |
|
18 |
from mandaye import config, VERSION
|
|
18 | 19 |
from mandaye.db import sql_session |
20 |
from mandaye.exceptions import MandayeException |
|
19 | 21 |
from mandaye.models import Site, ExtUser, LocalUser |
20 | 22 |
from mandaye.log import logger |
21 | 23 |
from mandaye.http import HTTPResponse, HTTPHeader, HTTPRequest |
... | ... | |
38 | 40 |
'form_url': '/myform', |
39 | 41 |
'form_attrs': { 'name': 'form40', }, |
40 | 42 |
'username_field': 'user', |
41 |
'password_field': 'pwd' |
|
43 |
'password_field': 'pass', |
|
44 |
'post_fields': ['birthdate', 'card_number'] |
|
42 | 45 |
} |
46 |
form_url, form_attrs, post_fields and username_field are obligatory |
|
43 | 47 |
site_name: str with the site name |
44 | 48 |
""" |
45 | 49 |
if not form_values.has_key('form_headers'): |
46 | 50 |
form_values['form_headers'] = { |
47 | 51 |
'Content-Type': 'application/x-www-form-urlencoded', |
48 |
'User-Agent': 'Mozilla/5.0 Mandaye/0.0'
|
|
52 |
'User-Agent': 'Mozilla/5.0 Mandaye/%s' % VERSION
|
|
49 | 53 |
} |
50 | 54 |
|
51 | 55 |
if not form_values.has_key('form_url') or \ |
52 | 56 |
not form_values.has_key('form_attrs') or \ |
53 |
not form_values.has_key('username_field') or \
|
|
54 |
not form_values.has_key('password_field'):
|
|
57 |
not form_values.has_key('post_fields') or \
|
|
58 |
not form_values.has_key('username_field'):
|
|
55 | 59 |
logger.critical("Bad configuration: AuthForm form_values dict must have \ |
56 |
this keys: form_url, form_attrs, username_field and password_field") |
|
57 |
# TODO: manage Mandaye exceptions |
|
58 |
raise BaseException, 'AuthForm bad configuration' |
|
60 |
this keys: form_url, form_attrs, post_fields and username_field") |
|
61 |
raise MandayeException, 'AuthForm bad configuration' |
|
62 |
if config.encrypt_secret and not form_values.has_key('password_field'): |
|
63 |
logger.critical("Bad configuration: AuthForm form_values dict must have a \ |
|
64 |
a password_field key if you want to encode a password.") |
|
65 |
raise MandayeException, 'AuthForm bad configuration' |
|
59 | 66 |
|
60 | 67 |
self.form_url = form_values['form_url'] |
61 | 68 |
self.form_values = form_values |
69 |
if not self.form_values.has_key('post_fields'): |
|
70 |
self.form_values['post_fields'] = [] |
|
62 | 71 |
self.site_name = site_name |
63 | 72 |
|
64 |
def _encrypt_pwd(self, pwd):
|
|
73 |
def _encrypt_pwd(self, post_values):
|
|
65 | 74 |
""" This method allows you to encrypt a password |
66 | 75 |
To use this feature you muste set encrypt_ext_password to True |
67 | 76 |
in your configuration and set a secret in encrypt_secret |
68 |
pwd: the password you want to encrypt
|
|
69 |
return None if encryption failed
|
|
77 |
post_values: containt the post values
|
|
78 |
return None and modify post_values
|
|
70 | 79 |
""" |
71 |
logger.debug("Encrypt password") |
|
72 |
enc_pwd = pwd |
|
73 | 80 |
if config.encrypt_secret: |
74 |
try: |
|
75 |
cipher = AES.new(config.encrypt_secret, AES.MODE_CFB) |
|
76 |
enc_pwd = cipher.encrypt(pwd) |
|
77 |
enc_pwd = base64.b64encode(enc_pwd) |
|
78 |
except Exception, e: |
|
79 |
if config.debug: |
|
80 |
traceback.print_exc() |
|
81 |
logger.warning('Password encrypting failed %s' % e) |
|
81 |
logger.debug("Encrypt password") |
|
82 |
password = post_values[self.form_values['password_field']] |
|
83 |
if config.encrypt_secret: |
|
84 |
try: |
|
85 |
cipher = AES.new(config.encrypt_secret, AES.MODE_CFB, "0000000000000000") |
|
86 |
password = cipher.encrypt(password) |
|
87 |
password = base64.b64encode(password) |
|
88 |
post_values[self.form_values['password_field']] = password |
|
89 |
except Exception, e: |
|
90 |
if config.debug: |
|
91 |
traceback.print_exc() |
|
92 |
logger.warning('Password encrypting failed %s' % e) |
|
93 |
else: |
|
94 |
logger.warning("You must set a secret to use pwd encryption") |
|
82 | 95 |
else: |
83 |
logger.warning("You must set a secret to use pwd encryption") |
|
84 |
return enc_pwd |
|
96 |
logger.warning("You must set a password_field to encode a password") |
|
85 | 97 |
|
86 |
def _decrypt_pwd(self, enc_pwd):
|
|
98 |
def _decrypt_pwd(self, post_values):
|
|
87 | 99 |
""" This method allows you to dencrypt a password encrypt with |
88 | 100 |
_encrypt_pwd method. To use this feature you muste set |
89 | 101 |
encrypt_ext_password to True in your configuration and |
90 | 102 |
set a secret in encrypt_secret |
91 |
enc_pwd: your encoded password
|
|
92 |
return None if encryption failed
|
|
103 |
post_values: containt the post values
|
|
104 |
return None and modify post_values
|
|
93 | 105 |
""" |
94 |
logger.debug("Decrypt password") |
|
95 |
pwd = enc_pwd |
|
96 | 106 |
if config.encrypt_secret: |
107 |
logger.debug("Decrypt password") |
|
108 |
password = post_values[self.form_values['password_field']] |
|
97 | 109 |
try: |
98 |
cipher = AES.new(config.encrypt_secret, AES.MODE_CFB) |
|
99 |
pwd = base64.b64decode(enc_pwd) |
|
100 |
pwd = cipher.decrypt(pwd) |
|
110 |
cipher = AES.new(config.encrypt_secret, AES.MODE_CFB, "0000000000000000") |
|
111 |
password = base64.b64decode(password) |
|
112 |
password = cipher.decrypt(password) |
|
113 |
post_values[self.form_values['password_field']] = password |
|
101 | 114 |
except Exception, e: |
102 | 115 |
if config.debug: |
103 | 116 |
traceback.print_exc() |
104 | 117 |
logger.warning('Decrypting password failed: %s' % e) |
105 | 118 |
else: |
106 | 119 |
logger.warning("You must set a secret to use pwd decryption") |
107 |
return pwd |
|
108 | 120 |
|
109 |
def replay(self, env, username, password, extra_values={}): |
|
121 |
def _get_password(self, post_values): |
|
122 |
if self.form_values.has_key('password_field'): |
|
123 |
if config.encrypt_ext_password: |
|
124 |
return self._encrypt_pwd( |
|
125 |
post_values[self.form_values['password_field']] |
|
126 |
) |
|
127 |
return post_values[self.form_values['password_field']] |
|
128 |
return None |
|
129 |
|
|
130 |
def replay(self, env, post_values): |
|
110 | 131 |
""" replay the login / password |
111 | 132 |
env: WSGI env with beaker session and the target |
112 |
extra_values: dict with the field name (key) and the field value (value)
|
|
133 |
post_values: dict with the field name (key) and the field value (value)
|
|
113 | 134 |
""" |
114 | 135 |
if not "://" in self.form_url: |
115 | 136 |
self.form_url = env['target'].geturl() + '/' + self.form_url |
... | ... | |
155 | 176 |
params[input.name] = input.value |
156 | 177 |
else: |
157 | 178 |
params[input.name] = '' |
158 |
params[self.form_values['username_field']] = username |
|
159 |
params[self.form_values['password_field']] = password |
|
160 |
for key, value in extra_values.iteritems(): |
|
179 |
for key, value in post_values.iteritems(): |
|
161 | 180 |
params[key] = value |
162 | 181 |
params = urllib.urlencode(params) |
163 | 182 |
request = HTTPRequest(cookies, headers, "POST", params) |
164 | 183 |
return get_response(env, request, action, cj) |
165 | 184 |
|
166 |
def _save_association(self, env, local_login, ext_username, ext_pwd, ext_birthdate=None):
|
|
185 |
def _save_association(self, env, local_login, post_values):
|
|
167 | 186 |
""" save an association in the database |
168 | 187 |
env: wsgi environment |
169 | 188 |
local_login: the Mandaye login |
170 |
ext_username: username of the external site (use for the replay) |
|
171 |
ext_pwd: password of the external site |
|
172 |
ext_birthdate: external birthdate (optional) |
|
189 |
post_values: dict with the post values |
|
173 | 190 |
""" |
191 |
ext_username = post_values[self.form_values['username_field']] |
|
192 |
if config.encrypt_ext_password: |
|
193 |
self._encrypt_pwd(post_values) |
|
174 | 194 |
site = sql_session().query(Site).\ |
175 | 195 |
filter_by(name=self.site_name).first() |
176 | 196 |
if not site: |
... | ... | |
194 | 214 |
logger.info('New association: %s with %s on site %s' % \ |
195 | 215 |
(ext_username, local_login, self.site_name)) |
196 | 216 |
ext_user.login = ext_username |
197 |
if config.encrypt_ext_password: |
|
198 |
ext_pwd = self._encrypt_pwd(ext_pwd) |
|
199 |
ext_user.password = ext_pwd |
|
217 |
ext_user.post_values = post_values |
|
200 | 218 |
ext_user.local_user = local_user |
201 | 219 |
ext_user.last_connection = datetime.now() |
202 | 220 |
ext_user.site = site |
203 |
# TODO: generalize this |
|
204 |
if ext_birthdate: |
|
205 |
ext_user.birthdate = ext_birthdate |
|
206 | 221 |
sql_session().commit() |
207 | 222 |
env['beaker.session']['login'] = local_login |
208 | 223 |
env['beaker.session'][self.site_name] = ext_user.id |
... | ... | |
221 | 236 |
qs = parse_qs(env['QUERY_STRING']) |
222 | 237 |
for key, value in qs.iteritems(): |
223 | 238 |
qs[key] = value[0] |
224 |
if not post.has_key('username') or not post.has_key('password'): |
|
225 |
logger.info('Association auth failed: form not correctly filled') |
|
226 |
qs['type'] = 'badlogin' |
|
227 |
return _302(values.get('associate_url') + "?%s" % urllib.urlencode(qs)) |
|
228 |
username = post['username'][0] |
|
229 |
# TODO: generized this part (use a generic key / value table) |
|
230 |
extra_values = {} |
|
231 |
if post.has_key('birthdate') and self.form_values.has_key('birthdate_field'): |
|
232 |
birthdate_field = self.form_values['birthdate_field'] |
|
233 |
birthdate = post['birthdate'][0] |
|
234 |
extra_values = {birthdate_field: birthdate} |
|
235 |
else: |
|
236 |
birthdate = None |
|
237 |
response = self.replay(env, username, |
|
238 |
post['password'][0], extra_values) |
|
239 |
post_fields = self.form_values['post_fields'] |
|
240 |
post_values = {} |
|
241 |
for field in post_fields: |
|
242 |
if not post.has_key(field): |
|
243 |
logger.info('Association auth failed: form not correctly filled') |
|
244 |
qs['type'] = 'badlogin' |
|
245 |
return _302(values.get('associate_url') + "?%s" % urllib.urlencode(qs)) |
|
246 |
post_values[field] = post[field][0] |
|
247 |
response = self.replay(env, post_values) |
|
239 | 248 |
if eval(condition): |
240 | 249 |
logger.debug("Replay works: save the association") |
241 |
self._save_association(env, login, username, post['password'][0], birthdate)
|
|
250 |
self._save_association(env, login, post_values)
|
|
242 | 251 |
if qs.has_key('next_url'): |
243 | 252 |
return _302(qs['next_url'], response.cookies) |
244 | 253 |
return response |
... | ... | |
250 | 259 |
def _login_ext_user(self, ext_user, env, condition, values): |
251 | 260 |
""" Log in an external user |
252 | 261 |
""" |
253 |
if not ext_user.login or not ext_user.password:
|
|
262 |
if not ext_user.login: |
|
254 | 263 |
return _500(env['PATH_INFO'], |
255 | 264 |
'Invalid values for AuthFormDispatcher.login') |
256 |
# TODO: generized this condition |
|
257 |
extra_values = {} |
|
258 |
if ext_user.birthdate and self.form_values.has_key('birthdate_field'): |
|
259 |
extra_values = { self.form_values['birthdate_field']: ext_user.birthdate } |
|
265 |
post_values = copy.deepcopy(ext_user.post_values) |
|
260 | 266 |
if config.encrypt_ext_password: |
261 |
pwd = self._decrypt_pwd(ext_user.password) |
|
262 |
else: |
|
263 |
pwd = ext_user.password |
|
264 |
response = self.replay(env, ext_user.login, |
|
265 |
pwd, extra_values) |
|
267 |
self._decrypt_pwd(post_values) |
|
268 |
response = self.replay(env, post_values) |
|
266 | 269 |
if condition and eval(condition): |
267 | 270 |
ext_user.last_connection = datetime.now() |
268 | 271 |
sql_session().commit() |
mandaye/auth/espacefamille.py | ||
---|---|---|
10 | 10 |
|
11 | 11 |
class EspaceFamilleAuth(VincennesAuth): |
12 | 12 |
|
13 |
def replay(self, env, username, password, extra_values={}):
|
|
13 |
def replay(self, env, post_values):
|
|
14 | 14 |
""" This hack a hack method |
15 | 15 |
There is a bug in httplib so this method make a manual post |
16 | 16 |
""" |
... | ... | |
25 | 25 |
except Exception, e: |
26 | 26 |
return _500('Espace famille: no cookie JSESSIONID', e) |
27 | 27 |
|
28 |
params = {'codeFamille': username, 'motDePasse': password, 'idSession': fsession}
|
|
29 |
body = urllib.urlencode(params)
|
|
28 |
post_values['idSession'] = fsession
|
|
29 |
body = urllib.urlencode(post_values)
|
|
30 | 30 |
headers = "POST %s HTTP/1.1\r\nAccept-Encoding: identity\r\n\ |
31 | 31 |
Content-Length: %d\r\nConnection: close\r\n\ |
32 | 32 |
Accept: text/html; */*\r\nHost: %s\r\n\ |
mandaye/configs/biblio_vincennes.py | ||
---|---|---|
5 | 5 |
form_values = { |
6 | 6 |
'form_url': '/sezhame/page/connexion-abonne', |
7 | 7 |
'form_attrs': { 'id': 'dk-opac15-login-form', }, |
8 |
'post_fields': ['user', 'password'], |
|
8 | 9 |
'username_field': 'user', |
9 |
'password_field': 'password' |
|
10 |
'password_field': 'password',
|
|
10 | 11 |
} |
11 | 12 |
|
12 | 13 |
auth = VincennesAuth(form_values, 'biblio', 'https://www.vincennes.fr/comptecitoyen/auth') |
mandaye/configs/duonet_vincennes.py | ||
---|---|---|
9 | 9 |
form_values = { |
10 | 10 |
'form_url': 'Connect.aspx?key=%s' % duonet_key, |
11 | 11 |
'form_attrs': { 'name': 'form1' }, |
12 |
'post_fields': ['txtNomFoyer', 'txtDateNaissance', 'txtCode'], |
|
12 | 13 |
'username_field': 'txtNomFoyer', |
13 |
'birthdate_field': 'txtDateNaissance', |
|
14 |
'password_field': 'txtCode', |
|
14 |
'password_field': 'txtCode' |
|
15 | 15 |
} |
16 | 16 |
|
17 | 17 |
|
mandaye/configs/famille_vincennes.py | ||
---|---|---|
9 | 9 |
'form_action': '%s/login.do' % folder_target, |
10 | 10 |
'form_url': '%s/index.do' % folder_target, |
11 | 11 |
'form_attrs': { 'action': 'login.do', }, |
12 |
'post_fields': ['codeFamille', 'motDePasse'], |
|
12 | 13 |
'username_field': 'codeFamille', |
13 | 14 |
'password_field': 'motDePasse' |
14 | 15 |
} |
mandaye/exceptions.py | ||
---|---|---|
6 | 6 |
"Mandaye is somehow improperly configured" |
7 | 7 |
pass |
8 | 8 |
|
9 |
class MandayeException(Exception): |
|
10 |
"Mandaye generic exception" |
|
11 |
pass |
mandaye/models.py | ||
---|---|---|
1 | 1 |
|
2 |
import collections |
|
3 |
import json |
|
2 | 4 |
|
3 | 5 |
from datetime import datetime |
4 | 6 |
|
5 | 7 |
from sqlalchemy import Column, Integer, String, DateTime |
6 | 8 |
from sqlalchemy import ForeignKey |
7 | 9 |
from sqlalchemy.ext.declarative import declarative_base |
8 |
from sqlalchemy.orm import relationship, backref |
|
9 |
|
|
10 |
from sqlalchemy.ext.mutable import Mutable |
|
11 |
from sqlalchemy.orm import column_property, relationship, backref |
|
12 |
from sqlalchemy.types import TypeDecorator, VARCHAR |
|
13 |
|
|
14 |
|
|
15 |
class JSONEncodedDict(TypeDecorator): |
|
16 |
"Represents an immutable structure as a json-encoded string." |
|
17 |
|
|
18 |
impl = VARCHAR |
|
19 |
|
|
20 |
def process_bind_param(self, value, dialect): |
|
21 |
if value is not None: |
|
22 |
value = json.dumps(value) |
|
23 |
return value |
|
24 |
|
|
25 |
def process_result_value(self, value, dialect): |
|
26 |
if value is not None: |
|
27 |
value = json.loads(value) |
|
28 |
return value |
|
29 |
|
|
30 |
class MutationDict(Mutable, dict): |
|
31 |
|
|
32 |
@classmethod |
|
33 |
def coerce(cls, key, value): |
|
34 |
""" Convert plain dictionaries to MutationDict. """ |
|
35 |
if not isinstance(value, MutationDict): |
|
36 |
if isinstance(value, dict): |
|
37 |
return MutationDict(value) |
|
38 |
# this call will raise ValueError |
|
39 |
return Mutable.coerce(key, value) |
|
40 |
else: |
|
41 |
return value |
|
42 |
|
|
43 |
def __setitem__(self, key, value): |
|
44 |
""" Detect dictionary set events and emit change events. """ |
|
45 |
dict.__setitem__(self, key, value) |
|
46 |
self.changed() |
|
47 |
|
|
48 |
def __delitem__(self, key): |
|
49 |
""" Detect dictionary del events and emit change events. """ |
|
50 |
dict.__delitem__(self, key) |
|
51 |
self.changed() |
|
52 |
|
|
53 |
MutationDict.associate_with(JSONEncodedDict) |
|
10 | 54 |
Base = declarative_base() |
11 | 55 |
|
12 | 56 |
class Site(Base): |
... | ... | |
22 | 66 |
return "<Site('%s')>" % (self.name) |
23 | 67 |
|
24 | 68 |
class LocalUser(Base): |
25 |
""" Mandaye local user
|
|
69 |
""" Mandaye's user
|
|
26 | 70 |
""" |
27 | 71 |
__tablename__ = 'local_users' |
28 | 72 |
|
29 | 73 |
id = Column(Integer, primary_key=True) |
30 |
login = Column(String(150), nullable=True, unique=True)
|
|
74 |
login = Column(String(150), nullable=False, unique=True)
|
|
31 | 75 |
password = Column(String(25), nullable=True) |
32 |
fullname = Column(String(150), nullable=True) |
|
76 |
firstname = Column(String(150), nullable=True) |
|
77 |
lastname = Column(String(150), nullable=True) |
|
78 |
fullname = column_property(firstname + " " + lastname) |
|
79 |
|
|
80 |
creation_date = Column(DateTime, default=datetime.now(), nullable=False) |
|
81 |
last_connection = Column(DateTime, default=datetime.now()) |
|
33 | 82 |
|
34 | 83 |
def __init__(self, login=None, password=None, fullname=None): |
35 | 84 |
self.login = login |
... | ... | |
37 | 86 |
self.fullname = fullname |
38 | 87 |
|
39 | 88 |
def __repr__(self): |
40 |
if self.login: |
|
41 |
return "<LocalUser('%s')>" % (self.login) |
|
42 |
elif self.token: |
|
43 |
return "<LocalUser('%s')>" % (self.token) |
|
44 |
return "<LocalUser>" |
|
89 |
return "<LocalUser('%d %s')>" % (self.id, self.fullname) |
|
45 | 90 |
|
46 | 91 |
class ExtUser(Base): |
47 | 92 |
""" User of externals applications |
... | ... | |
49 | 94 |
__tablename__ = 'ext_users' |
50 | 95 |
|
51 | 96 |
id = Column(Integer, primary_key=True) |
52 |
login = Column(String(80), nullable=True)
|
|
53 |
password = Column(String(25), nullable=True)
|
|
54 |
birthdate = Column(String(15), nullable=True)
|
|
97 |
login = Column(String(150), nullable=False)
|
|
98 |
post_values = Column(JSONEncodedDict)
|
|
99 |
creation_date = Column(DateTime, default=datetime.now(), nullable=False)
|
|
55 | 100 |
last_connection = Column(DateTime, default=datetime.now()) |
56 | 101 |
|
57 | 102 |
local_user_id = Column(Integer, ForeignKey('local_users.id'), nullable=False) |
... | ... | |
59 | 104 |
local_user = relationship("LocalUser", backref=backref('ext_users')) |
60 | 105 |
site = relationship("Site", backref=backref('users')) |
61 | 106 |
|
62 |
def __init__(self, login=None, password=None):
|
|
107 |
def __init__(self, login=None, post_values=None):
|
|
63 | 108 |
self.login = login |
64 |
self.password = password
|
|
109 |
self.post_values = post_values
|
|
65 | 110 |
|
66 | 111 |
def __repr__(self): |
67 |
if self.login: |
|
68 |
return "<ExtUser('%s')>" % (self.login) |
|
69 |
elif self.token: |
|
70 |
return "<ExtUser('%s')>" % (self.token) |
|
71 |
return "<ExtUser>" |
|
72 |
|
|
112 |
return "<ExtUser '%d'>" % (self.id) |
|
73 | 113 |
|
74 | 114 |
|
mandaye/templates/biblio/associate.html | ||
---|---|---|
32 | 32 |
<form id="dk-opac15-login-form" method="post" accept-charset="UTF-8" action="${action_url}"> |
33 | 33 |
<div><div class="form-item"> |
34 | 34 |
<label for="edit-user">Numéro de carte : <span title="Ce champ est obligatoire." class="form-required">*</span></label> |
35 |
<input type="text" class="form-text required" value="" size="19" id="edit-user" name="username" maxlength="128">
|
|
35 |
<input type="text" class="form-text required" value="" size="19" id="edit-user" name="user" maxlength="128"> |
|
36 | 36 |
</div> |
37 | 37 |
<div class="form-item"> |
38 | 38 |
<label for="edit-password">Mot de passe : <span title="Ce champ est obligatoire." class="form-required">*</span></label> |
mandaye/templates/duonet/associate.html | ||
---|---|---|
47 | 47 |
<span id="lblPassword">Nom de famille</span> |
48 | 48 |
</td> |
49 | 49 |
<td align="left"> |
50 |
<input name="username" type="text" id="txtNomFoyer" autocomplete="off" style="font-weight:bold;" />
|
|
50 |
<input name="txtNomFoyer" type="text" id="txtNomFoyer" autocomplete="off" style="font-weight:bold;" />
|
|
51 | 51 |
</td> |
52 | 52 |
</tr> |
53 | 53 |
|
... | ... | |
57 | 57 |
</td> |
58 | 58 |
|
59 | 59 |
<td align="left"> |
60 |
<input name="birthdate" type="text" id="txtDateNaissance" autocomplete="off" style="font-weight:bold;" />
|
|
60 |
<input name="txtDateNaissance" type="text" id="txtDateNaissance" autocomplete="off" style="font-weight:bold;" />
|
|
61 | 61 |
<span id="lblPassword1" style="font-size:XX-Small;font-style:italic;">(Ex: 16/06/2008)</span> |
62 | 62 |
</td> |
63 | 63 |
</tr> |
... | ... | |
67 | 67 |
<span id="lblLogin">Code DuoNET</span> |
68 | 68 |
</td> |
69 | 69 |
<td align="left" style="padding-left:3px"> |
70 |
<input name="password" type="password" maxlength="13" id="txtCode" autocomplete="off" style="font-weight:bold;width:121px;" />
|
|
70 |
<input name="txtCode" type="password" maxlength="13" id="txtCode" autocomplete="off" style="font-weight:bold;width:121px;" />
|
|
71 | 71 |
<span id="lblPassword2" style="font-size:XX-Small;font-style:italic;">(Ex: 1994000001001)</span> |
72 | 72 |
</td> |
73 | 73 |
</tr> |
mandaye/templates/famille/associate.html | ||
---|---|---|
19 | 19 |
% endif |
20 | 20 |
<div class="firstline"> |
21 | 21 |
<label for="cdfmll" class="first"><b>⇒</b> Code famille</label> : |
22 |
<input type="text" id="cdfmll" class="txt" name="username" title="Indiquez votre code famille"><br>
|
|
22 |
<input type="text" id="cdfmll" class="txt" name="codeFamille" title="Indiquez votre code famille"><br>
|
|
23 | 23 |
<label for="mtdpss"><b>⇒</b> Mot de passe</label> : |
24 |
<input type="password" id="mtdpss" class="txt" name="password" title="Indiquez votre mot de passe"></div>
|
|
24 |
<input type="password" id="mtdpss" class="txt" name="motDePasse" title="Indiquez votre mot de passe"></div>
|
|
25 | 25 |
<input type="submit" class="submit" value="Associer"><br> |
26 | 26 |
</div> |
27 | 27 |
</form> |
Formats disponibles : Unified diff
Fix #709: authform now store every post values
username * mandaye/models.py: add dict management and post_values argument * mandaye/configs: upgrade configurations * mandaye/templates/ : upgrade templates for this new feature