Compare commits
	
		
			7 Commits
		
	
	
		
			5551b6438a
			...
			58ebbf601b
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 58ebbf601b | |||
| d0cb54350f | |||
| 9a6e439cda | |||
|   | a6ce11b10b | ||
|   | 79d59dc29c | ||
|   | 79ed648c7b | ||
|   | adea2b1545 | 
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -2,3 +2,5 @@ | |||||||
| __pycache__ | __pycache__ | ||||||
| *.swp | *.swp | ||||||
| jon/config.json | jon/config.json | ||||||
|  | venv/ | ||||||
|  | .venv/ | ||||||
|  | |||||||
| @ -1,9 +1,11 @@ | |||||||
| import inspect | import inspect | ||||||
| import json | import json | ||||||
|  | import sys | ||||||
| 
 | 
 | ||||||
| from flask import Flask, render_template | from flask import Flask, render_template | ||||||
| 
 | 
 | ||||||
| from . import ( | from . import ( | ||||||
|  |     auth, | ||||||
|     db, |     db, | ||||||
|     entry, |     entry, | ||||||
|     inventory, |     inventory, | ||||||
| @ -21,6 +23,12 @@ def create_app(): | |||||||
| 
 | 
 | ||||||
|     db.init_app(app) |     db.init_app(app) | ||||||
| 
 | 
 | ||||||
|  |     # This function denies every request until `auth.ACCESS_TOKEN` | ||||||
|  |     # is passed using `?token=` to authenticate the session. | ||||||
|  |     @app.before_request | ||||||
|  |     def before_req_fun(): | ||||||
|  |         return auth.before_request() | ||||||
|  | 
 | ||||||
|     @app.context_processor |     @app.context_processor | ||||||
|     def utility_processor(): |     def utility_processor(): | ||||||
|         return dict(inspect.getmembers(template_utils, inspect.isfunction)) |         return dict(inspect.getmembers(template_utils, inspect.isfunction)) | ||||||
| @ -28,8 +36,12 @@ def create_app(): | |||||||
|     app.register_blueprint(location.bp) |     app.register_blueprint(location.bp) | ||||||
|     app.register_blueprint(inventory.bp) |     app.register_blueprint(inventory.bp) | ||||||
|     app.register_blueprint(entry.bp) |     app.register_blueprint(entry.bp) | ||||||
|  |     app.register_blueprint(auth.bp) | ||||||
|  | 
 | ||||||
|     @app.route("/") |     @app.route("/") | ||||||
|     def index(): |     def index(): | ||||||
|         return render_template("index.html") |         return render_template("index.html") | ||||||
| 
 | 
 | ||||||
|  |     print(f"Jon started. Token: {auth.ACCESS_TOKEN}", file=sys.stderr) | ||||||
|  | 
 | ||||||
|     return app |     return app | ||||||
|  | |||||||
							
								
								
									
										41
									
								
								jon/auth.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								jon/auth.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,41 @@ | |||||||
|  | import random | ||||||
|  | import string | ||||||
|  | 
 | ||||||
|  | from flask import Blueprint, request, redirect, render_template, session | ||||||
|  | 
 | ||||||
|  | bp = Blueprint("auth", __name__, url_prefix="/auth") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ACCESS_TOKEN = "".join(random.choice(string.ascii_lowercase) for i in range(64)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ALLOWED_PATHS = [ | ||||||
|  |     "/favicon.ico", | ||||||
|  |     "/static/jon.css" | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def before_request(): | ||||||
|  |     """ | ||||||
|  |     If the correct token query parameter is passed along with any request, | ||||||
|  |     we mark this session authenticated by setting `session["authenticated"]`. | ||||||
|  |     Unless the session is authenticated, all requests results in a 403 FORBIDDEN. | ||||||
|  |     """ | ||||||
|  |     if "token" in request.args: | ||||||
|  |         if request.args["token"] == ACCESS_TOKEN: | ||||||
|  |             session["authenticated"] = () | ||||||
|  |         # Reload the page without query parameters | ||||||
|  |         return redirect(request.path) | ||||||
|  | 
 | ||||||
|  |     # Don't deny any paths in `ALLOWED_PATHS` | ||||||
|  |     if request.path in ALLOWED_PATHS: | ||||||
|  |         return | ||||||
|  | 
 | ||||||
|  |     if not "authenticated" in session: | ||||||
|  |         return render_template("auth/denied.html"), 403 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @bp.get("/logout") | ||||||
|  | def logout(): | ||||||
|  |     session.pop("authenticated", None) | ||||||
|  |     return redirect("/") | ||||||
							
								
								
									
										65
									
								
								jon/static/jon.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								jon/static/jon.css
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,65 @@ | |||||||
|  | html { | ||||||
|  |   font-family: Helvetica, sans-serif; | ||||||
|  | } | ||||||
|  | h1 { | ||||||
|  |   margin: 0; | ||||||
|  | } | ||||||
|  | nav > ul { | ||||||
|  |   padding-left: 0; | ||||||
|  | } | ||||||
|  | nav > ul > li { | ||||||
|  |   display: inline-block; | ||||||
|  |   list-style: none; | ||||||
|  | } | ||||||
|  | nav > ul > li + li:before { | ||||||
|  |   content: ' · '; | ||||||
|  | } | ||||||
|  | .current-page > a { | ||||||
|  |   position: relative; | ||||||
|  | } | ||||||
|  | .current-page > a:after { | ||||||
|  |   content: '↓'; | ||||||
|  |   font-size: 0.8em; | ||||||
|  |   box-sizing: border-box; | ||||||
|  |   position: absolute; | ||||||
|  |   display: block; | ||||||
|  |   right: 50%; | ||||||
|  |   top: -1em; | ||||||
|  |   width: 1em; | ||||||
|  |   text-align: center; | ||||||
|  |   margin-right: -0.5em; | ||||||
|  |   animation: wiggle 0.8s ease-in-out 0s infinite; | ||||||
|  |   /* animation-direction: alternate; */ | ||||||
|  | } | ||||||
|  | .--align-left { | ||||||
|  |   text-align: left; | ||||||
|  | } | ||||||
|  | .--align-right { | ||||||
|  |   text-align: right; | ||||||
|  | } | ||||||
|  | .--centered { | ||||||
|  |   text-align: center; | ||||||
|  | } | ||||||
|  | @keyframes wiggle { | ||||||
|  |   0%, 100% { margin-top: 0; } | ||||||
|  |   50% { margin-top: -0.5em; } | ||||||
|  |   /* 100% { transform: rotate(1turn); } */ | ||||||
|  | } | ||||||
|  | table { | ||||||
|  |   border-spacing: .5em 0; | ||||||
|  | } | ||||||
|  | th { | ||||||
|  |   font-size: .8em; | ||||||
|  | } | ||||||
|  | @media print { | ||||||
|  |   body { | ||||||
|  |     font-size: 8px; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | .form-input > label { | ||||||
|  |   font-size: .8em; | ||||||
|  | } | ||||||
|  | .form-input > input:not([type=radio]), | ||||||
|  | .form-input > select { | ||||||
|  |   display: block; | ||||||
|  | } | ||||||
							
								
								
									
										36
									
								
								jon/templates/auth/denied.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								jon/templates/auth/denied.html
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,36 @@ | |||||||
|  | 
 | ||||||
|  | <!DOCTYPE html> | ||||||
|  | <html> | ||||||
|  |   <head> | ||||||
|  |     <meta charset="UTF-8"> | ||||||
|  |     <title>jon · not authenticated</title> | ||||||
|  |     <link rel="stylesheet" href="/static/jon.css"> | ||||||
|  |   </head> | ||||||
|  |   <body> | ||||||
|  |     <header> | ||||||
|  |       <h1>jon</h1> | ||||||
|  | 
 | ||||||
|  |       {% if config.DEBUG %} | ||||||
|  |       <details> | ||||||
|  |         <summary><code>config</code></summary> | ||||||
|  |         <pre>{% for key, value in config.items() %}{{ key }} = {{ value }} | ||||||
|  | {% endfor %}</pre> | ||||||
|  |       </details> | ||||||
|  |       {% endif %} | ||||||
|  |     </header> | ||||||
|  | 
 | ||||||
|  |     <main> | ||||||
|  |       <p> | ||||||
|  |         Damit kein Schabernack getrieben wird müssen wir sicherstellen, dass du die Person bist die jon ausgeführt hat. | ||||||
|  |         Gib unten das Token ein, welches jon beim Starten ausgegeben hat. | ||||||
|  |       </p> | ||||||
|  |       <form method="GET"> | ||||||
|  |         <div class="form-input"> | ||||||
|  |           <label for="token">Token</label> | ||||||
|  |           <input type="password" name="token" placeholder="Token" id="token"> | ||||||
|  |         </div> | ||||||
|  |         <button type="submit">Authentifizieren</button> | ||||||
|  |       </form> | ||||||
|  |     </main> | ||||||
|  |   </body> | ||||||
|  | </html> | ||||||
| @ -3,83 +3,17 @@ | |||||||
|   <head> |   <head> | ||||||
|     <meta charset="UTF-8"> |     <meta charset="UTF-8"> | ||||||
|     <title>jon</title> |     <title>jon</title> | ||||||
|     <style> |     <link rel="stylesheet" href="/static/jon.css"> | ||||||
|       html { |  | ||||||
|         font-family: Helvetica, sans-serif; |  | ||||||
|       } |  | ||||||
|       h1 { |  | ||||||
|         margin: 0; |  | ||||||
|       } |  | ||||||
|       nav > ul { |  | ||||||
|         padding-left: 0; |  | ||||||
|       } |  | ||||||
|       nav > ul > li { |  | ||||||
|         display: inline-block; |  | ||||||
|         list-style: none; |  | ||||||
|       } |  | ||||||
|       nav > ul > li + li:before { |  | ||||||
|         content: ' · '; |  | ||||||
|       } |  | ||||||
|       .current-page > a { |  | ||||||
|         position: relative; |  | ||||||
|       } |  | ||||||
|       .current-page > a:after { |  | ||||||
|         content: '↓'; |  | ||||||
|         font-size: 0.8em; |  | ||||||
|         box-sizing: border-box; |  | ||||||
|         position: absolute; |  | ||||||
|         display: block; |  | ||||||
|         right: 50%; |  | ||||||
|         top: -1em; |  | ||||||
|         width: 1em; |  | ||||||
|         text-align: center; |  | ||||||
|         margin-right: -0.5em; |  | ||||||
|         animation: wiggle 0.8s ease-in-out 0s infinite; |  | ||||||
|         /* animation-direction: alternate; */ |  | ||||||
|       } |  | ||||||
|       .--align-left { |  | ||||||
|         text-align: left; |  | ||||||
|       } |  | ||||||
|       .--align-right { |  | ||||||
|         text-align: right; |  | ||||||
|       } |  | ||||||
|       .--centered { |  | ||||||
|         text-align: center; |  | ||||||
|       } |  | ||||||
|       @keyframes wiggle { |  | ||||||
|         0%, 100% { margin-top: 0; } |  | ||||||
|         50% { margin-top: -0.5em; } |  | ||||||
|         /* 100% { transform: rotate(1turn); } */ |  | ||||||
|       } |  | ||||||
|       table { |  | ||||||
|         border-spacing: .5em 0; |  | ||||||
|       } |  | ||||||
|       th { |  | ||||||
|         font-size: .8em; |  | ||||||
|       } |  | ||||||
|       @media print { |  | ||||||
|         body { |  | ||||||
|           font-size: 8px; |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|       .form-input > label { |  | ||||||
|         font-size: .8em; |  | ||||||
|       } |  | ||||||
|       .form-input > input:not([type=radio]), |  | ||||||
|       .form-input > select { |  | ||||||
|         display: block; |  | ||||||
|       } |  | ||||||
|     </style> |  | ||||||
|   </head> |   </head> | ||||||
|   <body> |   <body> | ||||||
|     <header> |     <header> | ||||||
|       <h1>jon</h1> |       <h1>jon</h1> | ||||||
|       <nav> |       <nav> | ||||||
|         <ul> |         <ul> | ||||||
|           <li {{ "class=current-page" if request.path == "/" else "" }}><a href="/">Home</a></li> |           <li{{ " class=current-page" if request.path == "/" else "" }}><a href="/">Home</a></li> | ||||||
|           <li {{ "class=current-page" if request.path.startswith("/inventory") else "" }}><a href="/inventory">Inventar</a></li> |           <li{{ " class=current-page" if request.path.startswith("/inventory") else "" }}><a href="/inventory">Inventar</a></li> | ||||||
|           <li {{ "class=current-page" if request.path.startswith("/entry") else "" }}><a href="/entry">Eintragen</a></li> |           <li{{ " class=current-page" if request.path.startswith("/entry") else "" }}><a href="/entry">Eintragen</a></li> | ||||||
|           <li {{ "class=current-page" if request.path.startswith("/location") else "" }}> |           <li{{ " class=current-page" if request.path.startswith("/location") else "" }}> | ||||||
|             <a href="/location"> |             <a href="/location"> | ||||||
|             {% if "location" not in session %} |             {% if "location" not in session %} | ||||||
|               Raum wählen |               Raum wählen | ||||||
| @ -88,6 +22,7 @@ | |||||||
|             {% endif %} |             {% endif %} | ||||||
|             </a> |             </a> | ||||||
|           </li> |           </li> | ||||||
|  |           <li{{ " class=current-page" if request.path.startswith("/auth/logout") else "" }}><a href="/auth/logout">Logout</a></li> | ||||||
|         </ul> |         </ul> | ||||||
|       </nav> |       </nav> | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| {% extends "base.html" %} | {% extends "base.html" %} | ||||||
| 
 | 
 | ||||||
| {% block content %} | {% block content %} | ||||||
| <form method="POST" action="."> | <form method="POST"> | ||||||
|   <select name="location_id"> |   <select name="location_id"> | ||||||
|     <option value="" {{ "selected" if "location" not in session else ""}}>-</option> |     <option value="" {{ "selected" if "location" not in session else ""}}>-</option> | ||||||
|   {% for location in locations %} |   {% for location in locations %} | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user