very early session and registration support
This commit is contained in:
		
							parent
							
								
									91fcbb7101
								
							
						
					
					
						commit
						c843f444e7
					
				
					 14 changed files with 317 additions and 67 deletions
				
			
		|  | @ -2,33 +2,26 @@ import flask | ||||||
| from flask import request | from flask import request | ||||||
| import functools | import functools | ||||||
| import time | import time | ||||||
| import uuid |  | ||||||
| import warnings | import warnings | ||||||
| 
 | 
 | ||||||
| def _generate_session_token(): | import jsonify | ||||||
|     token = str(uuid.uuid4()) |  | ||||||
|     #print('MAKE SESSION', token) |  | ||||||
|     return token |  | ||||||
| 
 | 
 | ||||||
| def give_session_token(function): | 
 | ||||||
|  | def required_fields(fields): | ||||||
|  |     ''' | ||||||
|  |     Declare that the endpoint requires certain POST body fields. Without them, | ||||||
|  |     we respond with 400 and a message. | ||||||
|  |     ''' | ||||||
|  |     def with_required_fields(function): | ||||||
|         @functools.wraps(function) |         @functools.wraps(function) | ||||||
|         def wrapped(*args, **kwargs): |         def wrapped(*args, **kwargs): | ||||||
|         # Inject new token so the function doesn't know the difference |             if not all(field in request.form for field in fields): | ||||||
|         token = request.cookies.get('etiquette_session', None) |                 response = {'error': 'Required fields: %s' % ', '.join(fields)} | ||||||
|         if not token: |                 response = jsonify.make_json_response(response, status=400) | ||||||
|             token = _generate_session_token() |                 return response | ||||||
|             request.cookies = dict(request.cookies) |             return function(*args, **kwargs) | ||||||
|             request.cookies['etiquette_session'] = token |  | ||||||
| 
 |  | ||||||
|         ret = function(*args, **kwargs) |  | ||||||
| 
 |  | ||||||
|         # Send the token back to the client |  | ||||||
|         if not isinstance(ret, flask.Response): |  | ||||||
|             ret = flask.Response(ret) |  | ||||||
|         ret.set_cookie('etiquette_session', value=token, max_age=60) |  | ||||||
| 
 |  | ||||||
|         return ret |  | ||||||
|         return wrapped |         return wrapped | ||||||
|  |     return with_required_fields | ||||||
| 
 | 
 | ||||||
| def not_implemented(function): | def not_implemented(function): | ||||||
|     ''' |     ''' | ||||||
|  |  | ||||||
							
								
								
									
										157
									
								
								etiquette.py
									
									
									
									
									
								
							
							
						
						
									
										157
									
								
								etiquette.py
									
									
									
									
									
								
							|  | @ -12,6 +12,7 @@ import exceptions | ||||||
| import helpers | import helpers | ||||||
| import jsonify | import jsonify | ||||||
| import phototagger | import phototagger | ||||||
|  | import sessions | ||||||
| 
 | 
 | ||||||
| # pip install | # pip install | ||||||
| # https://raw.githubusercontent.com/voussoir/else/master/_voussoirkit/voussoirkit.zip | # https://raw.githubusercontent.com/voussoir/else/master/_voussoirkit/voussoirkit.zip | ||||||
|  | @ -27,6 +28,7 @@ site.debug = True | ||||||
| 
 | 
 | ||||||
| P = phototagger.PhotoDB() | P = phototagger.PhotoDB() | ||||||
| 
 | 
 | ||||||
|  | session_manager = sessions.SessionManager() | ||||||
| 
 | 
 | ||||||
| #################################################################################################### | #################################################################################################### | ||||||
| #################################################################################################### | #################################################################################################### | ||||||
|  | @ -34,6 +36,9 @@ P = phototagger.PhotoDB() | ||||||
| #################################################################################################### | #################################################################################################### | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | def back_url(): | ||||||
|  |     return request.args.get('goto') or request.referrer or '/' | ||||||
|  | 
 | ||||||
| def create_tag(easybake_string): | def create_tag(easybake_string): | ||||||
|     notes = P.easybake(easybake_string) |     notes = P.easybake(easybake_string) | ||||||
|     notes = [{'action': action, 'tagname': tagname} for (action, tagname) in notes] |     notes = [{'action': action, 'tagname': tagname} for (action, tagname) in notes] | ||||||
|  | @ -60,12 +65,6 @@ def delete_synonym(synonym): | ||||||
|     master_tag.remove_synonym(synonym) |     master_tag.remove_synonym(synonym) | ||||||
|     return {'action':'delete_synonym', 'synonym': synonym} |     return {'action':'delete_synonym', 'synonym': synonym} | ||||||
| 
 | 
 | ||||||
