Skip to content

Commit

Permalink
Merge branch 'feature/privacy-first'
Browse files Browse the repository at this point in the history
  • Loading branch information
AzorianMatt committed Jan 24, 2023
2 parents 0c42bda + 3a8ad7c commit 948973a
Show file tree
Hide file tree
Showing 16 changed files with 101 additions and 85 deletions.
1 change: 0 additions & 1 deletion configs/development.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
SECRET_KEY = 'e951e5a1f4b94151b360f47edf596dd2'
BIND_ADDRESS = '0.0.0.0'
PORT = 9191
OFFLINE_MODE = False

### DATABASE CONFIG
SQLA_DB_USER = 'pda'
Expand Down
2 changes: 0 additions & 2 deletions configs/docker_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@
'SAML_LOGOUT',
'SAML_LOGOUT_URL',
'SAML_ASSERTION_ENCRYPTED',
'OFFLINE_MODE',
'REMOTE_USER_LOGOUT_URL',
'REMOTE_USER_COOKIES',
'SIGNUP_ENABLED',
Expand All @@ -77,7 +76,6 @@
'SAML_WANT_MESSAGE_SIGNED',
'SAML_LOGOUT',
'SAML_ASSERTION_ENCRYPTED',
'OFFLINE_MODE',
'REMOTE_USER_ENABLED',
'SIGNUP_ENABLED',
'LOCAL_DB_ENABLED',
Expand Down
1 change: 0 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,3 @@ services:
- GUNICORN_TIMEOUT=60
- GUNICORN_WORKERS=2
- GUNICORN_LOGLEVEL=DEBUG
- OFFLINE_MODE=False # True for offline, False for external resources
7 changes: 0 additions & 7 deletions powerdnsadmin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,6 @@ def create_app(config=None):
app.jinja_env.filters['display_record_name'] = utils.display_record_name
app.jinja_env.filters['display_master_name'] = utils.display_master_name
app.jinja_env.filters['display_second_to_time'] = utils.display_time
app.jinja_env.filters[
'email_to_gravatar_url'] = utils.email_to_gravatar_url
app.jinja_env.filters[
'display_setting_state'] = utils.display_setting_state
app.jinja_env.filters['pretty_domain_name'] = utils.pretty_domain_name
Expand All @@ -93,9 +91,4 @@ def inject_setting():
setting = Setting()
return dict(SETTING=setting)

@app.context_processor
def inject_mode():
setting = app.config.get('OFFLINE_MODE', False)
return dict(OFFLINE_MODE=setting)

return app
1 change: 0 additions & 1 deletion powerdnsadmin/default_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
BIND_ADDRESS = '0.0.0.0'
PORT = 9191
HSTS_ENABLED = False
OFFLINE_MODE = False
FILESYSTEM_SESSIONS_ENABLED = False
SESSION_COOKIE_SAMESITE = 'Lax'
CSRF_COOKIE_HTTPONLY = True
Expand Down
13 changes: 0 additions & 13 deletions powerdnsadmin/lib/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,12 @@
import re
import json
import requests
import hashlib
import ipaddress
import idna

from collections.abc import Iterable
from distutils.version import StrictVersion
from urllib.parse import urlparse
from datetime import datetime, timedelta


def auth_from_url(url):
Expand Down Expand Up @@ -186,17 +184,6 @@ def pdns_api_extended_uri(version):
return ""


def email_to_gravatar_url(email="", size=100):
"""
AD doesn't necessarily have email
"""
if email is None:
email = ""

hash_string = hashlib.md5(email.encode('utf-8')).hexdigest()
return "https://s.gravatar.com/avatar/{0}?s={1}".format(hash_string, size)


def display_setting_state(value):
if value == 1:
return "ON"
Expand Down
3 changes: 2 additions & 1 deletion powerdnsadmin/models/setting.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,8 @@ class Setting(db.Model):
'otp_force': False,
'max_history_records': 1000,
'deny_domain_override': False,
'account_name_extra_chars': False
'account_name_extra_chars': False,
'gravatar_enabled': False,
}

def __init__(self, id=None, name=None, value=None):
Expand Down
49 changes: 35 additions & 14 deletions powerdnsadmin/routes/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -1259,20 +1259,41 @@ def history_table(): # ajax call data
@login_required
@operator_role_required
def setting_basic():
if request.method == 'GET':
settings = [
'maintenance', 'fullscreen_layout', 'record_helper',
'login_ldap_first', 'default_record_table_size',
'default_domain_table_size', 'auto_ptr', 'record_quick_edit',
'pretty_ipv6_ptr', 'dnssec_admins_only',
'allow_user_create_domain', 'allow_user_remove_domain', 'allow_user_view_history', 'bg_domain_updates', 'site_name',
'session_timeout', 'warn_session_timeout', 'ttl_options',
'pdns_api_timeout', 'verify_ssl_connections', 'verify_user_email',
'delete_sso_accounts', 'otp_field_enabled', 'custom_css', 'enable_api_rr_history', 'max_history_records', 'otp_force',
'deny_domain_override', 'enforce_api_ttl', 'account_name_extra_chars'
]

return render_template('admin_setting_basic.html', settings=settings)
settings = [
'account_name_extra_chars',
'allow_user_create_domain',
'allow_user_remove_domain',
'allow_user_view_history',
'auto_ptr',
'bg_domain_updates',
'custom_css',
'default_domain_table_size',
'default_record_table_size',
'delete_sso_accounts',
'deny_domain_override',
'dnssec_admins_only',
'enable_api_rr_history',
'enforce_api_ttl',
'fullscreen_layout',
'gravatar_enabled',
'login_ldap_first',
'maintenance',
'max_history_records',
'otp_field_enabled',
'otp_force',
'pdns_api_timeout',
'pretty_ipv6_ptr',
'record_helper',
'record_quick_edit',
'session_timeout',
'site_name',
'ttl_options',
'verify_ssl_connections',
'verify_user_email',
'warn_session_timeout',
]

return render_template('admin_setting_basic.html', settings=settings)


@admin_bp.route('/setting/basic/<path:setting>/edit', methods=['POST'])
Expand Down
60 changes: 58 additions & 2 deletions powerdnsadmin/routes/user.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import datetime
from flask import Blueprint, request, render_template, make_response, jsonify, redirect, url_for, g, session, current_app
import hashlib
import imghdr
import mimetypes

from flask import Blueprint, request, render_template, make_response, jsonify, redirect, url_for, g, session, \
current_app, after_this_request, abort
from flask_login import current_user, login_required, login_manager

from ..models.user import User, Anonymous
Expand Down Expand Up @@ -96,4 +101,55 @@ def qrcode():
'Cache-Control': 'no-cache, no-store, must-revalidate',
'Pragma': 'no-cache',
'Expires': '0'
}
}


@user_bp.route('/image', methods=['GET'])
@login_required
def image():
"""Returns the user profile image or avatar."""

@after_this_request
def add_cache_headers(response_):
"""When the response is ok, add cache headers."""
if 200 <= response_.status_code <= 399:
response_.cache_control.private = True
response_.cache_control.max_age = int(datetime.timedelta(days=1).total_seconds())
return response_

def return_image(content, content_type=None):
"""Return the given binary image content. Guess the type if not given."""
if not content_type:
guess = mimetypes.guess_type('example.' + imghdr.what(None, h=content))
if guess and guess[0]:
content_type = guess[0]

return content, 200, {'Content-Type': content_type}

# To prevent "cache poisoning", the username query parameter is required
if request.args.get('username', None) != current_user.username:
abort(400)

setting = Setting()

if session['authentication_type'] == 'LDAP':
search_filter = '(&({0}={1}){2})'.format(setting.get('ldap_filter_username'),
current_user.username,
setting.get('ldap_filter_basic'))
result = User().ldap_search(search_filter, setting.get('ldap_base_dn'))
if result and result[0] and result[0][0] and result[0][0][1]:
user_obj = result[0][0][1]
for key in ['jpegPhoto', 'thumbnailPhoto']:
if key in user_obj and user_obj[key] and user_obj[key][0]:
current_app.logger.debug(f'Return {key} from ldap as user image')
return return_image(user_obj[key][0])

email = current_user.email
if email and setting.get('gravatar_enabled'):
hash_ = hashlib.md5(email.encode('utf-8')).hexdigest()
url = f'https://s.gravatar.com/avatar/{hash_}?s=100'
current_app.logger.debug('Redirect user image request to gravatar')
return redirect(url, 307)

# Fallback to the local default image
return current_app.send_static_file('img/user_image.png')
9 changes: 0 additions & 9 deletions powerdnsadmin/static/custom/css/custom.css
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,6 @@ table td {
background-position: center;
}

.navbar-nav>.user-menu>.dropdown-menu>li.user-header>img.img-circle.offline {
filter: brightness(0);
border-color: black;
}

.navbar-nav>.user-menu .user-image.offline {
filter: brightness(0);
}

