Commit 76cd4c23 authored by Robin Sonnabend's avatar Robin Sonnabend
Browse files

Implemented ip network restriction

parent 2b441f0d
"""empty message
Revision ID: 0068d7a0fac0
Revises: 4bdc217932c3
Create Date: 2017-03-04 02:27:14.915941
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '0068d7a0fac0'
down_revision = '4bdc217932c3'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('protocoltypes', sa.Column('allowed_networks', sa.String(), nullable=True))
op.add_column('protocoltypes', sa.Column('restrict_networks', sa.Boolean(), nullable=True))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('protocoltypes', 'restrict_networks')
op.drop_column('protocoltypes', 'allowed_networks')
# ### end Alembic commands ###
......@@ -6,7 +6,7 @@ from io import StringIO, BytesIO
from enum import Enum
from shared import db, date_filter, date_filter_short, escape_tex, DATE_KEY, START_TIME_KEY, END_TIME_KEY
from utils import random_string, url_manager, get_etherpad_url, split_terms
from utils import random_string, url_manager, get_etherpad_url, split_terms, check_ip_in_networks
from models.errors import DateNotMatchingException
import os
......@@ -36,6 +36,8 @@ class ProtocolType(db.Model):
wiki_only_public = db.Column(db.Boolean)
printer = db.Column(db.String)
calendar = db.Column(db.String)
restrict_networks = db.Column(db.Boolean)
allowed_networks = db.Column(db.String)
protocols = relationship("Protocol", backref=backref("protocoltype"), cascade="all, delete-orphan", order_by="Protocol.id")
default_tops = relationship("DefaultTOP", backref=backref("protocoltype"), cascade="all, delete-orphan", order_by="DefaultTOP.number")
......@@ -45,8 +47,9 @@ class ProtocolType(db.Model):
def __init__(self, name, short_name, organization, usual_time,
is_public, modify_group, private_group, public_group,
private_mail, public_mail,
use_wiki, wiki_category, wiki_only_public, printer, calendar):
private_mail, public_mail, use_wiki, wiki_category,
wiki_only_public, printer, calendar,
restrict_networks, allowed_networks):
self.name = name
self.short_name = short_name
self.organization = organization
......@@ -62,18 +65,22 @@ class ProtocolType(db.Model):
self.wiki_only_public = wiki_only_public
self.printer = printer
self.calendar = calendar
self.restrict_networks = restrict_networks
self.allowed_networks = allowed_networks
def __repr__(self):
return ("<ProtocolType(id={}, short_name={}, name={}, "
"organization={}, is_public={}, modify_group={}, "
"private_group={}, public_group={}, use_wiki={}, "
"wiki_category='{}', wiki_only_public={}, printer={}, "
"usual_time={}, calendar='{}')>".format(
"usual_time={}, calendar='{}', restrict_networks={}, "
"allowed_networks='{}')>".format(
self.id, self.short_name, self.name,
self.organization, self.is_public, self.modify_group,
self.private_group, self.public_group, self.use_wiki,
self.wiki_category, self.wiki_only_public, self.printer,
self.usual_time, self.calendar))
self.usual_time, self.calendar, self.restrict_networks,
self.allowed_networks))
def get_latest_protocol(self):
candidates = sorted([protocol for protocol in self.protocols if protocol.is_done()], key=lambda p: p.date, reverse=True)
......@@ -81,12 +88,18 @@ class ProtocolType(db.Model):
return None
return candidates[0]
@hybrid_method
def has_public_view_right(self, user):
return (self.has_public_anonymous_view_right()
or (user is not None and self.has_public_authenticated_view_right(user)))
def has_public_anonymous_view_right(self):
return (self.is_public
or (user is not None and
((self.public_group != "" and self.public_group in user.groups)
or (self.private_group != "" and self.private_group in user.groups))))
and (not self.restrict_networks
or check_ip_in_networks(self.allowed_networks)))
def has_public_authenticated_view_right(self, user):
return ((self.public_group != "" and self.public_group in user.groups)
or (self.private_group != "" and self.private_group in user.groups))
def has_private_view_right(self, user):
return (user is not None and self.private_group != "" and self.private_group in user.groups)
......
......@@ -29,7 +29,6 @@ MarkupSafe==0.23
nose==1.3.7
packaging==16.8
pathtools==0.1.2
pkg-resources==0.0.0
psycopg2==2.6.2
Pygments==2.2.0
pyldap==2.4.28
......
......@@ -9,9 +9,9 @@ from flask_migrate import Migrate, MigrateCommand
#from flask_socketio import SocketIO
from celery import Celery
from sqlalchemy import or_, and_
#from apscheduler.schedulers.background import BackgroundScheduler
#from apscheduler.triggers.cron import CronTrigger
#from apscheduler.triggers.interval import IntervalTrigger
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.triggers.cron import CronTrigger
from apscheduler.triggers.interval import IntervalTrigger
import atexit
from io import StringIO, BytesIO
import os
......@@ -45,17 +45,6 @@ celery = make_celery(app, config)
# return socketio
#socketio = make_socketio(app, config)
#def make_scheduler(app, config, function):
# scheduler = BackgroundScheduler()
# scheduler.start()
# scheduler.add_job(
# func=function,
# trigger=CronTrigger(hour='*', minute=23),
# id="scheduler",
# name="Do an action regularly",
# replace_existing=True)
# atexit.register(scheduler.shutdown)
app.jinja_env.trim_blocks = True
app.jinja_env.lstrip_blocks = True
app.jinja_env.filters["datify"] = date_filter
......@@ -1227,11 +1216,25 @@ def logout():
return redirect(url_for(".index"))
def make_scheduler():
from uwsgidecorators import timer as uwsgitimer, signal as uwsgisignal, cron as uwsgicron
@uwsgicron(30, -1, -1, -1, -1, target="mule")
def uwsgi_timer(signum):
if signum == 0:
check_and_send_reminders()
try:
from uwsgidecorators import timer as uwsgitimer, signal as uwsgisignal, cron as uwsgicron
print("using uwsgi for cron-like tasks")
@uwsgicron(30, -1, -1, -1, -1, target="mule")
def uwsgi_timer(signum):
if signum == 0:
check_and_send_reminders()
except ModuleNotFoundError:
print("uwsgi not found, falling back to apscheduler for cron-like tasks")
def make_scheduler(app, config, function):
scheduler = BackgroundScheduler()
scheduler.start()
scheduler.add_job(
func=function,
trigger=CronTrigger(hour='*', minute=30),
id="scheduler",
name="Do an action regularly",
replace_existing=True)
atexit.register(scheduler.shutdown)
def check_and_send_reminders():
print("check and send reminders")
......
......@@ -11,6 +11,7 @@ from email.mime.application import MIMEApplication
from datetime import datetime, date, timedelta
import requests
from io import BytesIO
import ipaddress
import config
......@@ -170,3 +171,17 @@ def add_line_numbers(text):
line
))
return "\n".join(lines)
def check_ip_in_networks(networks_string):
address = ipaddress.ip_address(request.remote_addr)
if address == ipaddress.ip_address("127.0.0.1") and "X-Real-Ip" in request.headers:
address = ipaddress.ip_address(request.headers["X-Real-Ip"])
print(address)
try:
for network_string in networks_string.split(","):
network = ipaddress.ip_network(network_string.strip())
if address in network:
return True
return False
except ValueError:
return False
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, DateField, HiddenField, IntegerField, SelectField, FileField, DateTimeField, TextAreaField
from wtforms import StringField, PasswordField, BooleanField, DateField, HiddenField, IntegerField, SelectField, FileField, DateTimeField, TextAreaField, Field, widgets
from wtforms.validators import InputRequired, Optional
import ipaddress
from models.database import TodoState
from validators import CheckTodoDateByState
from calendarpush import Client as CalendarClient
......@@ -59,6 +61,36 @@ def coerce_todostate(key):
key = TodoState[key_part]
return key
class IPNetworkField(Field):
widget = widgets.TextInput()
def __init__(self, label=None, validators=None, **kwargs):
super().__init__(label, validators, **kwargs)
def _value(self):
if self.raw_data:
print("raw_data in _value", self.raw_data)
return " ".join(self.raw_data)
else:
return self.data and str(self.data) or ""
def process_formdata(self, valuelist):
print("valuelist in process_formdata", valuelist)
if valuelist:
data_str = valuelist[0]
result_parts = []
try:
for part in data_str.split(","):
part = part.strip()
if len(part) > 0:
network = ipaddress.ip_network(part)
result_parts.append(network)
except ValueError as exc:
print(exc)
self.data = None
raise ValueError(self.gettext("Not a valid IP Network: {}".format(str(exc))))
self.data = ",".join(map(str, result_parts))
class LoginForm(FlaskForm):
username = StringField("Benutzer", validators=[InputRequired("Bitte gib deinen Benutzernamen ein.")])
password = PasswordField("Passwort", validators=[InputRequired("Bitte gib dein Passwort ein.")])
......@@ -79,6 +111,8 @@ class ProtocolTypeForm(FlaskForm):
wiki_only_public = BooleanField("Wiki ist öffentlich")
printer = SelectField("Drucker", choices=[])
calendar = SelectField("Kalender", choices=[])
restrict_networks = BooleanField("Netzwerke einschränken")
allowed_networks = IPNetworkField("Erlaubte Netzwerke")
def __init__(self, **kwargs):
super().__init__(**kwargs)
......
......@@ -125,9 +125,10 @@ class ProtocolTypeTable(SingleValueTable):
calendar_headers = ["Kalender"]
if not config.CALENDAR_ACTIVE:
calendar_headers = []
network_headers = ["Netzwerke einschränken", "Erlaubte Netzwerke"]
action_headers = ["Aktion"]
return (general_headers + mail_headers + printing_headers
+ wiki_headers + calendar_headers + action_headers)
+ wiki_headers + calendar_headers + network_headers + action_headers)
def row(self):
general_part = [
......@@ -159,8 +160,13 @@ class ProtocolTypeTable(SingleValueTable):
calendar_part = [self.value.calendar if self.value.calendar is not None else ""]
if not config.CALENDAR_ACTIVE:
calendar_part = []
network_part = [
Table.bool(self.value.restrict_networks),
self.value.allowed_networks
]
action_part = [Table.link(url_for("delete_type", type_id=self.value.id), "Löschen", confirm="Bist du dir sicher, dass du den Protokolltype {} löschen möchtest?".format(self.value.name))]
return general_part + mail_part + printing_part + wiki_part + calendar_part + action_part
return (general_part + mail_part + printing_part + wiki_part +
calendar_part + network_part + action_part)
class DefaultTOPsTable(Table):
def __init__(self, tops, protocoltype=None):
......
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