| def make_json_response(j, *args, **kwargs): |  | ||||||
|     dumped = json.dumps(j) |  | ||||||
|     response = flask.Response(dumped, *args, **kwargs) |  | ||||||
|     response.headers['Content-Type'] = 'application/json;charset=utf-8' |  | ||||||
|     return response |  | ||||||
| 
 |  | ||||||
| def P_album(albumid): | def P_album(albumid): | ||||||
|     try: |     try: | ||||||
|         return P.get_album(albumid) |         return P.get_album(albumid) | ||||||
|  | @ -159,11 +158,82 @@ def send_file(filepath): | ||||||
| #################################################################################################### | #################################################################################################### | ||||||
| #################################################################################################### | #################################################################################################### | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| @site.route('/') | @site.route('/') | ||||||
| @decorators.give_session_token | @session_manager.give_token | ||||||
| def root(): | def root(): | ||||||
|     motd = random.choice(P.config['motd_strings']) |     motd = random.choice(P.config['motd_strings']) | ||||||
|     return flask.render_template('root.html', motd=motd) |     session = session_manager.get(request) | ||||||
|  |     return flask.render_template('root.html', motd=motd, session=session) | ||||||
|  | 
 | ||||||
|  | @site.route('/login', methods=['GET']) | ||||||
|  | @session_manager.give_token | ||||||
|  | def get_login(): | ||||||
|  |     session = session_manager.get(request) | ||||||
|  |     return flask.render_template('login.html', session=session) | ||||||
|  | 
 | ||||||
|  | @site.route('/register', methods=['GET']) | ||||||
|  | def get_register(): | ||||||
|  |     return flask.redirect('/login') | ||||||
|  | 
 | ||||||
|  | @site.route('/login', methods=['POST']) | ||||||
|  | @session_manager.give_token | ||||||
|  | @decorators.required_fields(['username', 'password']) | ||||||
|  | def post_login(): | ||||||
|  |     if session_manager.get(request): | ||||||
|  |         flask.abort(403, 'You\'re already signed in.') | ||||||
|  | 
 | ||||||
|  |     username = request.form['username'] | ||||||
|  |     password = request.form['password'] | ||||||
|  |     user = P.get_user(username=username) | ||||||
|  |     try: | ||||||
|  |         user = P.login(user.id, password) | ||||||
|  |     except exceptions.WrongLogin: | ||||||
|  |         flask.abort(422, 'Wrong login.') | ||||||
|  |     session = sessions.Session(request, user) | ||||||
|  |     session_manager.add(session) | ||||||
|  |     response = flask.Response('redirect', status=302, headers={'Location': '/'}) | ||||||
|  |     return response | ||||||
|  | 
 | ||||||
|  | @site.route('/register', methods=['POST']) | ||||||
|  | @session_manager.give_token | ||||||
|  | @decorators.required_fields(['username', 'password_1', 'password_2']) | ||||||
|  | def post_register(): | ||||||
|  |     if session_manager.get(request): | ||||||
|  |         flask.abort(403, 'You\'re already signed in.') | ||||||
|  | 
 | ||||||
|  |     username = request.form['username'] | ||||||
|  |     password_1 = request.form['password_1'] | ||||||
|  |     password_2 = request.form['password_2'] | ||||||
|  | 
 | ||||||
|  |     if password_1 != password_2: | ||||||
|  |         flask.abort(422, 'Passwords do not match.') | ||||||
|  | 
 | ||||||
|  |     try: | ||||||
|  |         user = P.register_user(username, password_1) | ||||||
|  |     except exceptions.UsernameTooShort as e: | ||||||
|  |         flask.abort(422, 'Username shorter than minimum of %d' % P.config['min_username_length']) | ||||||
|  |     except exceptions.UsernameTooLong as e: | ||||||
|  |         flask.abort(422, 'Username longer than maximum of %d' % P.config['max_username_length']) | ||||||
|  |     except exceptions.InvalidUsernameChars as e: | ||||||
|  |         flask.abort(422, 'Username contains invalid characters %s' % e.args[0]) | ||||||
|  |     except exceptions.PasswordTooShort as e: | ||||||
|  |         flask.abort(422, 'Password is shorter than minimum of %d' % P.config['min_password_length']) | ||||||
|  |     except exceptions.UserExists as e: | ||||||
|  |         flask.abort(422, 'User %s already exists' % e.args[0]) | ||||||
|  | 
 | ||||||
|  |     session = sessions.Session(request, user) | ||||||
|  |     session_manager.add(session) | ||||||
|  |     response = flask.Response('redirect', status=302, headers={'Location': '/'}) | ||||||
|  |     return response | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @site.route('/logout', methods=['GET', 'POST']) | ||||||
|  | @session_manager.give_token | ||||||
|  | def logout(): | ||||||
|  |     session_manager.remove(request) | ||||||
|  |     response = flask.Response('redirect', status=302, headers={'Location': back_url()}) | ||||||
|  |     return response | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @site.route('/favicon.ico') | @site.route('/favicon.ico') | ||||||
|  | @ -182,22 +252,24 @@ def get_album_core(albumid): | ||||||
|     return album |     return album | ||||||
| 
 | 
 | ||||||
| @site.route('/album/<albumid>') | @site.route('/album/<albumid>') | ||||||
| @decorators.give_session_token | @session_manager.give_token | ||||||
| def get_album_html(albumid): | def get_album_html(albumid): | ||||||
|     album = get_album_core(albumid) |     album = get_album_core(albumid) | ||||||
|  |     session = session_manager.get(request) | ||||||
|     response = flask.render_template( |     response = flask.render_template( | ||||||
|         'album.html', |         'album.html', | ||||||
|         album=album, |         album=album, | ||||||
|         photos=album['photos'], |         photos=album['photos'], | ||||||
|  |         session=session, | ||||||
|         view=request.args.get('view', 'grid'), |         view=request.args.get('view', 'grid'), | ||||||
|     ) |     ) | ||||||
|     return response |     return response | ||||||
| 
 | 
 | ||||||
| @site.route('/album/<albumid>.json') | @site.route('/album/<albumid>.json') | ||||||
| @decorators.give_session_token | @session_manager.give_token | ||||||
| def get_album_json(albumid): | def get_album_json(albumid): | ||||||
|     album = get_album_core(albumid) |     album = get_album_core(albumid) | ||||||
|     return make_json_response(album) |     return jsonify.make_json_response(album) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @site.route('/album/<albumid>.tar') | @site.route('/album/<albumid>.tar') | ||||||
|  | @ -218,21 +290,24 @@ def get_albums_core(): | ||||||
|     return albums |     return albums | ||||||
| 
 | 
 | ||||||
| @site.route('/albums') | @site.route('/albums') | ||||||
| @decorators.give_session_token | @session_manager.give_token | ||||||
| def get_albums_html(): | def get_albums_html(): | ||||||
|     albums = get_albums_core() |     albums = get_albums_core() | ||||||
|     return flask.render_template('albums.html', albums=albums) |     session = session_manager.get(request) | ||||||
|  |     return flask.render_template('albums.html', albums=albums, session=session) | ||||||
| 
 | 
 | ||||||
| @site.route('/albums.json') | @site.route('/albums.json') | ||||||
| @decorators.give_session_token | @session_manager.give_token | ||||||
| def get_albums_json(): | def get_albums_json(): | ||||||
|     albums = get_albums_core() |     albums = get_albums_core() | ||||||
|     return make_json_response(albums) |     return jsonify.make_json_response(albums) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @site.route('/bookmarks') | @site.route('/bookmarks') | ||||||
|  | @session_manager.give_token | ||||||
| def get_bookmarks(): | def get_bookmarks(): | ||||||
|     return flask.render_template('bookmarks.html') |     session = session_manager.get(request) | ||||||
|  |     return flask.render_template('bookmarks.html', session=session) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @site.route('/file/<photoid>') | @site.route('/file/<photoid>') | ||||||
|  | @ -268,20 +343,20 @@ def get_photo_core(photoid): | ||||||
|     return photo |     return photo | ||||||
| 
 | 
 | ||||||
| @site.route('/photo/<photoid>', methods=['GET']) | @site.route('/photo/<photoid>', methods=['GET']) | ||||||
| @decorators.give_session_token | @session_manager.give_token | ||||||
| def get_photo_html(photoid): | def get_photo_html(photoid): | ||||||
|     photo = get_photo_core(photoid) |     photo = get_photo_core(photoid) | ||||||
|     photo['tags'].sort(key=lambda x: x['qualified_name']) |     photo['tags'].sort(key=lambda x: x['qualified_name']) | ||||||
|     return flask.render_template('photo.html', photo=photo) |     session = session_manager.get(request) | ||||||
|  |     return flask.render_template('photo.html', photo=photo, session=session) | ||||||
| 
 | 
 | ||||||
| @site.route('/photo/<photoid>.json', methods=['GET']) | @site.route('/photo/<photoid>.json', methods=['GET']) | ||||||
| @decorators.give_session_token | @session_manager.give_token | ||||||
| def get_photo_json(photoid): | def get_photo_json(photoid): | ||||||
|     photo = get_photo_core(photoid) |     photo = get_photo_core(photoid) | ||||||
|     photo = make_json_response(photo) |     photo = jsonify.make_json_response(photo) | ||||||
|     return photo |     return photo | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| def get_search_core(): | def get_search_core(): | ||||||
|     #print(request.args) |     #print(request.args) | ||||||
| 
 | 
 | ||||||
|  | @ -418,11 +493,12 @@ def get_search_core(): | ||||||
|     return final_results |     return final_results | ||||||
| 
 | 
 | ||||||
| @site.route('/search') | @site.route('/search') | ||||||
| @decorators.give_session_token | @session_manager.give_token | ||||||
| def get_search_html(): | def get_search_html(): | ||||||
|     search_results = get_search_core() |     search_results = get_search_core() | ||||||
|     search_kwargs = search_results['search_kwargs'] |     search_kwargs = search_results['search_kwargs'] | ||||||
|     qualname_map = search_results['qualname_map'] |     qualname_map = search_results['qualname_map'] | ||||||
|  |     session = session_manager.get(request) | ||||||
|     response = flask.render_template( |     response = flask.render_template( | ||||||
|         'search.html', |         'search.html', | ||||||
|         next_page_url=search_results['next_page_url'], |         next_page_url=search_results['next_page_url'], | ||||||
|  | @ -430,13 +506,14 @@ def get_search_html(): | ||||||
|         photos=search_results['photos'], |         photos=search_results['photos'], | ||||||
|         qualname_map=json.dumps(qualname_map), |         qualname_map=json.dumps(qualname_map), | ||||||
|         search_kwargs=search_kwargs, |         search_kwargs=search_kwargs, | ||||||
|  |         session=session, | ||||||
|         total_tags=search_results['total_tags'], |         total_tags=search_results['total_tags'], | ||||||
|         warns=search_results['warns'], |         warns=search_results['warns'], | ||||||
|     ) |     ) | ||||||
|     return response |     return response | ||||||
| 
 | 
 | ||||||
| @site.route('/search.json') | @site.route('/search.json') | ||||||
| @decorators.give_session_token | @session_manager.give_token | ||||||
| def get_search_json(): | def get_search_json(): | ||||||
|     search_results = get_search_core() |     search_results = get_search_core() | ||||||
|     #search_kwargs = search_results['search_kwargs'] |     #search_kwargs = search_results['search_kwargs'] | ||||||
|  | @ -445,7 +522,7 @@ def get_search_json(): | ||||||
|     include_qualname_map = helpers.truthystring(include_qualname_map) |     include_qualname_map = helpers.truthystring(include_qualname_map) | ||||||
|     if not include_qualname_map: |     if not include_qualname_map: | ||||||
|         search_results.pop('qualname_map') |         search_results.pop('qualname_map') | ||||||
|     return make_json_response(search_results) |     return jsonify.make_json_response(search_results) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @site.route('/static/<filename>') | @site.route('/static/<filename>') | ||||||
|  | @ -468,18 +545,19 @@ def get_tags_core(specific_tag=None): | ||||||
| 
 | 
 | ||||||
| @site.route('/tags') | @site.route('/tags') | ||||||
| @site.route('/tags/<specific_tag>') | @site.route('/tags/<specific_tag>') | ||||||
| @decorators.give_session_token | @session_manager.give_token | ||||||
| def get_tags_html(specific_tag=None): | def get_tags_html(specific_tag=None): | ||||||
|     tags = get_tags_core(specific_tag) |     tags = get_tags_core(specific_tag) | ||||||
|     return flask.render_template('tags.html', tags=tags) |     session = session_manager.get(request) | ||||||
|  |     return flask.render_template('tags.html', tags=tags, session=session) | ||||||
| 
 | 
 | ||||||
