|
|
|
|
@@ -8,7 +8,7 @@ from flask_wtf import FlaskForm
|
|
|
|
|
from oidc import login_required, read_user
|
|
|
|
|
from pymongo import MongoClient
|
|
|
|
|
from wtforms import BooleanField, IntegerField, SelectField, StringField, validators
|
|
|
|
|
from wtforms.validators import DataRequired
|
|
|
|
|
from wtforms.validators import DataRequired, InputRequired
|
|
|
|
|
|
|
|
|
|
page_doorboy = Blueprint("doorboy", __name__)
|
|
|
|
|
db = MongoClient(const.MONGO_URI).get_default_database()
|
|
|
|
|
@@ -82,9 +82,19 @@ def save_doorboy_edit(token_id):
|
|
|
|
|
})
|
|
|
|
|
return redirect("/m/doorboy/me")
|
|
|
|
|
|
|
|
|
|
def hold_duration_check(form, field):
|
|
|
|
|
# 0 cancels/closes an active hold immediately; any real hold must last at
|
|
|
|
|
# least the documented 5-second minimum (and at most 6 hours). Reject 1-4
|
|
|
|
|
# so a typo cannot arm an unusably short hold. field.data is None when the
|
|
|
|
|
# submitted value was not a valid integer; leave that to the other validators.
|
|
|
|
|
if field.data is None:
|
|
|
|
|
return
|
|
|
|
|
if field.data != 0 and not (5 <= field.data <= 21600):
|
|
|
|
|
raise validators.ValidationError("Duration must be 0 (to close now) or between 5 and 21600 seconds")
|
|
|
|
|
|
|
|
|
|
class HoldDoorForm(FlaskForm):
|
|
|
|
|
door_name = SelectField("Door name", choices=[(j,j) for j in ["grounddoor", "frontdoor", "backdoor"]], validators=[DataRequired()])
|
|
|
|
|
duration = IntegerField('Duration in seconds', validators=[DataRequired(), validators.NumberRange(min=5, max=21600)])
|
|
|
|
|
door_name = SelectField("Door name", choices=[(j,j) for j in ["grounddoor", "frontdoor", "backdoor", "workshopdoor"]], validators=[DataRequired()])
|
|
|
|
|
duration = IntegerField('Duration in seconds', validators=[InputRequired(), hold_duration_check])
|
|
|
|
|
|
|
|
|
|
# duration=0 to override and close right away
|
|
|
|
|
@page_doorboy.route("/m/doorboy/hold", methods=["POST"])
|
|
|
|
|
@@ -92,16 +102,32 @@ class HoldDoorForm(FlaskForm):
|
|
|
|
|
def view_doorboy_hold():
|
|
|
|
|
user = read_user()
|
|
|
|
|
form = HoldDoorForm(request.form)
|
|
|
|
|
if form.validate_on_submit():
|
|
|
|
|
db.doorlog.insert_one({
|
|
|
|
|
"method": "hold",
|
|
|
|
|
"timestamp": datetime.utcnow(),
|
|
|
|
|
"door": form.door_name.data,
|
|
|
|
|
"approved": True,
|
|
|
|
|
"user": user["username"],
|
|
|
|
|
"expires": datetime.utcnow() + timedelta(seconds=form.duration.data)
|
|
|
|
|
})
|
|
|
|
|
return redirect("/m/doorboy")
|
|
|
|
|
if not form.validate_on_submit():
|
|
|
|
|
# Validation or CSRF failure: report it instead of redirecting as if the
|
|
|
|
|
# hold had been accepted, so the user knows the door will not be held.
|
|
|
|
|
errors = "; ".join(m for messages in form.errors.values() for m in messages)
|
|
|
|
|
return "Invalid hold request: %s" % (errors or "bad or expired form"), 400
|
|
|
|
|
|
|
|
|
|
door = form.door_name.data
|
|
|
|
|
if door == "workshopdoor":
|
|
|
|
|
access_group = "k-space:workshop"
|
|
|
|
|
else:
|
|
|
|
|
access_group = "k-space:floor"
|
|
|
|
|
approved = access_group in g.users_lookup.get(user["username"], User()).groups
|
|
|
|
|
db.doorlog.insert_one({
|
|
|
|
|
"method": "hold",
|
|
|
|
|
"timestamp": datetime.utcnow(),
|
|
|
|
|
"door": door,
|
|
|
|
|
"approved": approved,
|
|
|
|
|
"user": user["username"],
|
|
|
|
|
"expires": datetime.utcnow() + timedelta(seconds=form.duration.data)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if approved:
|
|
|
|
|
return redirect("/m/doorboy")
|
|
|
|
|
# Logged for audit, but the proxy only honors approved holds, so tell the
|
|
|
|
|
# user the door will not actually be held rather than redirecting silently.
|
|
|
|
|
return "You are not in the access group required to hold this door", 403
|
|
|
|
|
|
|
|
|
|
# Writes open event to log, which is picked up by doorboy-proxy.
|
|
|
|
|
@page_doorboy.route("/m/doorboy/<door>/open")
|
|
|
|
|
@@ -135,6 +161,7 @@ def view_doorboy():
|
|
|
|
|
user = read_user()
|
|
|
|
|
workshop_access = "k-space:workshop" in g.users_lookup.get(user["username"], User()).groups
|
|
|
|
|
|
|
|
|
|
hold_form = HoldDoorForm()
|
|
|
|
|
latest_swipes = db.inventory.find({"component": "doorboy", "type":"token"}).sort([("last_seen", -1)]).limit(10)
|
|
|
|
|
return render_template("doorboy.html", **locals())
|
|
|
|
|
|
|
|
|
|
|