.search-input {
width: 100%;
}
Binary file removed powerdnsadmin/static/img/gravatar.png
Binary file not shown.
Binary file added powerdnsadmin/static/img/user_image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions powerdnsadmin/templates/admin_pdns_stats.html
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ <h3 class="box-title">PDNS Statistics</h3>
<tbody>
{% for statistic in statistics %}
<tr class="odd gradeX">
<td><a href="https://google.com/search?q=site:doc.powerdns.com+{{ statistic['name'] }}"
<td><a href="https://doc.powerdns.com/authoritative/search.html?q={{ statistic['name'] }}"
target="_blank" class="btn btn-flat btn-xs blue"><i
class="fa fa-search"></i></a></td>
<td>{{ statistic['name'] }}</td>
Expand Down Expand Up @@ -70,7 +70,7 @@ <h3 class="box-title">PDNS Configuration</h3>
<tbody>
{% for config in configs %}
<tr class="odd gradeX">
<td><a href="https://google.com/search?q=site:doc.powerdns.com+{{ config['name'] }}"
<td><a href="https://doc.powerdns.com/authoritative/search.html?q={{ config['name'] }}"
target="_blank" class="btn btn-flat btn-xs blue"><i
class="fa fa-search"></i></a></td>
<td>{{ config['name'] }}</td>
Expand Down
23 changes: 4 additions & 19 deletions powerdnsadmin/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,8 @@
{% block title %}<title>{{ SITE_NAME }}</title>{% endblock %}
<link rel="stylesheet" href="{{ url_for('static', filename='assets/css/style.css') }}">
<!-- Get Google Fonts we like -->
{% if OFFLINE_MODE %}
<link rel="stylesheet" href="{{ url_for('static', filename='assets/css/source_sans_pro.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='assets/css/roboto_mono.css') }}">
{% else %}
<link rel="stylesheet" href="//fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,300italic,400italic,600italic">
<link rel="stylesheet" href="//fonts.googleapis.com/css?family=Roboto+Mono:400,300,700">
{% endif %}
<!-- Tell the browser to be responsive to screen width -->
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
<!-- Tell Safari to not recognise telephone numbers -->
Expand All @@ -25,20 +20,10 @@
{% if SETTING.get('custom_css') %}
<link rel="stylesheet" href="/static/custom/{{ SETTING.get('custom_css') }}">
{% endif %}
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
{% endblock %}
</head>
<body class="hold-transition skin-blue sidebar-mini {% if not SETTING.get('fullscreen_layout') %}layout-boxed{% endif %}">
{% if OFFLINE_MODE %}
{% set gravatar_url = url_for('static', filename='img/gravatar.png') %}
{% elif current_user.email is defined %}
{% set gravatar_url = current_user.email|email_to_gravatar_url(size=80) %}
{% endif %}
{% set user_image_url = url_for('user.image', username=current_user.username) %}
<div class="wrapper">
{% block pageheader %}
<header class="main-header">
Expand All @@ -62,14 +47,14 @@
<!-- User Account: style can be found in dropdown.less -->
<li class="dropdown user user-menu">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
<img src="{{ gravatar_url }}" class="user-image {{ 'offline' if OFFLINE_MODE }}" alt="User Image"/>
<img src="{{ user_image_url }}" class="user-image" alt="User Image"/>
<span class="hidden-xs">
{{ current_user.firstname }}
</span>
</a>
<ul class="dropdown-menu">
<li class="user-header">
<img src="{{ gravatar_url }}" class="img-circle {{ 'offline' if OFFLINE_MODE }}" alt="User Image"/>
<img src="{{ user_image_url }}" class="img-circle" alt="User Image"/>
<p>
{{ current_user.firstname }} {{ current_user.lastname }}
<small>{{ current_user.role.name }}</small>
Expand Down Expand Up @@ -100,7 +85,7 @@
{% if current_user.id is defined %}
<div class="user-panel">
<div class="pull-left image">
<img src="{{ gravatar_url }}" class="img-circle" alt="User Image"/>
<img src="{{ user_image_url }}" class="img-circle" alt="User Image"/>
</div>
<div class="pull-left info">
<p>{{ current_user.firstname }} {{ current_user.lastname }}</p>
Expand Down
6 changes: 0 additions & 6 deletions powerdnsadmin/templates/login.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,6 @@
{% if SETTING.get('custom_css') %}
<link rel="stylesheet" href="/static/custom/{{ SETTING.get('custom_css') }}">
{% endif %}
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
</head>

<body class="hold-transition login-page">
Expand Down
7 changes: 0 additions & 7 deletions powerdnsadmin/templates/register.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,6 @@
{% assets "css_login" -%}
<link rel="stylesheet" href="{{ ASSET_URL }}">
{%- endassets %}

<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
</head>

<body class="hold-transition register-page">
Expand Down

0 comments on commit 948973a

Please sign in to comment.