| @site.route('/tags.json') | @site.route('/tags.json') | ||||||
| @site.route('/tags/<specific_tag>.json') | @site.route('/tags/<specific_tag>.json') | ||||||
| @decorators.give_session_token | @session_manager.give_token | ||||||
| def get_tags_json(specific_tag=None): | def get_tags_json(specific_tag=None): | ||||||
|     tags = get_tags_core(specific_tag) |     tags = get_tags_core(specific_tag) | ||||||
|     tags = [t[0] for t in tags] |     tags = [t[0] for t in tags] | ||||||
|     return make_json_response(tags) |     return jsonify.make_json_response(tags) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @site.route('/thumbnail/<photoid>') | @site.route('/thumbnail/<photoid>') | ||||||
|  | @ -495,7 +573,7 @@ def get_thumbnail(photoid): | ||||||
| 
 | 
 | ||||||
| @site.route('/album/<albumid>', methods=['POST']) | @site.route('/album/<albumid>', methods=['POST']) | ||||||
| @site.route('/album/<albumid>.json', methods=['POST']) | @site.route('/album/<albumid>.json', methods=['POST']) | ||||||
| @decorators.give_session_token | @session_manager.give_token | ||||||
| def post_edit_album(albumid): | def post_edit_album(albumid): | ||||||
|     ''' |     ''' | ||||||
|     Edit the album's title and description. |     Edit the album's title and description. | ||||||
|  | @ -512,18 +590,18 @@ def post_edit_album(albumid): | ||||||
|             tag = P_tag(tag) |             tag = P_tag(tag) | ||||||
|         except exceptions.NoSuchTag: |         except exceptions.NoSuchTag: | ||||||
|             response = {'error': 'That tag doesnt exist', 'tagname': tag} |             response = {'error': 'That tag doesnt exist', 'tagname': tag} | ||||||
|             return make_json_response(response, status=404) |             return jsonify.make_json_response(response, status=404) | ||||||
|         recursive = request.form.get('recursive', False) |         recursive = request.form.get('recursive', False) | ||||||
|         recursive = helpers.truthystring(recursive) |         recursive = helpers.truthystring(recursive) | ||||||
|         album.add_tag_to_all(tag, nested_children=recursive) |         album.add_tag_to_all(tag, nested_children=recursive) | ||||||
|         response['action'] = action |         response['action'] = action | ||||||
|         response['tagname'] = tag.name |         response['tagname'] = tag.name | ||||||
|         return make_json_response(response) |         return jsonify.make_json_response(response) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @site.route('/photo/<photoid>', methods=['POST']) | @site.route('/photo/<photoid>', methods=['POST']) | ||||||
| @site.route('/photo/<photoid>.json', methods=['POST']) | @site.route('/photo/<photoid>.json', methods=['POST']) | ||||||
| @decorators.give_session_token | @session_manager.give_token | ||||||
| def post_edit_photo(photoid): | def post_edit_photo(photoid): | ||||||
|     ''' |     ''' | ||||||
|     Add and remove tags from photos. |     Add and remove tags from photos. | ||||||
|  | @ -548,22 +626,22 @@ def post_edit_photo(photoid): | ||||||
|         tag = P.get_tag(tag) |         tag = P.get_tag(tag) | ||||||
|     except exceptions.NoSuchTag: |     except exceptions.NoSuchTag: | ||||||
|         response = {'error': 'That tag doesnt exist', 'tagname': tag} |         response = {'error': 'That tag doesnt exist', 'tagname': tag} | ||||||
|         return make_json_response(response, status=404) |         return jsonify.make_json_response(response, status=404) | ||||||
| 
 | 
 | ||||||
