mirror of
https://github.com/Yubico/yubiadmin.git
synced 2025-02-20 14:54:30 +01:00
Added dashboard.
This commit is contained in:
parent
e25537245d
commit
38afc25abd
@ -38,6 +38,7 @@ from yubiadmin.util.system import invoke_rc_d
|
||||
from yubiadmin.util.config import (python_handler, python_list_handler,
|
||||
FileConfig)
|
||||
from yubiadmin.util.form import ConfigForm, FileForm, ListField
|
||||
from yubiadmin.apps.dashboard import panel
|
||||
import logging as log
|
||||
try:
|
||||
from yubiauth import YubiAuth
|
||||
@ -230,6 +231,12 @@ class GetApiKeyForm(Form):
|
||||
raise Exception(data['error'])
|
||||
|
||||
|
||||
def using_default_client():
|
||||
auth_config.read()
|
||||
return auth_config['client_id'] == YKVAL_DEFAULT_ID and \
|
||||
auth_config['client_secret'] == YKVAL_DEFAULT_SECRET
|
||||
|
||||
|
||||
class YubiAuthApp(App):
|
||||
"""
|
||||
YubiAuth
|
||||
@ -251,6 +258,12 @@ class YubiAuthApp(App):
|
||||
return ['general', 'database', 'validation', 'advanced']
|
||||
return ['general', 'database', 'validation', 'users', 'advanced']
|
||||
|
||||
@property
|
||||
def dash_panels(self):
|
||||
if using_default_client():
|
||||
yield panel('YubiAuth', 'Using default YubiCloud client!',
|
||||
'/%s/validation' % self.name, 'danger')
|
||||
|
||||
def general(self, request):
|
||||
return self.render_forms(request, [SecurityForm(), HSMForm()],
|
||||
template='auth/general')
|
||||
@ -268,8 +281,7 @@ class YubiAuthApp(App):
|
||||
"""
|
||||
form = ValidationServerForm()
|
||||
resp = self.render_forms(request, [form])
|
||||
if form.client_id.data == YKVAL_DEFAULT_ID and \
|
||||
form.client_secret.data == YKVAL_DEFAULT_SECRET:
|
||||
if using_default_client():
|
||||
resp.data['alerts'].append(
|
||||
{
|
||||
'type': 'warning',
|
||||
|
57
yubiadmin/apps/dashboard.py
Normal file
57
yubiadmin/apps/dashboard.py
Normal file
@ -0,0 +1,57 @@
|
||||
# Copyright (c) 2013 Yubico AB
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or
|
||||
# without modification, are permitted provided that the following
|
||||
# conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following
|
||||
# disclaimer in the documentation and/or other materials provided
|
||||
# with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from yubiadmin.util.app import App, render
|
||||
|
||||
__all__ = [
|
||||
'app',
|
||||
'panel'
|
||||
]
|
||||
|
||||
|
||||
def panel(title, content, link=None, level=None):
|
||||
return {
|
||||
'title': title,
|
||||
'content': content,
|
||||
'link': link,
|
||||
'level': level
|
||||
}
|
||||
|
||||
|
||||
class DashboardApp(App):
|
||||
hidden = True
|
||||
|
||||
def __call__(self, request):
|
||||
from yubiadmin.apps import apps
|
||||
panels = [panel for app in apps if not getattr(app, 'disabled', False)
|
||||
and hasattr(app, 'dash_panels') for panel in app.dash_panels]
|
||||
request.environ['yubiadmin.response'].extend('content',
|
||||
render('dashboard',
|
||||
panels=panels))
|
||||
|
||||
|
||||
app = DashboardApp()
|
@ -29,6 +29,7 @@ from yubiadmin.util.app import App, CollectionApp, render
|
||||
from yubiadmin.util.system import run, invoke_rc_d
|
||||
from yubiadmin.util.form import FileForm
|
||||
from yubiadmin.util.config import parse_block
|
||||
from yubiadmin.apps.dashboard import panel
|
||||
from wtforms import Form
|
||||
from wtforms.fields import TextField
|
||||
import os
|
||||
@ -74,6 +75,15 @@ class FreeRadius(App):
|
||||
def disabled(self):
|
||||
return not os.path.isdir('/etc/freeradius')
|
||||
|
||||
@property
|
||||
def dash_panels(self):
|
||||
running = is_freerad_running()
|
||||
yield panel('FreeRADIUS',
|
||||
'FreeRadius server is %s' %
|
||||
('running' if running else 'stopped'),
|
||||
'/%s/general' % self.name,
|
||||
'success' if running else 'danger')
|
||||
|
||||
def __init__(self):
|
||||
self._clients = RadiusClients()
|
||||
|
||||
|
@ -31,6 +31,7 @@ from webob import Response
|
||||
from threading import Timer
|
||||
from yubiadmin.util.app import App, render
|
||||
from yubiadmin.util.system import run
|
||||
from yubiadmin.apps.dashboard import panel
|
||||
|
||||
__all__ = [
|
||||
'app'
|
||||
@ -96,12 +97,35 @@ class SystemApp(App):
|
||||
|
||||
@property
|
||||
def disabled(self):
|
||||
return not os.path.isdir('/usr/share/yubix')
|
||||
#return not os.path.isdir('/usr/share/yubix')
|
||||
return False
|
||||
|
||||
@property
|
||||
def hidden(self):
|
||||
return self.disabled
|
||||
|
||||
@property
|
||||
def dash_panels(self):
|
||||
if needs_restart():
|
||||
yield panel('System', 'System restart required', level='danger')
|
||||
|
||||
updates = len(get_updates())
|
||||
if updates > 0:
|
||||
yield panel(
|
||||
'System',
|
||||
'There are <strong>%d</strong> updates available' % updates,
|
||||
'/%s/general' % self.name,
|
||||
'info'
|
||||
)
|
||||
|
||||
_, result = run('uptime')
|
||||
time, rest = [x.strip() for x in result.split('up', 1)]
|
||||
parts = [x.strip() for x in rest.split(',')]
|
||||
uptime = parts[0] if not 'days' in parts[0] else '%s, %s' % \
|
||||
tuple(parts[:2])
|
||||
yield panel('System', 'System time: %s<br />Uptime: %s' %
|
||||
(time, uptime), level='info')
|
||||
|
||||
def general(self, request):
|
||||
alerts = []
|
||||
if needs_restart():
|
||||
|
@ -34,6 +34,7 @@ from yubiadmin.util.config import (RegexHandler, FileConfig, php_inserter,
|
||||
parse_block, strip_comments, strip_quotes)
|
||||
from yubiadmin.util.form import ConfigForm, FileForm, DBConfigForm, ListField
|
||||
from yubiadmin.util.system import invoke_rc_d, run
|
||||
from yubiadmin.apps.dashboard import panel
|
||||
|
||||
__all__ = [
|
||||
'app'
|
||||
@ -215,6 +216,18 @@ class YubikeyVal(App):
|
||||
def disabled(self):
|
||||
return not os.path.isfile(YKVAL_CONFIG_FILE)
|
||||
|
||||
@property
|
||||
def dash_panels(self):
|
||||
if not is_daemon_running():
|
||||
ykval_config.read()
|
||||
if len(ykval_config['sync_pool']) > 0:
|
||||
yield panel('YubiKey Validation Server',
|
||||
'The sync daemon is NOT running, '
|
||||
'though the sync pool is not empty!',
|
||||
'/%s/synchronization' % self.name,
|
||||
'danger'
|
||||
)
|
||||
|
||||
def __init__(self):
|
||||
self._clients = YubikeyValClients()
|
||||
|
||||
|
@ -64,7 +64,7 @@ class YubiAdmin(object):
|
||||
modules = [data for (_, data) in apps_data.values()]
|
||||
|
||||
if not module_name:
|
||||
return render('index', modules=modules)
|
||||
module_name = 'dashboard'
|
||||
|
||||
if not module_name in apps_data:
|
||||
raise exc.HTTPNotFound
|
||||
|
@ -49,6 +49,22 @@ a.disabled, a.disabled:hover {
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.alert > a {
|
||||
color: #c09853;
|
||||
}
|
||||
|
||||
.alert-info > a {
|
||||
color: #3a87ad;
|
||||
}
|
||||
|
||||
.alert-danger > a {
|
||||
color: #b94a48;
|
||||
}
|
||||
|
||||
.alert-success > a {
|
||||
color: #468847;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: #9edb48;
|
||||
color: #ffffff;
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
<div class="row">
|
||||
<div class="span12">
|
||||
<h1>YubiADMIN</h1>
|
||||
<h1><a href="/">YubiADMIN</a></h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
<div class="span3">
|
||||
<div class="well">
|
||||
<ul class="nav nav-list">
|
||||
<li {% if module is defined and module.name == 'dashboard' %}class="active"{% endif %}><a href="/">Dashboard</a></li>
|
||||
<li class="nav-header">Modules</li>
|
||||
{% for mod in modules if not mod.disabled and not mod.hidden %}
|
||||
<li {% if module is defined and mod.name == module.name %}class="active"{% endif %} >
|
||||
|
46
yubiadmin/templates/dashboard.html
Normal file
46
yubiadmin/templates/dashboard.html
Normal file
@ -0,0 +1,46 @@
|
||||
{% macro render_panel(panel) %}
|
||||
{% if panel.level == 'danger' %}
|
||||
{% set icon = "icon-exclamation-sign" %}
|
||||
{% elif panel.level == 'info' %}
|
||||
{% set icon = "icon-info-sign" %}
|
||||
{% elif panel.level == 'success' %}
|
||||
{% set icon = "icon-ok-sign" %}
|
||||
{% else %}
|
||||
{% set icon = "icon-question-sign" %}
|
||||
{% endif %}
|
||||
<div class="alert{% if panel.level %} alert-{{ panel.level }}{% endif %}">
|
||||
<i class="{{ icon }}"></i>
|
||||
{% if panel.link %}
|
||||
<a href="{{ panel.link }}"><strong>{{ panel.title }}</strong></a>
|
||||
{% else %}
|
||||
<strong>{{ panel.title }}</strong>
|
||||
{% endif %}
|
||||
<br />
|
||||
{{ panel.content }}
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
<h2>Dashboard</h2>
|
||||
<div class="row-fluid">
|
||||
<div class="span4">
|
||||
{% for panel in panels %}
|
||||
{% if loop.index0 % 3 == 0 %}
|
||||
{{ render_panel(panel) }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="span4">
|
||||
{% for panel in panels %}
|
||||
{% if loop.index0 % 3 == 1 %}
|
||||
{{ render_panel(panel) }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="span4">
|
||||
{% for panel in panels %}
|
||||
{% if loop.index0 % 3 == 2 %}
|
||||
{{ render_panel(panel) }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
@ -98,14 +98,15 @@ class App(object):
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return sys.modules[self.__module__].__file__.split('/')[-1] \
|
||||
.rsplit('.', 1)[0]
|
||||
self.__class__.name = sys.modules[self.__module__].__file__ \
|
||||
.split('/')[-1].rsplit('.', 1)[0]
|
||||
return self.name
|
||||
|
||||
def __call__(self, request):
|
||||
section_name = request.path_info_pop()
|
||||
|
||||
if not section_name:
|
||||
section_name = self.sections[0]
|
||||
return self.redirect('/%s/%s' % (self.name, self.sections[0]))
|
||||
|
||||
if not hasattr(self, section_name):
|
||||
raise exc.HTTPNotFound
|
||||
|
Loading…
x
Reference in New Issue
Block a user