Compare commits
	
		
			5 Commits
		
	
	
		
			74618bdaf7
			...
			e2fd0533ce
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| e2fd0533ce | |||
| 476a7ebd47 | |||
| 1492451065 | |||
|   | faa6d0cf07 | ||
|   | e51bcee41d | 
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -2,3 +2,5 @@ | ||||
| __pycache__ | ||||
| *.swp | ||||
| jon/config.json | ||||
| venv/ | ||||
| .venv/ | ||||
|  | ||||
| @ -1,9 +1,11 @@ | ||||
| import inspect | ||||
| import json | ||||
| import sys | ||||
| 
 | ||||
| from flask import Flask, render_template | ||||
| 
 | ||||
| from . import ( | ||||
|     auth, | ||||
|     db, | ||||
|     entry, | ||||
|     inventory, | ||||
| @ -21,6 +23,12 @@ def create_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 | ||||
|     def utility_processor(): | ||||
|         return dict(inspect.getmembers(template_utils, inspect.isfunction)) | ||||
| @ -28,8 +36,12 @@ def create_app(): | ||||
|     app.register_blueprint(location.bp) | ||||
|     app.register_blueprint(inventory.bp) | ||||
|     app.register_blueprint(entry.bp) | ||||
|     app.register_blueprint(auth.bp) | ||||
| 
 | ||||
|     @app.route("/") | ||||
|     def index(): | ||||
|         return render_template("index.html") | ||||
| 
 | ||||
|     print(f"Jon started. Token: {auth.ACCESS_TOKEN}", file=sys.stderr) | ||||
| 
 | ||||
|     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 result 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> | ||||
|     <meta charset="UTF-8"> | ||||
|     <title>jon</title> | ||||
|     <style> | ||||
|       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> | ||||
|     <link rel="stylesheet" href="/static/jon.css"> | ||||
|   </head> | ||||
|   <body> | ||||
|     <header> | ||||
|       <h1>jon</h1> | ||||
|       <nav> | ||||
|         <ul> | ||||
|           <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("/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 == "/" 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("/entry") else "" }}><a href="/entry">Eintragen</a></li> | ||||
|           <li{{ " class=current-page" if request.path.startswith("/location") else "" }}> | ||||
|             <a href="/location"> | ||||
|             {% if "location" not in session %} | ||||
|               Raum wählen | ||||
| @ -88,6 +22,7 @@ | ||||
|             {% endif %} | ||||
|             </a> | ||||
|           </li> | ||||
|           <li{{ " class=current-page" if request.path.startswith("/auth/logout") else "" }}><a href="/auth/logout">Logout</a></li> | ||||
|         </ul> | ||||
|       </nav> | ||||
| 
 | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| {% extends "base.html" %} | ||||
| 
 | ||||
| {% block content %} | ||||
| <form method="POST" action="."> | ||||
| <form method="POST"> | ||||
|   <select name="location_id"> | ||||
|     <option value="" {{ "selected" if "location" not in session else ""}}>-</option> | ||||
|   {% for location in locations %} | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user