|     method(tag) |     method(tag) | ||||||
|     response['action'] = action |     response['action'] = action | ||||||
|     #response['tagid'] = tag.id |     #response['tagid'] = tag.id | ||||||
|     response['tagname'] = tag.name |     response['tagname'] = tag.name | ||||||
|     return make_json_response(response) |     return jsonify.make_json_response(response) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @site.route('/tags', methods=['POST']) | @site.route('/tags', methods=['POST']) | ||||||
| @decorators.give_session_token | @session_manager.give_token | ||||||
| def post_edit_tags(): | def post_edit_tags(): | ||||||
|     ''' |     ''' | ||||||
|     Create and delete tags and synonyms. |     Create and delete tags and synonyms. | ||||||
|     ''' |     ''' | ||||||
|     print(request.form) |     #print(request.form) | ||||||
|     status = 200 |     status = 200 | ||||||
|     if 'create_tag' in request.form: |     if 'create_tag' in request.form: | ||||||
|         action = 'create_tag' |         action = 'create_tag' | ||||||
|  | @ -605,6 +683,13 @@ def post_edit_tags(): | ||||||
|     return response |     return response | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @site.route('/apitest') | ||||||
|  | @session_manager.give_token | ||||||
|  | def apitest(): | ||||||
|  |     response = flask.Response('testing') | ||||||
|  |     response.set_cookie('etiquette_session', 'don\'t overwrite me') | ||||||
|  |     return response | ||||||
|  | 
 | ||||||
| if __name__ == '__main__': | if __name__ == '__main__': | ||||||
|     #site.run(threaded=True) |     #site.run(threaded=True) | ||||||
|     pass |     pass | ||||||
|  |  | ||||||
|  | @ -2,10 +2,10 @@ import datetime | ||||||
| import math | import math | ||||||
| import mimetypes | import mimetypes | ||||||
| import os | import os | ||||||
|  | import warnings | ||||||
| 
 | 
 | ||||||
| import constants | import constants | ||||||
| import exceptions | import exceptions | ||||||
| import warnings |  | ||||||
| 
 | 
 | ||||||
| from voussoirkit import bytestring | from voussoirkit import bytestring | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,4 +1,12 @@ | ||||||
|  | import flask | ||||||
| import helpers | import helpers | ||||||
|  | import json | ||||||
|  | 
 | ||||||
|  | def make_json_response(j, *args, **kwargs): | ||||||
|  |     dumped = json.dumps(j) | ||||||
|  |     response = flask.Response(dumped, *args, **kwargs) | ||||||
|  |     response.headers['Content-Type'] = 'application/json;charset=utf-8' | ||||||
|  |     return response | ||||||
| 
 | 
 | ||||||
