Skip to content

Commit

Permalink
Merge pull request #336 from loleg/main
Browse files Browse the repository at this point in the history
Authentication, notifications
  • Loading branch information
loleg committed Mar 16, 2023
2 parents 90e7c31 + 10482d4 commit 5003cd7
Show file tree
Hide file tree
Showing 39 changed files with 1,876 additions and 1,621 deletions.
64 changes: 33 additions & 31 deletions .github/workflows/codeql-analysis.yml
Expand Up @@ -14,9 +14,17 @@ name: "CodeQL"
on:
push:
branches: [ "main" ]
paths-ignore:
- '**/*.md'
- '**/*.txt'
- '**/*.html'
pull_request:
# The branches below must be a subset of the branches above
branches: [ "main" ]
paths-ignore:
- '**/*.md'
- '**/*.txt'
- '**/*.html'
schedule:
- cron: '43 4 * * 1'

Expand All @@ -37,38 +45,32 @@ jobs:
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support

steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Checkout repository
uses: actions/checkout@v3

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.

# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality

# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
# ℹ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun

- name: Autobuild
uses: github/codeql-action/autobuild@v2

# ℹ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
# - run: |
# echo "Run, Build Application using script"
# ./location_of_script_within_repo/buildscript.sh

# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.

# - run: |
# echo "Run, Build Application using script"
# ./location_of_script_within_repo/buildscript.sh

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
category: "/language:${{matrix.language}}"
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
category: "/language:${{matrix.language}}"
14 changes: 11 additions & 3 deletions .github/workflows/test.yml
Expand Up @@ -19,14 +19,22 @@ jobs:
os: ubuntu-latest
python-version: 3.9
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Setup Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Load cached Poetry installation
id: cached-poetry
uses: actions/cache@v3
with:
path: ~/.local # the path depends on the OS
key: poetry-0 # increment to reset cache
- name: Install Poetry
uses: snok/install-poetry@v1
if: steps.cached-poetry.outputs.cache-hit != 'true'
uses: snok/install-poetry@v1@v1
with:
version: 1.3.2
virtualenvs-create: true
virtualenvs-in-project: true
- name: Load cached venv
Expand Down
2 changes: 1 addition & 1 deletion dribdat/admin/forms.py
Expand Up @@ -190,7 +190,7 @@ class CategoryForm(FlaskForm):
description=u'Markdown and HTML supported')
logo_color = StringField(u'Custom color', [length(max=7)])
logo_icon = StringField(u'Custom icon', [length(max=20)],
description=u'fontawesome.com/v5/cheatsheet')
description=u'fontawesome.com/v4/cheatsheet')
event_id = SelectField(u'Specific to an event, or global if blank',
coerce=int)
submit = SubmitField(u'Save')
Expand Down
18 changes: 12 additions & 6 deletions dribdat/apifetch.py
Expand Up @@ -8,7 +8,7 @@
from pyquery import PyQuery as pq # noqa: N813
from base64 import b64decode
from flask_misaka import markdown
from bleach.sanitizer import ALLOWED_TAGS, ALLOWED_ATTRIBUTES
from bleach.sanitizer import ALLOWED_ATTRIBUTES
from urllib.parse import quote_plus
from .apievents import (
fetch_commits_github,
Expand Down Expand Up @@ -266,11 +266,14 @@ def parse_data_package(json):


# Basis: https://github.com/mozilla/bleach/blob/master/bleach/sanitizer.py#L16
ALLOWED_HTML_TAGS = list(ALLOWED_TAGS).extend([
ALLOWED_HTML_TAGS = [
'acronym', 'a', 'blockquote', 'li', 'abbr',
'strong', 'b', 'i', 'ul', 'ol', 'code', 'em',
'img', 'font', 'center', 'sub', 'sup', 'pre',
'table', 'tr', 'thead', 'tbody', 'td',
'h1', 'h2', 'h3', 'h4', 'h5',
'p', 'u', 'b', 'em', 'i',
])
'p', 'u'
]
ALLOWED_HTML_ATTR = ALLOWED_ATTRIBUTES
ALLOWED_HTML_ATTR['h1'] = ['id']
ALLOWED_HTML_ATTR['h2'] = ['id']
Expand Down Expand Up @@ -318,8 +321,11 @@ def FetchWebGoogleDoc(text, url):
content = doc("div#contents")
if len(content) < 1:
return {}
html_content = bleach.clean(content.html().strip(), strip=True,
tags=ALLOWED_HTML_TAGS,
content = content.html().strip()
if not content or len(content) < 1:
return {}
html_content = bleach.clean(content, strip=True,
tags=frozenset(ALLOWED_HTML_TAGS),
attributes=ALLOWED_HTML_ATTR)
obj = {}
# {
Expand Down
2 changes: 1 addition & 1 deletion dribdat/app.py
Expand Up @@ -160,7 +160,7 @@ def onebox(value):
app.tz = timezone(app.config['TIME_ZONE'])

# Lambda filters for safe image_url's
app.jinja_env.filters['quote_plus'] = lambda u: quote_plus(u, ':/?&=')
app.jinja_env.filters['quote_plus'] = lambda u: quote_plus(u or '', ':/?&=')

# Custom filters
@app.template_filter()
Expand Down
1 change: 1 addition & 0 deletions dribdat/assets.py
Expand Up @@ -17,6 +17,7 @@
js = Bundle(
"js/script.js",
"js/editor.js",
"js/notify.js",
filters='jsmin',
output="public/js/common.js"
)
Expand Down
30 changes: 30 additions & 0 deletions dribdat/public/api.py
Expand Up @@ -2,6 +2,8 @@
"""API calls for dribdat."""
import boto3
import tempfile
import datetime as dt

from flask import (
Blueprint, current_app,
Response, request, redirect,
Expand All @@ -10,6 +12,7 @@
)
from flask_login import login_required, current_user
from sqlalchemy import or_

from ..extensions import db
from ..utils import timesince, random_password
from ..decorators import admin_required
Expand Down Expand Up @@ -310,6 +313,33 @@ def event_push_datapackage():
return jsonify(status='Complete', results=results)


@blueprint.route('/event/current/get/status', methods=["GET"])
def event_get_status():
"""Get current event status."""
event = Event.query.filter_by(is_current=True).first() or \
Event.query.order_by(Event.id.desc()).first()
if not event:
return jsonify(status='')
return jsonify(status=event.status_text)


@blueprint.route('/event/<int:event_id>/push/status', methods=["PUT", "POST"])
@admin_required
def event_push_status(event_id):
"""Update event status."""
event = Event.query.filter_by(id=event_id).first_or_404()
newstatus = request.form.get('text')
if not request.form.get('text'):
# Clear the status
event.status = None
else:
# Update the status
event.status = str(dt.datetime.now().timestamp()) + ';' \
+ newstatus.replace(';', ':')
event.save()
return jsonify(status='OK')


@blueprint.route('/project/push.json', methods=["PUT", "POST"])
def project_push_json():
"""Push data into a project."""
Expand Down
34 changes: 33 additions & 1 deletion dribdat/public/auth.py
Expand Up @@ -9,6 +9,7 @@
from flask_dance.contrib.github import github
from dribdat.sso.auth0 import auth0
from dribdat.sso.mattermost import mattermost
from dribdat.sso.hitobito import hitobito
# Dribdat modules
from dribdat.user.models import User, Event, Role
from dribdat.extensions import login_manager # noqa: I005
Expand Down Expand Up @@ -239,8 +240,8 @@ def user_profile():
user.set_password(form.password.data)
else:
user.password = originalhash
user.updated_at = datetime.utcnow()

user.updated_at = datetime.utcnow()
db.session.add(user)
db.session.commit()
user.socialize()
Expand Down Expand Up @@ -435,3 +436,34 @@ def mattermost_login():
username,
resp_data['email'],
)


@blueprint.route("/hitobito_login", methods=["GET", "POST"])
def hitobito_login():
"""Handle login via hitobito."""
if not hitobito.authorized:
flash('Access denied to hitobito', 'danger')
return redirect(url_for("auth.login", local=1))
# Get remote user data
resp = hitobito.get("/en/oauth/profile", headers={'X-Scope': 'name'})
if not resp.ok:
flash('Unable to access hitobito data', 'danger')
return redirect(url_for("auth.login", local=1))
resp_data = resp.json()
#print(resp_data)
username = None
if 'nickname' in resp_data and resp_data['nickname'] is not None:
username = resp_data['nickname']
elif 'first_name' in resp_data and 'last_name' in resp_data:
fn = resp_data['first_name'].lower().strip()
ln = resp_data['last_name'].lower().strip()
username = "%s_%s" % (fn, ln)
if username is None:
flash('Invalid hitobito data format', 'danger')
return redirect(url_for("auth.login", local=1))
return get_or_create_sso_user(
resp_data['id'],
username,
resp_data['email'],
)

2 changes: 1 addition & 1 deletion dribdat/public/forms.py
Expand Up @@ -104,7 +104,7 @@ class ProjectDetailForm(FlaskForm):
logo_icon = StringField(
u'Named icon',
[length(max=20)], description='Select an icon from FontAwesome'
+ ': https://fontawesome.com/v5/cheatsheet')
+ ': https://fontawesome.com/v4/cheatsheet')
submit = SubmitField(u'Save changes')


Expand Down
24 changes: 15 additions & 9 deletions dribdat/public/views.py
Expand Up @@ -31,19 +31,23 @@ def current_event():

@blueprint.route("/dashboard/")
def dashboard():
"""Render a static dashboard."""
"""Render a static dashboard. This code sucks."""
event = current_event()
if not event:
return 'No current event'
wall = False
wall_url = wall = None
social_domains = ['twitter.com']
host = urlparse(event.community_url).hostname
for d in social_domains:
if host == d:
wall = True
if host == 'twitter.com' or host == 'mobile.twitter.com':
wall = 'twitter'
wall_url = event.community_url + '?ref_src=twsrc%5Etfw'
elif host == 'mastodon.social': # TODO: configure custom Mastodon provider via ENV-variable
wall = 'mastodon'
userpart = event.community_url.split('/@')[-1]
wall_url = 'https%3A%2F%2F%40' + userpart + '%40' + host + '%2Fusers%2F' + userpart.lower()
return render_template(
"public/dashboard.html",
current_event=event, with_social_wall=wall
current_event=event, with_social_wall=wall, wall_url=wall_url, status_text=event.status_text
)


Expand Down Expand Up @@ -97,13 +101,13 @@ def home():
# Filter past events
MAX_PAST_EVENTS = 6
events_past_next = events_past.count() > MAX_PAST_EVENTS
events_past = events_past.limit(MAX_PAST_EVENTS).all()
events_past = events_past.limit(MAX_PAST_EVENTS)
# Send to template
return render_template("public/home.html", active="home",
events_featured=events_featured.all(),
events_tips=resource_events.all(),
events_next=events_next.all(),
events_past=events_past,
events_past=events_past.all(),
events_past_next=events_past_next,
my_projects=my_projects,
current_event=cur_event)
Expand Down Expand Up @@ -338,4 +342,6 @@ def dribs():
'url': quote_plus(request.host_url + d.project.url)
}
return render_template("public/dribs.html",
endpoint='public.dribs', active='dribs', data=dribs)
current_event=current_event(),
endpoint='public.dribs', active='dribs',
data=dribs)

0 comments on commit 5003cd7

Please sign in to comment.