Commit 6c719d75 authored by Jan Čermák's avatar Jan Čermák

moved subtree filters to separate module, implemented authentication - refs #2799

parent aa9df8e0
......@@ -4,7 +4,7 @@ import bottle
from bottle_i18n import I18NMiddleware, I18NPlugin, i18n_defaults
import gettext
import logging
from nuci import client
from nuci import client, filters
import os
import sys
import wizard
......@@ -19,6 +19,11 @@ i18n_defaults(bottle.SimpleTemplate, bottle.request)
trans = gettext.translation("messages", os.path.join(BASE_DIR, "locale"), languages=["cs"])
gettext = trans.ugettext
# template defaults
# this is not really straight-forward, check for user_authenticated() (with brackets) in template,
# because bool(user_authenticated) is always True - it means bool(<function ...>)
bottle.SimpleTemplate.defaults["user_authenticated"] = lambda: bottle.request.environ["beaker.session"].get("user_authenticated")
@bottle.route("/")
@bottle.view("index")
......@@ -26,6 +31,25 @@ def index():
return dict()
@bottle.route("/", method="POST")
def login():
session = bottle.request.environ["beaker.session"]
if _check_password(bottle.request.POST.get("password")):
session["user_authenticated"] = True
session.save()
bottle.redirect("/wizard/")
bottle.redirect("/")
@bottle.route("/logout")
def logout():
session = bottle.request.environ["beaker.session"]
if "user_authenticated" in session:
del session["user_authenticated"]
session.save()
bottle.redirect("/")
@bottle.route('/static/<filename:re:.*>', name="static")
def static(filename):
if not bottle.DEBUG:
......@@ -33,6 +57,13 @@ def static(filename):
return bottle.static_file(filename, root=os.path.join(os.path.dirname(__file__), "static"))
def _check_password(password):
import pbkdf2
data = client.get(filter=filters.uci)
password_hash = data.find_child("uci.cznic.foris.password").value
# crypt automatically extracts salt and iterations from formatted pw hash
return password_hash == pbkdf2.crypt(password, salt=password_hash)
# ---------------------------------------------------------------------------- #
# MAIN #
# ---------------------------------------------------------------------------- #
......
"""
This module contains filters used for subtree filtering in nuci client. Filter is basically
an XML element that is passed to client.get() function and appropriate subtree is returned.
"""
from nuci.modules import time, uci_raw, updater
import xml.etree.cElementTree as ET
uci = ET.Element(uci_raw.Uci.qual_tag("uci"))
updater = ET.Element(updater.Updater.qual_tag("updater"))
time = ET.Element(time.Time.qual_tag("time"))
\ No newline at end of file
......@@ -2,6 +2,16 @@
<h1>Foris</h1>
<h2>roscesňýk</h2>
%if user_authenticated():
<a href="/logout">odhlásit</a>
%else:
<form action="" method="POST">
<label for="field-password">{{ _("Password") }}</label>
<input id="field-password" type="password" name="password">
<button class="button" type="submit">{{ _("Log in") }}</button>
</form>
%end
<ul>
<li><a href="uci/">about:config</a></li>
<li><a href="wizard/">kuzelňik</a></li>
......
......@@ -6,7 +6,7 @@ from ncclient.operations import RPCError
from nuci import client
from nuci.client import edit_uci_config
from nuci.modules import uci_raw
from utils import print_model
from utils import print_model, login_required
from validators import NotEmpty, RegExp
......@@ -73,6 +73,7 @@ app = Bottle()
@app.get("/")
@view("uci/index")
@login_required
def index():
uci_model = client.get_uci_config()
node_path = request.GET.get("node")
......@@ -82,6 +83,7 @@ def index():
@app.get("/<node:re:\w+(\.\w+)*>/edit", name="edit")
@view("uci/edit")
@login_required
def edit(node):
uci_model = client.get_uci_config()
node_model = uci_model.find_child(node)
......@@ -94,6 +96,7 @@ def edit(node):
@app.post("/<node:re:\w+(\.\w+)*>/edit", name="edit_post")
@view("uci/edit")
@login_required
def edit_post(node):
uci_model = client.get_uci_config()
node_model = uci_model.find_child(node)
......@@ -109,6 +112,7 @@ def edit_post(node):
@app.get("/<node:re:\w+(\.\w+)*>/create", name="create")
@view("uci/edit")
@login_required
def create(node):
operation = request.GET.get("operation")
uci_model = client.get_uci_config()
......@@ -135,6 +139,7 @@ def create(node):
@app.post("/<node:re:\w+(\.\w+)*>/create", name="create_post")
@view("uci/edit")
@login_required
def create_post(node):
operation = request.GET.get("operation")
uci_model = client.get_uci_config()
......@@ -172,6 +177,7 @@ def create_post(node):
@app.get("/<node:re:\w+(\.\w+)*>/remove", name="remove")
@login_required
def remove(node):
uci_model = client.get_uci_config()
node_model = uci_model.find_child(node)
......@@ -185,6 +191,7 @@ def remove(node):
@app.get("/<node:re:\w+(\.\w+)*>/debug", name="debug")
@login_required
def debug(node):
uci_model = client.get_uci_config()
node_model = uci_model.find_child(node)
......
import bottle
from functools import wraps
import logging
from xml.etree import cElementTree as ET
......@@ -5,6 +7,22 @@ from xml.etree import cElementTree as ET
logger = logging.getLogger("foris.utils")
def login_required(func=None, redirect_url="/"):
"""Decorator for views that require login.
:param redirect_url:
:return:
"""
@wraps(func)
def wrapper(*args, **kwargs):
session = bottle.request.environ['beaker.session']
if not session.get("user_authenticated", False):
# "raise" bottle redirect
bottle.redirect(redirect_url)
return func(*args, **kwargs)
return wrapper
class Lazy(object):
def __init__(self, func):
self.func = func
......
......@@ -4,20 +4,15 @@ import logging
from foris import gettext as _
from form import Password, Textbox, Dropdown, Checkbox, Hidden
import fapi
from nuci import client
from nuci.modules import time, uci_raw, updater
from nuci import client, filters
from nuci.modules.uci_raw import Uci, Config, Section, Option
from utils import login_required
from validators import LenRange
import validators
import xml.etree.cElementTree as ET
logger = logging.getLogger("wizard")
# filter later used for config filtering
uci_filter = ET.Element(uci_raw.Uci.qual_tag("uci"))
updater_filter = ET.Element(updater.Updater.qual_tag("updater"))
class BaseWizardStep(object):
template = "wizard/form"
......@@ -118,7 +113,7 @@ class WizardStep2(BaseWizardStep):
def get_form(self):
# WAN
wan_form = fapi.ForisForm("wan", self.data, filter=uci_filter)
wan_form = fapi.ForisForm("wan", self.data, filter=filters.uci)
wan_main = wan_form.add_section(name="set_wan", title="WAN")
WAN_DHCP = "dhcp"
......@@ -195,7 +190,7 @@ class WizardStep3(BaseWizardStep):
raise ValueError("Unknown Wizard action.")
def get_form(self):
time_form = fapi.ForisForm("time", self.data, filter=ET.Element(time.Time.qual_tag("time")))
time_form = fapi.ForisForm("time", self.data, filter=filters.time)
time_main = time_form.add_section(name="set_time", title="Time")
time_main.add_field(Textbox, name="time", label="Time", nuci_path="time",
......@@ -251,7 +246,7 @@ class WizardStep5(BaseWizardStep):
def get_form(self):
# WAN
lan_form = fapi.ForisForm("lan", self.data, filter=uci_filter)
lan_form = fapi.ForisForm("lan", self.data, filter=filters.uci)
lan_main = lan_form.add_section(name="set_lan", title="LAN")
lan_main.add_field(Checkbox, name="dhcp_enabled", label="Enable DHCP", nuci_path="uci.dhcp.lan.ignore",
......@@ -296,7 +291,7 @@ class WizardStep6(BaseWizardStep):
name = "wifi"
def get_form(self):
wifi_form = fapi.ForisForm("lan", self.data, filter=uci_filter)
wifi_form = fapi.ForisForm("lan", self.data, filter=filters.uci)
wifi_main = wifi_form.add_section(name="set_wifi", title="WiFi")
wifi_main.add_field(Hidden, name="iface_section", nuci_path="uci.wireless.@wifi-iface[1]", nuci_preproc=lambda val: val.name)
wifi_main.add_field(Checkbox, name="wifi_enabled", label="Enable WiFi", default=True,
......@@ -374,6 +369,7 @@ def get_wizard(number):
@app.route("/step/<number:re:\d+>/ajax")
@login_required
def ajax(number=1):
action = request.GET.get("action")
if not action:
......@@ -388,19 +384,21 @@ def ajax(number=1):
@app.route("/", name="wizard-step")
@login_required
def wizard():
bottle.redirect("/wizard/step/1")
@app.route("/step/<number:re:\d+>", name="wizard-step")
@login_required
def step(number=1):
Wizard = get_wizard(number)
wiz = Wizard()
return wiz.render(stepnumber=number)
@app.route("/", method="POST")
@app.route("/step/<number:re:\d+>", method="POST")
@login_required
def step_post(number=1):
Wizard = get_wizard(number)
wiz = Wizard(request.POST)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment