view ui/administratorwindow.cpp @ 1395:a2574a029322

Fix Base 64 signature size calculation. If the signature byte size is not equally dividable by three the base 64 encoding needs three additional bytes. The value is now fixed to avoid such errors in the future.
author Andre Heinecke <andre.heinecke@intevation.de>
date Mon, 26 Jan 2015 13:17:32 +0100
parents b6fb5d347023
children
line wrap: on
line source
/* Copyright (C) 2014 by Bundesamt für Sicherheit in der Informationstechnik
 * Software engineering by Intevation GmbH
 *
 * This file is Free Software under the GNU GPL (v>=2)
 * and comes with ABSOLUTELY NO WARRANTY!
 * See LICENSE.txt for details.
 */
#include "administratorwindow.h"

#include <QDebug>
#include <QMessageBox>
#include <QAction>
#include <QMenu>
#include <QApplication>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QGroupBox>
#include <QSplitter>
#include <QLabel>
#include <QImage>
#include <QCheckBox>
#include <QHeaderView>
#include <QFileDialog>
#include <QStandardPaths>
#include <QSortFilterProxyModel>
#include <QDesktopServices>
#include <QFileInfo>

#include "certificatetabledelegate.h"
#include "createinstallerdialog.h"
#include "createcertlistdialog.h"
#include "certificatediffdialog.h"
#include "aboutdialog.h"
#include "util.h"

#define HELP_PATH "/doc/index.html"

AdministratorWindow::AdministratorWindow() {
    setWindowTitle(tr("TrustBridge Administration"));
    createActions();
    createMenuBar();
    createContent();
    loadCurrentCertificates();
    resize(1190, 500);
}

void AdministratorWindow::loadCurrentCertificates()
{
    QString path = QStandardPaths::locate(
        QStandardPaths::DataLocation, QString("current_certificates.txt"));
    certificateModel->removeAll();
    mCertList.readList(path.toLocal8Bit());
    loadCertificateTable();
}

void AdministratorWindow::createActions()
{
}

void AdministratorWindow::createMenuBar()
{
    menuBar = new QMenuBar(this);
    QMenu *menu = new QMenu(tr("Menu"), menuBar);
    menuBar->addMenu(menu);
    QAction *createInstaller = menu->addAction(tr("Create installer ..."));
    QAction *about = menu->addAction(tr("About TrustBridge"));
    QAction *help = menu->addAction(tr("Help"));
    help->setIcon(QIcon(":/img/show-help_16.png"));
    menu->addSeparator();
    QAction *quit = menu->addAction(tr("Quit"));
    connect(createInstaller, SIGNAL(triggered()), this, SLOT(createInstaller()));
    connect(about, SIGNAL(triggered()), this, SLOT(showAbout()));
    connect(help, SIGNAL(triggered()), this, SLOT(showHelp()));
    connect(quit, SIGNAL(triggered()), qApp, SLOT(quit()));
    setMenuBar(menuBar);
}

void AdministratorWindow::createContent()
{
    // Create a central widget containing the main layout.
    QWidget *base = new QWidget;

    // Layouts and Container
    QVBoxLayout *mainLayout = new QVBoxLayout;
    QVBoxLayout *certLayout = new QVBoxLayout;
    QHBoxLayout *headerLayout = new QHBoxLayout;
    QVBoxLayout *headerTextLayout = new QVBoxLayout;
    QHBoxLayout *bottomLayout = new QHBoxLayout;

    // The certificate list
    QGroupBox *certBox = new QGroupBox(
        tr("All managed root certificates of the certificate list: "));
    certificateView = new QTableView;
    certificateModel = new CertificateTabelModel();
    mFilterModel = new QSortFilterProxyModel(this);
    mFilterModel->setSourceModel(certificateModel);
    CertificateTableDelegate *delegate = new CertificateTableDelegate(certificateView);
    certificateView->setModel(mFilterModel);
    certificateView->setItemDelegate(delegate);
    certificateView->horizontalHeader()->setStretchLastSection(true);
    certificateView->resizeColumnsToContents();
    certificateView->setColumnWidth(0, 60);
    certificateView->setSelectionBehavior(QAbstractItemView::SelectRows);
    certificateView->setSelectionMode(QAbstractItemView::SingleSelection);
    connect(certificateView, SIGNAL(clicked(const QModelIndex&)), this,
        SLOT(clickedCertificate(const QModelIndex&)));
    certificateView->verticalHeader()->setVisible(false);
    certificateView->setSortingEnabled(true);
    certLayout->addWidget(certificateView);
    certBox->setLayout(certLayout);

    // The header (icon, about text)
    QImage *logoImage = new QImage(":/img/logo.png");
    QLabel *logo = new QLabel;
    logo->setBackgroundRole(QPalette::Base);
    logo->setPixmap(QPixmap::fromImage(*logoImage));
    QLabel *title = new QLabel("<h2>" + tr("TrustBridge Administration") + "</h2>");
    QLabel *subTitle = new QLabel(
        tr("Management application of the BSI certificate installer"));
    headerTextLayout->addWidget(title);
    headerTextLayout->addWidget(subTitle);
    headerLayout->addWidget(logo);
    headerLayout->addLayout(headerTextLayout);
    headerLayout->setStretch(0, 0);
    headerLayout->setStretch(1, 10);

    // The buttons.
    bottomLayout->setAlignment(Qt::AlignBottom);
    saveButton = new QPushButton(QIcon(":/img/document-save.png"), " " + tr("Create list"));
    connect(saveButton, SIGNAL(clicked()), this, SLOT(saveCertificateFile()));
    loadButton = new QPushButton(QIcon(":/img/document-open.png"), " " + tr("Load list"));
    connect(loadButton, SIGNAL(clicked()), this, SLOT(loadCertificateFile()));
    addButton = new QPushButton(QIcon(":/img/document-new.png"), " " + tr("Add certificate"));
    connect(addButton, SIGNAL(clicked()), this, SLOT(addCertificates()));
    removeButton = new QPushButton(QIcon(":/img/document-close.png"), " " + tr("Remove certificate"));
    removeButton->setEnabled(false);
    connect(removeButton, SIGNAL(clicked()), this, SLOT(removeCertificates()));
    bottomLayout->addWidget(saveButton);
    bottomLayout->addWidget(loadButton);
    bottomLayout->addWidget(addButton);
    bottomLayout->addWidget(removeButton);
    bottomLayout->insertStretch(4, 10);

    mainLayout->addLayout(headerLayout);
    mainLayout->addWidget(certBox);
    mainLayout->addLayout(bottomLayout);


    // QMainWindow allready has a layout. All child layouts and widgets are
    // managed in the central widget.
    base->setLayout(mainLayout);
    setCentralWidget(base);
}

void AdministratorWindow::loadCertificateFile()
{
    QString lastCertFile = mSettings.value("LastCertList", QDir::homePath()).toString();
    QString certFile = QFileDialog::getOpenFileName(
        this, tr("Select certificate list file"), lastCertFile, "*.txt");
    if (certFile.isNull()) {
        return;
    }
    mCertList.readList(certFile.toLocal8Bit().constData());
    if (!mCertList.isValid()) {
        QMessageBox::warning(this, tr("Error!"), tr("Failed to load the certificate list."));
    } else {
        certificateModel->removeAll();
        loadCertificateTable();
        mSettings.setValue("LastCertList", certFile);
    }
}

void AdministratorWindow::saveCertificateFile()
{
    CertificateDiffDialog *diffDialog = new CertificateDiffDialog(this);
    int ret = diffDialog->exec();
    if (ret == QDialog::Accepted) {
        CreateCertListDialog *dialog = new CreateCertListDialog(this);
        dialog->show();
        connect (dialog, SIGNAL(creationSuccessful()), this, SLOT(loadCurrentCertificates()));
    }
}

void AdministratorWindow::addCertificates()
{
    QStringList certFiles = QFileDialog::getOpenFileNames(
        this, tr("Select certificate"), mSettings.value("LastCertAddDir", QDir::homePath()).toString(),
            "*.pem *.der *.crt *.cer");
    for (int i = 0; i < certFiles.size(); i++) {
        QString certFile = certFiles.at(i);
        QList<Certificate> certs = Certificate::fromFileName(certFile);
        addToCertificateTable(certs);
        QDir certDir (certFile);
        mSettings.setValue("LastCertAddDir", certDir.absolutePath());
    }
    certificateView->resizeColumnsToContents();
    certificateView->setColumnWidth(0, 60);
}

void AdministratorWindow::removeCertificates()
{
    QModelIndexList list = certificateView->selectionModel()->selectedRows();
    foreach (QModelIndex idx, list) {
        QModelIndex realidx = mFilterModel->mapToSource(idx);
        if (!idx.data(Qt::UserRole).toBool()) {
            certificateModel->removeRow(realidx.row(), realidx.parent());
        } else {
            qDebug() << "Tried to remove old certificate at idx: " << realidx.row();;
        }
    }
}