| def album(a, minimal=False): | def album(a, minimal=False): | ||||||
|     j = { |     j = { | ||||||
|  |  | ||||||
							
								
								
									
										79
									
								
								sessions.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								sessions.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,79 @@ | ||||||
|  | import flask | ||||||
|  | from flask import request | ||||||
|  | import functools | ||||||
|  | import helpers | ||||||
|  | import uuid | ||||||
|  | 
 | ||||||
|  | def _generate_token(): | ||||||
|  |     token = str(uuid.uuid4()) | ||||||
|  |     #print('MAKE SESSION', token) | ||||||
|  |     return token | ||||||
|  | 
 | ||||||
|  | def _normalize_token(token): | ||||||
|  |     if isinstance(token, flask.Request): | ||||||
|  |         token = token.cookies.get('etiquette_session', None) | ||||||
|  |      | ||||||
|  | class SessionManager: | ||||||
|  |     def __init__(self): | ||||||
|  |         self.sessions = {} | ||||||
|  | 
 | ||||||
|  |     def add(self, session): | ||||||
|  |         self.sessions[session.token] = session | ||||||
|  | 
 | ||||||
|  |     def get(self, token): | ||||||
|  |         token = _normalize_token(token) | ||||||
|  |         return self.sessions.get(token, None) | ||||||
|  | 
 | ||||||
|  |     def give_token(self, function): | ||||||
|  |         ''' | ||||||
|  |         This decorator ensures that the user has an `etiquette_session` cookie | ||||||
|  |         before reaching the request handler. | ||||||
|  |         If the user does not have the cookie, they are given one. | ||||||
|  |         If they do, its lifespan is reset. | ||||||
|  |         ''' | ||||||
|  |         @functools.wraps(function) | ||||||
|  |         def wrapped(*args, **kwargs): | ||||||
|  |             # Inject new token so the function doesn't know the difference | ||||||
|  |             token = request.cookies.get('etiquette_session', None) | ||||||
|  |             if not token: | ||||||
|  |                 token = _generate_token() | ||||||
|  |                 request.cookies = dict(request.cookies) | ||||||
|  |                 request.cookies['etiquette_session'] = token | ||||||
|  | 
 | ||||||
|  |             response = function(*args, **kwargs) | ||||||
|  | 
 | ||||||
|  |             if not isinstance(response, flask.Response): | ||||||
|  |                 response = flask.Response(response) | ||||||
|  | 
 | ||||||
|  |             # Send the token back to the client | ||||||
|  |             # but only if the endpoint didn't manually set the cookie. | ||||||
|  |             for (headerkey, value) in response.headers: | ||||||
|  |                 if headerkey == 'Set-Cookie' and value.startswith('etiquette_session='): | ||||||
|  |                     break | ||||||
|  |             else: | ||||||
|  |                 response.set_cookie('etiquette_session', value=token, max_age=86400) | ||||||
|  |                 self.maintain(token) | ||||||
|  | 
 | ||||||
|  |             return response | ||||||
|  |         return wrapped | ||||||
|  | 
 | ||||||
|  |     def maintain(self, token): | ||||||
|  |         session = self.get(token) | ||||||
|  |         if session: | ||||||
|  |             session.maintain() | ||||||
|  | 
 | ||||||
|  |     def remove(self, token): | ||||||
|  |         token = _normalize_token(token) | ||||||
|  |         if token in self.sessions: | ||||||
|  |             self.sessions.pop(token) | ||||||
|  | 
 | ||||||
|  | class Session: | ||||||
|  |     def __init__(self, request, user): | ||||||
|  |         self.token = _normalize_token(request) | ||||||
|  |         self.user = user | ||||||
|  |         self.ip_address = request.remote_addr | ||||||
|  |         self.user_agent = request.headers.get('User-Agent', '') | ||||||
|  |         self.last_activity = int(helpers.now()) | ||||||
|  | 
 | ||||||
|  |     def maintain(self): | ||||||
|  |         self.last_activity = int(helpers.now()) | ||||||
|  | @ -22,7 +22,7 @@ p | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| <body> | <body> | ||||||
| {{header.make_header()}} | {{header.make_header(session=session)}} | ||||||
| <div id="content_body"> | <div id="content_body"> | ||||||
|     <h2>{{album["title"]}}</h2> |     <h2>{{album["title"]}}</h2> | ||||||
|     <p>{{album["description"]}}</p> |     <p>{{album["description"]}}</p> | ||||||
|  |  | ||||||
|  | @ -16,7 +16,7 @@ | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| <body> | <body> | ||||||
| {{header.make_header()}} | {{header.make_header(session=session)}} | ||||||
| <div id="content_body"> | <div id="content_body"> | ||||||
|     {% for album in albums %} |     {% for album in albums %} | ||||||
|     {% if album["title"] %} |     {% if album["title"] %} | ||||||
|  |  | ||||||
|  | @ -13,7 +13,7 @@ | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| <body> | <body> | ||||||
|     {{header.make_header()}} |     {{header.make_header(session=session)}} | ||||||
|     <div id="content_body"> |     <div id="content_body"> | ||||||
|         <a href="/search?has_tags=no&orderby=random-desc&mimetype=image">Needs tagging</a> |         <a href="/search?has_tags=no&orderby=random-desc&mimetype=image">Needs tagging</a> | ||||||
|     </div> |     </div> | ||||||
|  |  | ||||||
|  | @ -1,7 +1,13 @@ | ||||||
| {% macro make_header() %} | {% macro make_header(session) %} | ||||||
| <div id="header"> | <div id="header"> | ||||||
|     <a class="header_element" href="/">Etiquette</a> |     <a class="header_element" href="/">Etiquette</a> | ||||||
|     <a class="header_element" href="/search">Search</a> |     <a class="header_element" href="/search">Search</a> | ||||||
|     <a class="header_element" href="/tags">Tags</a> |     <a class="header_element" href="/tags">Tags</a> | ||||||
|  |     {% if session %} | ||||||
|  |     <a class="header_element" href="/user/{{session.user.username}}">{{session.user.username}}</a> | ||||||
|  |     <a class="header_element" href="/logout" style="flex:0">Logout</a> | ||||||
|  |     {% else %} | ||||||
|  |     <a class="header_element" href="/login">Log in</a> | ||||||
|  |     {% endif %} | ||||||
| </div> | </div> | ||||||
| {% endmacro %} | {% endmacro %} | ||||||
							
								
								
									
										74
									
								
								templates/login.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								templates/login.html
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,74 @@ | ||||||
|  | <!DOCTYPE html5> | ||||||
|  | <html> | ||||||
|  | <head> | ||||||
|  |     {% import "header.html" as header %} | ||||||
|  |     <title>Login/Register</title> | ||||||
|  |     <meta charset="UTF-8"> | ||||||
|  |     <link rel="stylesheet" href="/static/common.css"> | ||||||
|  |     <script src="/static/common.js"></script> | ||||||
|  | 
 | ||||||
|  | <style> | ||||||
|  | #content_body | ||||||
|  | { | ||||||
|  |     justify-content: center; | ||||||
|  |     align-content: center; | ||||||
|  |     flex: 1; | ||||||
|  | } | ||||||
|  | #login_register_box | ||||||
|  | { | ||||||
|  |     margin: auto; | ||||||
|  |     display: flex; | ||||||
|  |     flex-direction: row; | ||||||
|  | } | ||||||
|  | form | ||||||
|  | { | ||||||
|  |     flex: 1; | ||||||
|  |     display: flex; | ||||||
|  |     flex-direction: column; | ||||||
|  |     padding: 25px; | ||||||
|  |     margin: 25px; | ||||||
|  |     border: 1px black solid; | ||||||
|  |     border-radius: 6px; | ||||||
|  | } | ||||||
|  | form > * | ||||||
|  | { | ||||||
|  |     margin-top: 5px; | ||||||
|  |     margin-bottom: 5px; | ||||||
|  | } | ||||||
|  | input | ||||||
|  | { | ||||||
|  |     width: 300px; | ||||||
|  | } | ||||||
|  | button | ||||||
|  | { | ||||||
|  |     width: 80px; | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | </head> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | <body> | ||||||
|  |     {{header.make_header(session=session)}} | ||||||
|  |     <div id="content_body"> | ||||||
|  |         <div id="login_register_box"> | ||||||
|  |         <form id="login_form" action="/login" method="post"> | ||||||
|  |             <span>Log in</span> | ||||||
|  |             <input type="text" name="username" placeholder="username"> | ||||||
|  |             <input type="password" name="password" placeholder="password"> | ||||||
|  |             <button type="submit">Log in</button> | ||||||
|  |         </form> | ||||||
|  |         <form id="register_form" action="/register" method="post"> | ||||||
|  |             <span>Register</span> | ||||||
|  |             <input type="text" name="username" placeholder="username"> | ||||||
|  |             <input type="password" name="password_1" placeholder="password"> | ||||||
|  |             <input type="password" name="password_2" placeholder="password again"> | ||||||
|  |             <button type="submit">Register</button> | ||||||
|  |         </form> | ||||||
|  |         </div> | ||||||
|  |     </div> | ||||||
|  | </body> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | <script type="text/javascript"> | ||||||
|  | </script> | ||||||
|  | </html> | ||||||
|  | @ -87,7 +87,7 @@ | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| <body> | <body> | ||||||
| {{header.make_header()}} | {{header.make_header(session=session)}} | ||||||
| <div id="content_body"> | <div id="content_body"> | ||||||
| <div id="left"> | <div id="left"> | ||||||
|     <div id="editor_area"> |     <div id="editor_area"> | ||||||
|  |  | ||||||
|  | @ -34,6 +34,11 @@ a:hover | ||||||
|     <a href="/tags">Browse tags</a> |     <a href="/tags">Browse tags</a> | ||||||
|     <a href="/albums">Browse albums</a> |     <a href="/albums">Browse albums</a> | ||||||
|     <a href="/bookmarks">Bookmarks</a> |     <a href="/bookmarks">Bookmarks</a> | ||||||
|  |     {% if session %} | ||||||
|  |     <a href="/user/{{session.user.username}}">{{session.user.username}}</a> | ||||||
|  |     {% else %} | ||||||
|  |     <a href="/login">Log in</a> | ||||||
|  |     {% endif %} | ||||||
| </body> | </body> | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -119,7 +119,7 @@ form | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| <body> | <body> | ||||||
|     {{header.make_header()}} |     {{header.make_header(session=session)}} | ||||||
|     <div id="error_message_area"> |     <div id="error_message_area"> | ||||||
|         {% for warn in warns %} |         {% for warn in warns %} | ||||||
|         <span class="search_warning">{{warn}}</span> |         <span class="search_warning">{{warn}}</span> | ||||||
|  |  | ||||||
|  | @ -59,7 +59,7 @@ body | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| <body> | <body> | ||||||
| {{header.make_header()}} | {{header.make_header(session=session)}} | ||||||
| <div id="content_body"> | <div id="content_body"> | ||||||
|     <div id="left"> |     <div id="left"> | ||||||
|         <ul> |         <ul> | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue