Skip to content
Snippets Groups Projects
Select Git revision
  • 88faa60b34ce3dea55b5e738e74abd609e1adf56
  • master default protected
  • release/202005
  • release/202001
  • release/201912
  • release/201911
  • release/releaseWindowsTestOne
  • release/releaseTest
  • release/releaseWindowsTest
  • release/windowsReleaseTest
  • release/201910
  • release/qt/201910
  • release/windows-test/201910
  • release/201908
  • release/201906
  • release/201905
  • release/201904
  • release/201903
  • release/201902
  • release/201901
  • release/201812
  • 1.0.0
  • 0.3.0
  • 0.2.1
  • 0.2.0
  • 0.1.0
26 results

securityevaluationmodel.cpp

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    securityevaluationmodel.cpp 36.49 KiB
    /****************************************************************************
     *   Copyright (C) 2013-2016 by Savoir-faire Linux                          *
     *   Author : Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com> *
     *                                                                          *
     *   This library is free software; you can redistribute it and/or          *
     *   modify it under the terms of the GNU Lesser General Public             *
     *   License as published by the Free Software Foundation; either           *
     *   version 2.1 of the License, or (at your option) any later version.     *
     *                                                                          *
     *   This library is distributed in the hope that it will be useful,        *
     *   but WITHOUT ANY WARRANTY; without even the implied warranty of         *
     *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU      *
     *   Lesser General Public License for more details.                        *
     *                                                                          *
     *   You should have received a copy of the GNU General Public License      *
     *   along with this program.  If not, see <http://www.gnu.org/licenses/>.  *
     ***************************************************************************/
    #include "securityevaluationmodel.h"
    
    //Qt
    #include <QtCore/QIdentityProxyModel>
    #include <QtCore/QTimer>
    
    //Ring
    #include "account.h"
    #include "certificatemodel.h"
    #include "globalinstances.h"
    #include "interfaces/pixmapmanipulatori.h"
    #include "private/securityevaluationmodel_p.h"
    #include "securityflaw.h"
    #include "private/securityflaw_p.h"
    #include "private/certificate_p.h"
    
    #include <QtAlgorithms>
    
    const QString SecurityEvaluationModelPrivate::messages[enum_class_size<SecurityEvaluationModel::AccountSecurityChecks>()] = {
       /*SRTP_ENABLED                */QObject::tr("Your media streams are not encrypted, please enable SDES"),
       /*TLS_ENABLED                 */QObject::tr("TLS is disabled, the negotiation won't be encrypted. Your communication will be vulnerable to "
                                       "snooping"),
       /*CERTIFICATE_MATCH           */QObject::tr("Your certificate and authority don't match, if your certificate require an authority, it won't work"),
       /*OUTGOING_SERVER_MATCH       */QObject::tr("The outgoing server specified doesn't match the hostname or the one included in the certificate"),
       /*VERIFY_INCOMING_ENABLED     */QObject::tr("The \"verify incoming certificate\" option is disabled, this leave you vulnerable to man in the middle attack"),
       /*VERIFY_ANSWER_ENABLED       */QObject::tr("The \"verify answer certificate\" option is disabled, this leave you vulnerable to man in the middle attack"),
       /*REQUIRE_CERTIFICATE_ENABLED */QObject::tr("None of your certificate provide a private key, this is required. Please select a private key"
                                           " or use a certificate with one built-in"),
       /* NOT_MISSING_CERTIFICATE    */QObject::tr("No certificate has been provided. This is, for now, unsupported by Ring"),
       /* NOT_MISSING_AUTHORITY      */QObject::tr("No certificate authority is provided, it won't be possible to validate if the answer certificates are valid. Some account may also not work."),
    };
    
    static const QString s1 = QObject::tr("Your certificate is expired, please contact your system administrator.");
    static const QString s2 = QObject::tr("Your certificate is self signed. This break the chain of trust.");
    
    const TypedStateMachine< SecurityEvaluationModel::SecurityLevel , SecurityEvaluationModel::AccountSecurityChecks >
    SecurityEvaluationModelPrivate::maximumSecurityLevel = {{
       /* SRTP_ENABLED                     */ SecurityEvaluationModel::SecurityLevel::NONE        ,
       /* TLS_ENABLED                      */ SecurityEvaluationModel::SecurityLevel::NONE        ,
       /* CERTIFICATE_MATCH                */ SecurityEvaluationModel::SecurityLevel::WEAK        ,
       /* OUTGOING_SERVER_MATCH            */ SecurityEvaluationModel::SecurityLevel::MEDIUM      ,
       /* VERIFY_INCOMING_ENABLED          */ SecurityEvaluationModel::SecurityLevel::MEDIUM      ,
       /* VERIFY_ANSWER_ENABLED            */ SecurityEvaluationModel::SecurityLevel::MEDIUM      ,
       /* REQUIRE_CERTIFICATE_ENABLED      */ SecurityEvaluationModel::SecurityLevel::WEAK        ,
       /* NOT_MISSING_CERTIFICATE          */ SecurityEvaluationModel::SecurityLevel::WEAK        ,
       /* NOT_MISSING_AUTHORITY            */ SecurityEvaluationModel::SecurityLevel::ACCEPTABLE  ,
    }};
    
    const TypedStateMachine< SecurityEvaluationModel::Severity , SecurityEvaluationModel::AccountSecurityChecks >
    SecurityEvaluationModelPrivate::flawSeverity = {{
       /* SRTP_ENABLED                      */ SecurityEvaluationModel::Severity::ISSUE           ,
       /* TLS_ENABLED                       */ SecurityEvaluationModel::Severity::ISSUE           ,
       /* CERTIFICATE_MATCH                 */ SecurityEvaluationModel::Severity::ERROR           ,
       /* OUTGOING_SERVER_MATCH             */ SecurityEvaluationModel::Severity::WARNING         ,
       /* VERIFY_INCOMING_ENABLED           */ SecurityEvaluationModel::Severity::ISSUE           ,
       /* VERIFY_ANSWER_ENABLED             */ SecurityEvaluationModel::Severity::ISSUE           ,
       /* REQUIRE_CERTIFICATE_ENABLED       */ SecurityEvaluationModel::Severity::ISSUE           ,
       /* NOT_MISSING_CERTIFICATE           */ SecurityEvaluationModel::Severity::WARNING         ,
       /* NOT_MISSING_AUTHORITY             */ SecurityEvaluationModel::Severity::INFORMATION     ,
    }};
    
    const TypedStateMachine< SecurityEvaluationModel::SecurityLevel , Certificate::Checks > SecurityEvaluationModelPrivate::maximumCertificateSecurityLevel = {{
       /* HAS_PRIVATE_KEY                   */ SecurityEvaluationModel::SecurityLevel::NONE       ,
       /* EXPIRED                           */ SecurityEvaluationModel::SecurityLevel::MEDIUM     ,
       /* STRONG_SIGNING                    */ SecurityEvaluationModel::SecurityLevel::WEAK       ,
       /* NOT_SELF_SIGNED                   */ SecurityEvaluationModel::SecurityLevel::MEDIUM     ,
       /* KEY_MATCH                         */ SecurityEvaluationModel::SecurityLevel::NONE       ,
       /* PRIVATE_KEY_STORAGE_PERMISSION    */ SecurityEvaluationModel::SecurityLevel::MEDIUM     ,
       /* PUBLIC_KEY_STORAGE_PERMISSION     */ SecurityEvaluationModel::SecurityLevel::MEDIUM     ,
       /* PRIVATE_KEY_DIRECTORY_PERMISSIONS */ SecurityEvaluationModel::SecurityLevel::MEDIUM     ,
       /* PUBLIC_KEY_DIRECTORY_PERMISSIONS  */ SecurityEvaluationModel::SecurityLevel::MEDIUM     ,
       /* PRIVATE_KEY_STORAGE_LOCATION      */ SecurityEvaluationModel::SecurityLevel::ACCEPTABLE ,
       /* PUBLIC_KEY_STORAGE_LOCATION       */ SecurityEvaluationModel::SecurityLevel::ACCEPTABLE ,
       /* PRIVATE_KEY_SELINUX_ATTRIBUTES    */ SecurityEvaluationModel::SecurityLevel::ACCEPTABLE ,
       /* PUBLIC_KEY_SELINUX_ATTRIBUTES     */ SecurityEvaluationModel::SecurityLevel::ACCEPTABLE ,
       /* EXIST                             */ SecurityEvaluationModel::SecurityLevel::NONE       ,
       /* VALID                             */ SecurityEvaluationModel::SecurityLevel::NONE       ,
       /* VALID_AUTHORITY                   */ SecurityEvaluationModel::SecurityLevel::MEDIUM     ,
       /* KNOWN_AUTHORITY                   */ SecurityEvaluationModel::SecurityLevel::ACCEPTABLE , //TODO figure out of the impact of this
       /* NOT_REVOKED                       */ SecurityEvaluationModel::SecurityLevel::WEAK       ,
       /* AUTHORITY_MATCH                   */ SecurityEvaluationModel::SecurityLevel::NONE       ,
       /* EXPECTED_OWNER                    */ SecurityEvaluationModel::SecurityLevel::MEDIUM     , //TODO figure out of the impact of this
       /* ACTIVATED                         */ SecurityEvaluationModel::SecurityLevel::MEDIUM     , //TODO figure out of the impact of this
    }};
    
    static const Matrix1D<Certificate::Checks, bool> relevantWithoutPrivateKey = {
       { Certificate::Checks::HAS_PRIVATE_KEY                  , false },
       { Certificate::Checks::EXPIRED                          , true  },
       { Certificate::Checks::STRONG_SIGNING                   , true  },
       { Certificate::Checks::NOT_SELF_SIGNED                  , true  },
       { Certificate::Checks::KEY_MATCH                        , false },
       { Certificate::Checks::PRIVATE_KEY_STORAGE_PERMISSION   , true  },
       { Certificate::Checks::PUBLIC_KEY_STORAGE_PERMISSION    , true  },
       { Certificate::Checks::PRIVATE_KEY_DIRECTORY_PERMISSIONS, true  },
       { Certificate::Checks::PUBLIC_KEY_DIRECTORY_PERMISSIONS , true  },
       { Certificate::Checks::PRIVATE_KEY_STORAGE_LOCATION     , true  },
       { Certificate::Checks::PUBLIC_KEY_STORAGE_LOCATION      , true  },
       { Certificate::Checks::PRIVATE_KEY_SELINUX_ATTRIBUTES   , true  },
       { Certificate::Checks::PUBLIC_KEY_SELINUX_ATTRIBUTES    , true  },
       { Certificate::Checks::EXIST                            , true  },
       { Certificate::Checks::VALID                            , true  },
       { Certificate::Checks::VALID_AUTHORITY                  , true  },
       { Certificate::Checks::KNOWN_AUTHORITY                  , true  },
       { Certificate::Checks::NOT_REVOKED                      , true  },
       { Certificate::Checks::AUTHORITY_MATCH                  , true  },
       { Certificate::Checks::EXPECTED_OWNER                   , true  },
       { Certificate::Checks::ACTIVATED                        , true  },
    };
    
    const TypedStateMachine< SecurityEvaluationModel::Severity      , Certificate::Checks > SecurityEvaluationModelPrivate::certificateFlawSeverity = {{
       /* HAS_PRIVATE_KEY                   */ SecurityEvaluationModel::Severity::ERROR           ,
       /* EXPIRED                           */ SecurityEvaluationModel::Severity::WARNING         ,
       /* STRONG_SIGNING                    */ SecurityEvaluationModel::Severity::ISSUE           ,
       /* NOT_SELF_SIGNED                   */ SecurityEvaluationModel::Severity::WARNING         ,
       /* KEY_MATCH                         */ SecurityEvaluationModel::Severity::ERROR           ,
       /* PRIVATE_KEY_STORAGE_PERMISSION    */ SecurityEvaluationModel::Severity::WARNING         ,
       /* PUBLIC_KEY_STORAGE_PERMISSION     */ SecurityEvaluationModel::Severity::WARNING         ,
       /* PRIVATE_KEY_DIRECTORY_PERMISSIONS */ SecurityEvaluationModel::Severity::WARNING         ,
       /* PUBLIC_KEY_DIRECTORY_PERMISSIONS  */ SecurityEvaluationModel::Severity::WARNING         ,
       /* PRIVATE_KEY_STORAGE_LOCATION      */ SecurityEvaluationModel::Severity::INFORMATION     ,
       /* PUBLIC_KEY_STORAGE_LOCATION       */ SecurityEvaluationModel::Severity::INFORMATION     ,
       /* PRIVATE_KEY_SELINUX_ATTRIBUTES    */ SecurityEvaluationModel::Severity::INFORMATION     ,
       /* PUBLIC_KEY_SELINUX_ATTRIBUTES     */ SecurityEvaluationModel::Severity::INFORMATION     ,
       /* EXIST                             */ SecurityEvaluationModel::Severity::ERROR           ,
       /* VALID                             */ SecurityEvaluationModel::Severity::ERROR           ,
       /* VALID_AUTHORITY                   */ SecurityEvaluationModel::Severity::WARNING         ,
       /* KNOWN_AUTHORITY                   */ SecurityEvaluationModel::Severity::WARNING         ,
       /* NOT_REVOKED                       */ SecurityEvaluationModel::Severity::ISSUE           ,
       /* AUTHORITY_MATCH                   */ SecurityEvaluationModel::Severity::ISSUE           ,
       /* EXPECTED_OWNER                    */ SecurityEvaluationModel::Severity::WARNING         ,
       /* ACTIVATED                         */ SecurityEvaluationModel::Severity::WARNING         ,
    }};
    
    
    /**
     * This class add a prefix in front of Qt::DisplayRole to add a disambiguation
     * when there is multiple certificates in the same SecurityEvaluationModel and
     * also add new roles such as the severity, BackgroundRole and DecorationRole
     */
    class PrefixAndSeverityProxyModel : public QIdentityProxyModel
    {
       Q_OBJECT
    
    public:
    
       explicit PrefixAndSeverityProxyModel(const QString& prefix,QAbstractItemModel* parent);
    
       virtual QModelIndex index       ( int row                  , int column, const QModelIndex& parent ) const override;
       virtual QVariant    data        ( const QModelIndex& index , int role                              ) const override;
       virtual int         columnCount ( const QModelIndex& parent                                        ) const override;
    
       //Attributes
       QString m_Name;
    };
    
    /**
     * This model transform accounts attributes into security checks to validate if
     * some options reduce the security level.
     */
    class AccountChecksModel : public QAbstractTableModel
    {
       Q_OBJECT
    
    public:
    
       enum class Columns {
          MESSAGE ,
          SOURCE  ,
          RESULT  ,
          COUNT__
       };
    
       AccountChecksModel(const Account* a);
    
       //Model functions
       virtual QVariant      data        ( const QModelIndex& index, int role = Qt::DisplayRole     ) const override;
       virtual int           rowCount    ( const QModelIndex& parent = QModelIndex()                ) const override;
       virtual int           columnCount ( const QModelIndex& parent = QModelIndex()                ) const override;
       virtual Qt::ItemFlags flags       ( const QModelIndex& index                                 ) const override;
       virtual bool          setData     ( const QModelIndex& index, const QVariant &value, int role)       override;
       virtual QHash<int,QByteArray> roleNames() const override;
    
       //Helpers
       void update();
    
    private:
       //Attributes
       const Account* m_pAccount;
       Matrix1D<SecurityEvaluationModel::AccountSecurityChecks, Certificate::CheckValues> m_lCachedResults;
    };
    
    /**
     * This model take multiple listModels and append them one after the other
     *
     * the trick for high performance is to known at compile time the sizes
     */
    class CombinaisonProxyModel : public QAbstractTableModel
    {
       Q_OBJECT
    
    public:
       explicit CombinaisonProxyModel( QAbstractItemModel* publicCert ,
                                       QAbstractItemModel* caCert     ,
                                       QAbstractItemModel* account    ,
                                       QObject*            parent
                                     );
    
       //Model functions
       virtual QVariant      data        ( const QModelIndex& index, int role = Qt::DisplayRole     ) const override;
       virtual int           rowCount    ( const QModelIndex& parent = QModelIndex()                ) const override;
       virtual int           columnCount ( const QModelIndex& parent = QModelIndex()                ) const override;
       virtual Qt::ItemFlags flags       ( const QModelIndex& index                                 ) const override;
       virtual bool          setData     ( const QModelIndex& index, const QVariant &value, int role)       override;
       virtual QHash<int,QByteArray> roleNames() const override;
    
    private:
       //Attributes
       QVector<QAbstractItemModel*> m_lSources;
    
       ///All source model, in order
       enum Src {
          CA = 0, /*!< Rows allocated for the certificate authority */
          PK = 1, /*!< Rows allocated for the public certificate    */
          AC = 2, /*!< Rows allocated for the account settions      */
          ER = 3, /*!< TODO Rows allocated for runtime error        */
       };
    
       ///This model expect a certain size, get each sections size
       constexpr static const short sizes[] = {
          enum_class_size< Certificate             :: Checks                > (),
          enum_class_size< Certificate             :: Checks                > (),
          enum_class_size< SecurityEvaluationModel :: AccountSecurityChecks > (),
       };
    
       ///Get the combined size
       constexpr inline static int totalSize() {
          return sizes[CA] + sizes[PK] + sizes[AC]+1;
       }
    
       ///Get a model index from a value
       constexpr inline static int toModelIdx(const int value) {
          return (value >= sizes[CA] + sizes[PK] ?  AC : (
                  value >= sizes[PK]             ?  PK :
                                                    CA ) );
       }
    
       //Compute the correct index.row() offset
       constexpr inline static int fromFinal(const int value) {
          return (value >= sizes[CA] + sizes[PK] ?  value - sizes[CA] - sizes[PK] : (
                  value >= sizes[PK]             ?  value - sizes[CA]             :
                                                    value                         ) );
       }
    
    };
    
    constexpr const short CombinaisonProxyModel::sizes[];
    
    ///Create a callback map for signals to avoid a large switch(){} in the code
    static const Matrix1D<SecurityEvaluationModel::Severity, void(SecurityEvaluationModel::*)()> m_lSignalMap = {{
       /* UNSUPPORTED   */ nullptr                                           ,
       /* INFORMATION   */ &SecurityEvaluationModel::informationCountChanged ,
       /* WARN1NG       */ &SecurityEvaluationModel::warningCountChanged     ,
       /* ISSUE         */ &SecurityEvaluationModel::issueCountChanged       ,
       /* ERROR         */ &SecurityEvaluationModel::errorCountChanged       ,
       /* FATAL_WARNING */ &SecurityEvaluationModel::fatalWarningCountChanged,
    }};
    
    SecurityEvaluationModelPrivate::SecurityEvaluationModelPrivate(Account* account, SecurityEvaluationModel* parent) :
     QObject(parent),q_ptr(parent), m_pAccount(account),m_isScheduled(false),
     m_CurrentSecurityLevel(SecurityEvaluationModel::SecurityLevel::NONE),m_pAccChecks(nullptr),
     m_SeverityCount{
          /* UNSUPPORTED   */ 0,
          /* INFORMATION   */ 0,
          /* WARN1NG       */ 0,
          /* ISSUE         */ 0,
          /* ERROR         */ 0,
          /* FATAL_WARNING */ 0,
       }
    {
       //Make sure the security level is updated if something change
       QObject::connect(parent,&SecurityEvaluationModel::layoutChanged , this,&SecurityEvaluationModelPrivate::update);
       QObject::connect(parent,&SecurityEvaluationModel::dataChanged   , this,&SecurityEvaluationModelPrivate::update);
       QObject::connect(parent,&SecurityEvaluationModel::rowsInserted  , this,&SecurityEvaluationModelPrivate::update);
       QObject::connect(parent,&SecurityEvaluationModel::rowsRemoved   , this,&SecurityEvaluationModelPrivate::update);
       QObject::connect(parent,&SecurityEvaluationModel::modelReset    , this,&SecurityEvaluationModelPrivate::update);
    }
    
    
    
    /*******************************************************************************
     *                                                                             *
     *                       PrefixAndSeverityProxyModel                           *
     *                                                                             *
     ******************************************************************************/
    
    PrefixAndSeverityProxyModel::PrefixAndSeverityProxyModel(const QString& prefix, QAbstractItemModel* parent) :
       QIdentityProxyModel(parent),m_Name(prefix)
    {
       setSourceModel(parent);
    }
    
    ///It insert a second column with the source name
    int PrefixAndSeverityProxyModel::columnCount( const QModelIndex& parent) const
    {
       Q_UNUSED(parent)
       return parent.isValid() ? 0 : 3;
    }
    
    QModelIndex PrefixAndSeverityProxyModel::index( int row, int column, const QModelIndex& parent) const
    {
       if (column == 2)
          return createIndex(row,column);
       return QIdentityProxyModel::index(row,column,parent);
    }
    
    ///Map items and add elements
    QVariant PrefixAndSeverityProxyModel::data(const QModelIndex& index, int role) const
    {
       if (index.isValid()) {
    
          Certificate::Checks c = Certificate::Checks::HAS_PRIVATE_KEY;
          if (QIdentityProxyModel::data(index,(int)CertificateModel::Role::isCheck).toBool() == true)
             c = qvariant_cast<Certificate::Checks>(QIdentityProxyModel::data(index,(int)CertificateModel::Role::check));
          else if (index.column() != 2) //That column doesn't exist in the source, the won't exist
             return QVariant();
    
          switch (index.column()) {
             case (int)AccountChecksModel::Columns::MESSAGE:
                switch(role) {
                   case Qt::DecorationRole:
                      return GlobalInstances::pixmapManipulator().securityIssueIcon(index);
                   case (int)SecurityEvaluationModel::Role::Severity:
                      return QVariant::fromValue(SecurityEvaluationModelPrivate::certificateFlawSeverity[c]);
                   case (int)SecurityEvaluationModel::Role::SecurityLevel:
                      return QVariant::fromValue(SecurityEvaluationModelPrivate::maximumCertificateSecurityLevel[c]);
                }
                break;
             //
             case (int)AccountChecksModel::Columns::SOURCE: {
                switch(role) {
                   case Qt::DisplayRole:
                      return m_Name;
                   case (int)SecurityEvaluationModel::Role::Severity:
                      return QVariant::fromValue(SecurityEvaluationModelPrivate::certificateFlawSeverity[c]);
                   case (int)SecurityEvaluationModel::Role::SecurityLevel:
                      return QVariant::fromValue(SecurityEvaluationModelPrivate::maximumCertificateSecurityLevel[c]);
                }
                return QVariant();
             }
                break;
             //Map source column 1 to 2
             case (int)AccountChecksModel::Columns::RESULT: {
                const QModelIndex& srcIdx = sourceModel()->index(index.row(),1);
                c = qvariant_cast<Certificate::Checks>(srcIdx.data((int)CertificateModel::Role::check));
    
                switch(role) {
                   case (int)SecurityEvaluationModel::Role::Severity:
                      return QVariant::fromValue(SecurityEvaluationModelPrivate::certificateFlawSeverity[c]);
                   case (int)SecurityEvaluationModel::Role::SecurityLevel:
                      return QVariant::fromValue(SecurityEvaluationModelPrivate::maximumCertificateSecurityLevel[c]);
                }
    
                return srcIdx.data(role);
             }
          }
       }
    
       return QIdentityProxyModel::data(index,role);
    }
    
    
    
    /*******************************************************************************
     *                                                                             *
     *                             AccountChecksModel                              *
     *                                                                             *
     ******************************************************************************/
    
    AccountChecksModel::AccountChecksModel(const Account* a) : QAbstractTableModel(const_cast<Account*>(a)), m_pAccount(a)
    {
       update();
    }
    
    QVariant AccountChecksModel::data( const QModelIndex& index, int role ) const
    {
       if ((!index.isValid())
        || (index.row() < 0)
        || (index.row() >= enum_class_size<SecurityEvaluationModel::AccountSecurityChecks>())
       )
          return QVariant();
    
       const SecurityEvaluationModel::AccountSecurityChecks f = static_cast<SecurityEvaluationModel::AccountSecurityChecks>(index.row());
    
       switch(role) {
          case (int)SecurityEvaluationModel::Role::Severity:
             return QVariant::fromValue(
                m_lCachedResults[f] == Certificate::CheckValues::UNSUPPORTED ?
                   SecurityEvaluationModel::Severity::UNSUPPORTED : SecurityEvaluationModelPrivate::flawSeverity[f]
             );
          case (int)SecurityEvaluationModel::Role::SecurityLevel:
             return QVariant::fromValue(
                //If the check is unsupported then using "COMPLETE" won't affect the algorithm output
                // if n < current then n else current end will always be "current" when n == maximum
                m_lCachedResults[f] == Certificate::CheckValues::UNSUPPORTED ?
                   SecurityEvaluationModel::SecurityLevel::COMPLETE : SecurityEvaluationModelPrivate::maximumSecurityLevel[f]
             );
       }
    
       switch (index.column()) {
          case (int)AccountChecksModel::Columns::MESSAGE:
             switch(role) {
                case Qt::DisplayRole:
                   return SecurityEvaluationModelPrivate::messages[index.row()];
                case Qt::DecorationRole:
                   return GlobalInstances::pixmapManipulator().securityIssueIcon(index);
             };
             break;
          case (int)AccountChecksModel::Columns::SOURCE:
             switch(role) {
                case Qt::DisplayRole:
                   return tr("Configuration");
             };
             break;
          case (int)AccountChecksModel::Columns::RESULT:
             switch(role) {
                case Qt::DisplayRole:
                   if (m_lCachedResults[f] != Certificate::CheckValues::UNSUPPORTED)
                      return m_lCachedResults[f] == Certificate::CheckValues::PASSED ? true : false;
                   break;
             };
             break;
       };
    
       return QVariant();
    }
    
    int AccountChecksModel::rowCount( const QModelIndex& parent ) const
    {
       return parent.isValid() ? 0 : enum_class_size<SecurityEvaluationModel::AccountSecurityChecks>();
    }
    
    int AccountChecksModel::columnCount( const QModelIndex& parent ) const
    {
       return parent.isValid() ? 0 : enum_class_size<AccountChecksModel::Columns>();
    }
    
    Qt::ItemFlags AccountChecksModel::flags( const QModelIndex& index) const
    {
       Q_UNUSED(index)
       return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
    }
    
    bool AccountChecksModel::setData( const QModelIndex& index, const QVariant &value, int role)
    {
       Q_UNUSED(index)
       Q_UNUSED(value)
       Q_UNUSED(role )
       return false;
    }
    
    QHash<int,QByteArray> AccountChecksModel::roleNames() const
    {
       return {};
    }
    
    //This could have been an inlined function too
    #define SET_CHECK_VALUE(check,condition) {Certificate::CheckValues c = Certificate::CheckValues::UNSUPPORTED;\
       bool isSet = m_lCachedResults.isSet(check); if (isSet) c = m_lCachedResults[check]; m_lCachedResults.setAt( check ,\
       condition? Certificate::CheckValues::PASSED : Certificate::CheckValues::FAILED);\
       changed |= (!isSet) || c != m_lCachedResults[check];}
    
    void AccountChecksModel::update()
    {
       bool changed = false;
    
       // AccountSecurityChecks::SRTP_DISABLED
       SET_CHECK_VALUE(SecurityEvaluationModel::AccountSecurityChecks::SRTP_ENABLED                  ,
          m_pAccount->isSrtpEnabled                () || m_pAccount->protocol() == Account::Protocol::RING
       );
    
       // AccountSecurityChecks::TLS_DISABLED
       SET_CHECK_VALUE( SecurityEvaluationModel::AccountSecurityChecks::TLS_ENABLED                  ,
          m_pAccount->isTlsEnabled                 ()
       );
    
       // AccountSecurityChecks::CERTIFICATE_MISMATCH
       m_lCachedResults.setAt( SecurityEvaluationModel::AccountSecurityChecks::CERTIFICATE_MATCH     ,
          Certificate::CheckValues::UNSUPPORTED); //TODO
    
       // AccountSecurityChecks::OUTGOING_SERVER_MISMATCH
       m_lCachedResults.setAt( SecurityEvaluationModel::AccountSecurityChecks::OUTGOING_SERVER_MATCH ,
          Certificate::CheckValues::UNSUPPORTED); //TODO
    
       // AccountSecurityChecks::VERIFY_INCOMING_DISABLED
       SET_CHECK_VALUE( SecurityEvaluationModel::AccountSecurityChecks::VERIFY_INCOMING_ENABLED      ,
          m_pAccount->isTlsVerifyServer            ()
       );
    
       // AccountSecurityChecks::VERIFY_ANSWER_DISABLED
       SET_CHECK_VALUE( SecurityEvaluationModel::AccountSecurityChecks::VERIFY_ANSWER_ENABLED        ,
          m_pAccount->isTlsVerifyClient            ()
       );
    
       // AccountSecurityChecks::REQUIRE_CERTIFICATE_DISABLED
       SET_CHECK_VALUE( SecurityEvaluationModel::AccountSecurityChecks::REQUIRE_CERTIFICATE_ENABLED  ,
          m_pAccount->isTlsRequireClientCertificate()
       );
    
       // AccountSecurityChecks::MISSING_CERTIFICATE
       SET_CHECK_VALUE( SecurityEvaluationModel::AccountSecurityChecks::NOT_MISSING_CERTIFICATE      ,
          m_pAccount->tlsCertificate               ()
       );
    
       // AccountSecurityChecks::MISSING_AUTHORITY
       SET_CHECK_VALUE( SecurityEvaluationModel::AccountSecurityChecks::NOT_MISSING_AUTHORITY        ,
          m_pAccount->tlsCaListCertificate         ()
       );
    
       if (changed)
          emit dataChanged(index(0,2),index(rowCount()-1,2));
    }
    #undef SET_CHECK_VALUE
    
    
    /*******************************************************************************
     *                                                                             *
     *                          CombinaisonProxyModel                              *
     *                                                                             *
     ******************************************************************************/
    
    CombinaisonProxyModel::CombinaisonProxyModel(QAbstractItemModel* publicCert,
                                                 QAbstractItemModel* caCert    ,
                                                 QAbstractItemModel* account   ,
                                                 QObject*            parent    )
     : QAbstractTableModel(parent), m_lSources({publicCert,caCert,account})
    {
       for (int i = 0; i < m_lSources.size(); i++) {
          const QAbstractItemModel* m = m_lSources[i];
          if (m) {
             connect(m, &QAbstractItemModel::dataChanged, [this,i](const QModelIndex& tl, const QModelIndex& br) {
    
                int offset =0;
                for (int j = 0; j < i;j++)
                   offset += sizes[j];
    
    
                emit this->dataChanged(this->index(offset+tl.row(), br.column()), this->index(offset+br.row(), br.column()));
             });
          }
       }
    }
    
    QVariant CombinaisonProxyModel::data( const QModelIndex& index, int role) const
    {
       const QAbstractItemModel* src = m_lSources[toModelIdx(index.row())];
    
       //Role::Severity will give ::UNSUPPORTED (aka, 0) if a model is missing
       //this is done on purpose
    
       //All "groups" will have empty items for unsupported checks
    
       return index.isValid() && src ? src->index(fromFinal(index.row()),index.column()).data(role) : QVariant();
    }
    
    int CombinaisonProxyModel::rowCount( const QModelIndex& parent) const
    {
       return parent.isValid() ? 0 : totalSize();
    }
    
    int CombinaisonProxyModel::columnCount( const QModelIndex& parent) const
    {
       return parent.isValid() ? 0 : 3;
    }
    
    Qt::ItemFlags CombinaisonProxyModel::flags( const QModelIndex& index) const
    {
       Q_UNUSED(index)
       return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
    }
    
    bool CombinaisonProxyModel::setData( const QModelIndex& index, const QVariant &value, int role)
    {
       Q_UNUSED(index)
       Q_UNUSED(value)
       Q_UNUSED(role )
       return false;
    }
    
    QHash<int,QByteArray> CombinaisonProxyModel::roleNames() const
    {
       return {};
    }
    
    
    
    /*******************************************************************************
     *                                                                             *
     *                           SecurityEvaluationModel                           *
     *                                                                             *
     ******************************************************************************/
    
    SecurityEvaluationModel::SecurityEvaluationModel(Account* account) : QSortFilterProxyModel(account),
    d_ptr(new SecurityEvaluationModelPrivate(account,this))
    {
       Certificate* caCert = d_ptr->m_pAccount->tlsCaListCertificate ();
       Certificate* pkCert = d_ptr->m_pAccount->tlsCertificate       ();
    
       SecurityEvaluationModelPrivate::getCertificateSeverityProxy(caCert);
       SecurityEvaluationModelPrivate::getCertificateSeverityProxy(pkCert);
    
       d_ptr->m_pAccChecks = new AccountChecksModel(account);
    
       d_ptr->update();
    
       setSourceModel(new CombinaisonProxyModel(pkCert ? pkCert->d_ptr->m_pSeverityProxy : nullptr, caCert ? caCert->d_ptr->m_pSeverityProxy : nullptr, d_ptr->m_pAccChecks,this));
    
       setSortRole((int)Role::Severity);
    }
    
    SecurityEvaluationModel::~SecurityEvaluationModel()
    {
       delete d_ptr;
    }
    
    bool SecurityEvaluationModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const
    {
       const QModelIndex& idx  = sourceModel()->index(source_row,0,source_parent);
       const QModelIndex& idx2 = sourceModel()->index(source_row,2,source_parent);
       const Severity     s    = qvariant_cast<Severity>(idx.data((int)SecurityEvaluationModel::Role::Severity));
       return s != Severity::UNSUPPORTED && idx2.data(Qt::DisplayRole).toBool() == false;
    }
    
    QHash<int,QByteArray> SecurityEvaluationModel::roleNames() const
    {
       static QHash<int, QByteArray> roles = QAbstractItemModel::roleNames();
       static bool initRoles = false;
       if (!initRoles) {
          initRoles = true;
          roles[(int)Role::Severity] = "Severity";
       }
       return roles;
    }
    
    void SecurityEvaluationModelPrivate::update()
    {
       //As this can be called multiple time, only perform the checks once per event loop cycle
       if (!m_isScheduled) {
          m_pAccChecks->update();
    
    #if QT_VERSION >= 0x050400
          QTimer::singleShot(0,this,&SecurityEvaluationModelPrivate::updateReal);
          m_isScheduled = true;
    #else //Too bad for Qt < 5.3 users
          updateReal();
    #endif
    
       }
    }
    
    QAbstractItemModel* SecurityEvaluationModelPrivate::getCertificateSeverityProxy(Certificate* c)
    {
       if (!c)
          return nullptr;
    
       if (!c->d_ptr->m_pSeverityProxy)
          c->d_ptr->m_pSeverityProxy = new PrefixAndSeverityProxyModel(tr("Authority" ),c->checksModel());
    
       return c->d_ptr->m_pSeverityProxy;
    }
    
    SecurityEvaluationModel::SecurityLevel SecurityEvaluationModelPrivate::maxSecurityLevel(QAbstractItemModel* m, int* counter)
    {
       typedef SecurityEvaluationModel::Severity      Severity     ;
       typedef SecurityEvaluationModel::SecurityLevel SecurityLevel;
    
       SecurityLevel maxLevel = SecurityLevel::COMPLETE;
    
       for (int i=0; i < m->rowCount();i++) {
          const QModelIndex&  idx      = m->index(i,0);
    
          const Severity      severity = qvariant_cast<Severity>(
             idx.data((int) SecurityEvaluationModel::Role::Severity)
          );
    
          //Ignore items without severity
          const QVariant levelVariant = idx.data((int) SecurityEvaluationModel::Role::SecurityLevel );
    
          const SecurityLevel level    = levelVariant.canConvert<SecurityLevel>() ? qvariant_cast<SecurityLevel>(
             levelVariant
          ) : maxLevel;
    
          //Increment the count
          if (counter)
             counter[static_cast<int>(severity)]++;
    
          const bool forceIgnore = idx.data((int)CertificateModel::Role::requirePrivateKey).toBool();
    
          //Update the maximum level
          maxLevel = level < maxLevel && !forceIgnore ? level : maxLevel;
       }
    
       return maxLevel;
    }
    
    void SecurityEvaluationModelPrivate::updateReal()
    {
       typedef SecurityEvaluationModel::Severity      Severity     ;
       typedef SecurityEvaluationModel::SecurityLevel SecurityLevel;
    
       int countCache[enum_class_size<SecurityEvaluationModel::Severity>()];
    
       //Reset the counter
       for (const Severity s : EnumIterator<Severity>()) {
          countCache     [(int)s] = m_SeverityCount[(int)s];
          m_SeverityCount[(int)s] = 0                      ;
       }
    
       SecurityLevel maxLevel = maxSecurityLevel(q_ptr, m_SeverityCount);
    
       //Notify
       for (const Severity s : EnumIterator<Severity>()) {
          if (countCache[(int)s] != m_SeverityCount[(int)s] && m_lSignalMap[s])
             (q_ptr->*m_lSignalMap[s])();
       }
    
       //Update the security level
       if (m_CurrentSecurityLevel != maxLevel) {
          m_CurrentSecurityLevel = maxLevel;
    
          emit q_ptr->securityLevelChanged();
       }
    
       m_isScheduled = false;
    }
    
    QModelIndex SecurityEvaluationModel::getIndex(const SecurityFlaw* flaw)
    {
       return index(flaw->d_ptr->m_Row,0);
    }
    
    QList<SecurityFlaw*> SecurityEvaluationModel::currentFlaws()
    {
       return d_ptr->m_lCurrentFlaws;
    }
    
    SecurityEvaluationModel::SecurityLevel SecurityEvaluationModel::securityLevel() const
    {
       return d_ptr->m_CurrentSecurityLevel;
    }
    
    SecurityEvaluationModel::SecurityLevel SecurityEvaluationModelPrivate::certificateSecurityLevel(const Certificate* c, bool forceIgnorePrivateKey)
    {
       typedef SecurityEvaluationModel::SecurityLevel SecurityLevel;
    
       SecurityLevel maxLevelWithPriv    = SecurityLevel::COMPLETE;
       SecurityLevel maxLevelWithoutPriv = SecurityLevel::COMPLETE;
    
       const bool ignorePrivateKey = forceIgnorePrivateKey || (c->requirePrivateKey() == false);
    
       if (c->d_ptr->m_hasLoadedSecurityLevel) {
          if (ignorePrivateKey)
             return c->d_ptr->m_SecurityLevelWithoutPriv;
          else
             return c->d_ptr->m_SecurityLevelWithPriv;
       }
    
       for (const Certificate::Checks check : EnumIterator<Certificate::Checks>()) {
          const bool relevant = relevantWithoutPrivateKey[check];
          if (c->checkResult(check) == Certificate::CheckValues::FAILED) {
             if (relevant) {
                const SecurityLevel checkLevel = maximumCertificateSecurityLevel[check];
                maxLevelWithoutPriv = checkLevel < maxLevelWithoutPriv ? checkLevel : maxLevelWithoutPriv;
             }
             const SecurityLevel checkLevel = maximumCertificateSecurityLevel[check];
             maxLevelWithPriv = checkLevel < maxLevelWithPriv ? checkLevel : maxLevelWithPriv;
          }
       }
    
       c->d_ptr->m_hasLoadedSecurityLevel   = true;
       c->d_ptr->m_SecurityLevelWithoutPriv = maxLevelWithoutPriv;
       c->d_ptr->m_SecurityLevelWithPriv    = maxLevelWithPriv;
    
       return ignorePrivateKey ? c->d_ptr->m_SecurityLevelWithoutPriv : c->d_ptr->m_SecurityLevelWithPriv;
    }
    
    //Map the array to getters
    int SecurityEvaluationModel::informationCount             () const
    { return d_ptr->m_SeverityCount[ (int)Severity::INFORMATION   ]; }
    int SecurityEvaluationModel::warningCount                 () const
    { return d_ptr->m_SeverityCount[ (int)Severity::WARNING       ]; }
    int SecurityEvaluationModel::issueCount                   () const
    { return d_ptr->m_SeverityCount[ (int)Severity::ISSUE         ]; }
    int SecurityEvaluationModel::errorCount                   () const
    { return d_ptr->m_SeverityCount[ (int)Severity::ERROR         ]; }
    int SecurityEvaluationModel::fatalWarningCount            () const
    { return d_ptr->m_SeverityCount[ (int)Severity::FATAL_WARNING ]; }
    
    #include <securityevaluationmodel.moc>