void AdministratorWindow::loadCertificateTable() {
    foreach(const Certificate &cert, mCertList.getCertificates()) {
        certificateModel->addCertificate(cert, true);
    }
    certificateView->resizeColumnsToContents();
    certificateView->setColumnWidth(0, 60);
}

void AdministratorWindow::addToCertificateTable(const QList<Certificate> &certs)
{
    foreach(const Certificate &cert, certs) {
        certificateModel->addCertificate(cert, false);
    }
}

void AdministratorWindow::showAbout()
{
    AboutDialog *dialog = new AboutDialog(this);
    dialog->show();
}

void AdministratorWindow::createInstaller()
{
    qDebug() << "create Installer";
    CreateInstallerDialog *dialog = new CreateInstallerDialog(this);
    dialog->show();
}

void AdministratorWindow::clickedCertificate(const QModelIndex &index)
{
    QModelIndexList list = certificateView->selectionModel()->selectedRows();
    bool all_are_removable = true;
    foreach (QModelIndex idx, list) {
        if (idx.data(Qt::UserRole).toBool()) {
            all_are_removable = false;
            break;
        }
    }
    removeButton->setEnabled(all_are_removable);
}

void AdministratorWindow::logChanges(const QString &currentCerts, const QString& keyFingerprint)
{
    QDir logDir(QStandardPaths::writableLocation(QStandardPaths::DataLocation));
    QString logFilePath = logDir.filePath("log.txt");
    QFile logFile(logFilePath);

    if (!logFile.open(QIODevice::Append)) {
        QMessageBox::warning(this, tr("Error!"), tr("Failed to open log file: ") +
                logFilePath + tr(" Changes are not logged!"));
        qDebug() << "Failed to open log file: " << logFilePath;
        return;
    }

    CertificateList newCertList;
    newCertList.readList(currentCerts.toLocal8Bit());
    QByteArray entries = createLogEntries(newCertList, keyFingerprint);
    if(logFile.write(entries) != entries.size()) {
        QMessageBox::warning(this, tr("Error!"), tr("Failed to write log file: ") +
                logFilePath + tr(" Changes are not logged!"));
        return;
    }
    logFile.close();
}

QByteArray AdministratorWindow::createLogEntries(const CertificateList &list, const QString& keyFingerprint)
{
    QByteArray entries;
    QByteArray removeListEntries;

    QDateTime currentDate = QDateTime::currentDateTime();
    QDateTime newListDate = list.date();
    QDateTime listDate = mCertList.date();

    entries.append("##### " +
        currentDate.toString("yyyy-MM-dd hh:mm") +
        tr(" new certificatelist ") +
        newListDate.toString(Qt::ISODate) +
        tr(" based on list from ") +
        listDate.toString(Qt::ISODate) +
        "#####\r\n");
    entries.append(tr("signing certificate:") + "\r\n");
    entries.append(keyFingerprint);

    entries.append(QString::fromLatin1("\r\n") + tr("new certificates:") +"\r\n");

    foreach (const Certificate& cert, list.getCertificates()) {
        if (!mCertList.getCertificates().contains(cert)) {
            QString certEntry(cert.subjectCN() + ": " + cert.base64Line() + "\r\n");
            if (cert.isInstallCert()) {
                entries.append(certEntry);
            }
            else {
                removeListEntries.append(certEntry);
            }
        }
    }

    entries.append(tr("certificates marked to remove:") + "\r\n");
    entries.append(removeListEntries);
    entries.append("\r\n");

    return entries;
}

QList<Certificate> AdministratorWindow::currentChanges()
{
    QList<Certificate> changed;
    foreach(const Certificate& cert, certificates()) {
        if (!mCertList.getCertificates().contains(cert)) {
            changed.append(cert);
        }
    }
    return changed;
}

void AdministratorWindow::showHelp()
{
    char *inst_dir = get_install_dir();
    if (!inst_dir) {
        qDebug() << "Failed to find install dir";
        return;
    }
    QString helpPath = QString::fromUtf8(inst_dir);
    helpPath += HELP_PATH;
    QFileInfo fiHelp(helpPath);
    qDebug() << "Opening help: " << fiHelp.absoluteFilePath();
    if (!fiHelp.exists()) {
        QMessageBox::warning(this, tr("Error!"), tr ("Failed to find the manual"));
        return;
    }
#ifdef Q_OS_WIN
    QDesktopServices::openUrl(QUrl("file:///" + fiHelp.absoluteFilePath()));
#else
    QDesktopServices::openUrl(QUrl(fiHelp.absoluteFilePath()));
#endif
    free (inst_dir);
    return;
}

http://wald.intevation.org/projects/trustbridge/