1
0
mirror of https://github.com/Yubico/yubiadmin.git synced 2025-02-20 14:54:30 +01:00

Added dashboard.

This commit is contained in:
Dain Nilsson 2013-06-11 17:32:17 +02:00
parent e25537245d
commit 38afc25abd
10 changed files with 188 additions and 8 deletions

View File

@ -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',

View 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()

View File

@ -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()

View File

@ -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():

View File

@ -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()

View File

@ -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

View File

@ -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;

View File

@ -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 %} >

View 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>

View File

@ -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