Add basic admin console
Change-Id: Ib5e17d0d0952959ad3461e17da7c2d1247594548
diff --git a/TODO.md b/TODO.md
index fc0ee65..429aa11 100644
--- a/TODO.md
+++ b/TODO.md
@@ -20,6 +20,7 @@
- DONE: ensure columns have the right properties in DB
- DONE: send new user registration mails to admins
- DONE: check why newbooking is allowed if ssh key deleted after it gets added the first time
+- ensure users can only modify their own bookings
- introduce userstatus table and relationship
- check why error flashed after the first ssh key registration
- configure jenkins jobs for handle and delete booking
diff --git a/app/__init__.py b/app/__init__.py
index cb01b21..b46d683 100644
--- a/app/__init__.py
+++ b/app/__init__.py
@@ -35,6 +35,9 @@
from app.api import bp as api_bp
app.register_blueprint(api_bp, url_prefix='/api')
+ from app.admin import bp as admin_bp
+ app.register_blueprint(admin_bp, url_prefix='/admin')
+
from app.user import bp as user_bp
app.register_blueprint(user_bp)
diff --git a/app/admin/__init__.py b/app/admin/__init__.py
new file mode 100644
index 0000000..23ed694
--- /dev/null
+++ b/app/admin/__init__.py
@@ -0,0 +1,5 @@
+from flask import Blueprint
+
+bp = Blueprint('admin', __name__)
+
+from app.admin import routes
diff --git a/app/admin/decorators.py b/app/admin/decorators.py
new file mode 100644
index 0000000..93cfb17
--- /dev/null
+++ b/app/admin/decorators.py
@@ -0,0 +1,15 @@
+from functools import wraps
+from flask import flash, redirect, url_for
+from flask_login import current_user
+
+from app.models import UserRole
+
+def admin_required(func):
+ @wraps(func)
+ def decorated_function(*args, **kwargs):
+ admin_role_id = UserRole.query.filter_by(role_name='admin').first().id
+ if current_user.role_id != admin_role_id:
+ return redirect(url_for('main.index'))
+ return func(*args, **kwargs)
+
+ return decorated_function
diff --git a/app/admin/routes.py b/app/admin/routes.py
new file mode 100644
index 0000000..0309f2f
--- /dev/null
+++ b/app/admin/routes.py
@@ -0,0 +1,44 @@
+from datetime import datetime, timedelta
+
+from flask import current_app, flash, redirect, render_template, request, url_for
+from flask_login import current_user, login_user, logout_user, login_required
+from werkzeug.urls import url_parse
+
+from app import db
+from app.email import send_email, notify_admins
+from app.admin import bp
+from app.admin.decorators import admin_required
+from app.models import Booking, BookingStatus, User, UserOrganization, UserRole
+
+@bp.route('/bookings', methods=['GET', 'POST'])
+@login_required
+@admin_required
+def bookings():
+ flash('You are logged in as admin!', 'danger')
+ booking_status_id_to_text = dict()
+ for k, v in BookingStatus.query.with_entities(BookingStatus.id, BookingStatus.status_text).all():
+ booking_status_id_to_text[k] = v
+ user_id_to_name = dict()
+ for k, v in User.query.with_entities(User.id, User.email).all():
+ user_id_to_name[k] = v
+ bookings = Booking.query.order_by(Booking.created_on.desc())
+ return render_template('admin/bookings.html', title='[nolabs] | All Bookings', user=current_user,
+ bookings=bookings, user_id_to_name=user_id_to_name, booking_status_id_to_text=booking_status_id_to_text)
+
+@bp.route('/users', methods=['GET', 'POST'])
+@login_required
+@admin_required
+def users():
+ flash('You are logged in as admin!', 'danger')
+ user_id_to_name = dict()
+ for k, v in User.query.with_entities(User.id, User.email).all():
+ user_id_to_name[k] = v
+ role_id_to_name = dict()
+ for k, v in UserRole.query.with_entities(UserRole.id, UserRole.role_name).all():
+ role_id_to_name[k] = v
+ org_id_to_name = dict()
+ for k, v in UserOrganization.query.with_entities(UserOrganization.id, UserOrganization.org_name).all():
+ org_id_to_name[k] = v
+ users = User.query.order_by(User.registered_on.desc())
+ return render_template('admin/users.html', title='[nolabs] | All Users', user=current_user,
+ users=users, user_id_to_name=user_id_to_name, role_id_to_name=role_id_to_name, org_id_to_name=org_id_to_name)
diff --git a/app/templates/admin/bookings.html b/app/templates/admin/bookings.html
new file mode 100644
index 0000000..2651319
--- /dev/null
+++ b/app/templates/admin/bookings.html
@@ -0,0 +1,61 @@
+{% extends "base.html" %}
+{% block app_content %}
+ <div class="jumbotron">
+ <h3>All Bookings</h3>
+ <br/>
+<table class="table">
+ <thead>
+ <tr>
+ <th scope="col">#</th>
+ <th scope="col">User</th>
+ <th scope="col">Flavor</th>
+ <th scope="col">IP</th>
+ <th scope="col">Created On</th>
+ <th scope="col">Expires On</th>
+ <th scope="col">Status</th>
+ <th scope="col">Action</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for booking in bookings %}
+ {% set booking_status_text = booking_status_id_to_text[booking.status_id] %}
+ {% set booking_user = user_id_to_name[booking.user_id] %}
+ {% set text_color = '#808080' %}
+ {% if booking_status_text == 'new' %}
+ {% set text_color = '#808080' %}
+ {% elif booking_status_text == 'deploying' %}
+ {% set text_color = '#f4d03f' %}
+ {% elif booking_status_text == 'active' %}
+ {% set text_color = '#239b56' %}
+ {% elif booking_status_text == 'expired' %}
+ {% set text_color = '#eb984e' %}
+ {% elif booking_status_text == 'failed' %}
+ {% set text_color = '#ff0000' %}
+ {% elif booking_status_text == 'cancelled' %}
+ {% set text_color = '#cfcbcb' %}
+ {% endif -%}
+ <tr>
+ <th scope="row">{{ booking.id }}</th>
+ <td>{{ booking_user }}</td>
+ <td>{{ booking.scenario }}</td>
+ <td>{{ booking.floating_ip }}</td>
+ <td>{{ booking.created_on }}</td>
+ <td>{{ booking.expires_on }}</td>
+ <td><b><font color='{{ text_color }}'>{{ booking_status_text }}</font></b></td>
+ <td></td>
+ </tr>
+ {% endfor %}
+ </tbody>
+</table>
+<center>
+{% if prev_url %}
+ <a href="{{ prev_url }}">Newer bookings</a>  
+{% endif %}
+{% if next_url %}
+ <a href="{{ next_url }}">Previous bookings</a>
+{% endif %}
+</center>
+</div>
+ <div class="jumbotron" style="background:white">
+ </div>
+{% endblock %}
diff --git a/app/templates/admin/users.html b/app/templates/admin/users.html
new file mode 100644
index 0000000..44f4839
--- /dev/null
+++ b/app/templates/admin/users.html
@@ -0,0 +1,47 @@
+{% extends "base.html" %}
+{% block app_content %}
+ <div class="jumbotron">
+ <h3>All Users</h3>
+ <br/>
+<table class="table">
+ <thead>
+ <tr>
+ <th scope="col">#</th>
+ <th scope="col">Email</th>
+ <th scope="col">Organization</th>
+ <th scope="col">Role</th>
+ <th scope="col">Registered On</th>
+ <th scope="col">Last Logged in</th>
+ <th scope="col">Confirmed On</th>
+ <th scope="col">Confirmed</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for user in users %}
+ {% set user_org = org_id_to_name[user.org_id] %}
+ {% set user_role = role_id_to_name[user.role_id] %}
+ <tr>
+ <th scope="row">{{ user.id }}</th>
+ <td>{{ user.email }}</td>
+ <td>{{ user_org }}</td>
+ <td>{{ user_role }}</td>
+ <td>{{ user.registered_on }}</td>
+ <td>{{ user.last_logged_in }}</td>
+ <td>{{ user.confirmed_on }}</td>
+ <td>{{ user.confirmed }}</td>
+ </tr>
+ {% endfor %}
+ </tbody>
+</table>
+<center>
+{% if prev_url %}
+ <a href="{{ prev_url }}">Newer bookings</a>  
+{% endif %}
+{% if next_url %}
+ <a href="{{ next_url }}">Previous bookings</a>
+{% endif %}
+</center>
+</div>
+ <div class="jumbotron" style="background:white">
+ </div>
+{% endblock %}