Allow to add and revoke user identities.

Using a popup with required parameters.
This commit is contained in:
SET
2020-11-19 19:16:17 +01:00
parent 43e1eff221
commit bb174075df
15 changed files with 430 additions and 11 deletions

View File

@@ -38,6 +38,7 @@ mutex gs_fileWriteMutex;
"canEditUidValidity" : true,
"canEditExpiryTime" : true,
"canCreateKeys" : true,
"canAddRevokeUids" : true,
"privKeyIds" : [
"fullKeyId1",
"fullKeyId2"
@@ -172,6 +173,17 @@ bool AppConfig::CanCreateKeys() const
return cnObject.get("canCreateKeys");
}
bool AppConfig::CanAddRevokeUid() const
{
const WString commonName = GetSubjectDnAttribute(WSslCertificate::DnAttributeName::CommonName);
if (!m_SubjectCNObject.contains(commonName.toUTF8()))
return false;
Json::Object cnObject = m_SubjectCNObject.get(commonName.toUTF8());
if (!cnObject.contains("canAddRevokeUids"))
return false;
return cnObject.get("canAddRevokeUids");
}
bool AppConfig::UpdateSecretKeyOwnership(const WString& fpr, bool own)
{
const WString commonName = GetSubjectDnAttribute(WSslCertificate::DnAttributeName::CommonName);

View File

@@ -67,6 +67,12 @@ public:
* @return
*/
bool CanCreateKeys() const;
/**
* Allows to add or revoke a user identity to a key. It deos not mean
* deleting an identity.
* @return
*/
bool CanAddRevokeUid() const;
/**
* Disown the user of the secret key, or grants him ownership. It just adds
* or remove fpr from the private key array, without any further check. The

View File

@@ -314,7 +314,7 @@ void K7Main::OnKeyAnchorClicked(WAnchor * source)
DisplaySubKeys(id, secret);
if (m_config->CanDelete()) // m_btnDelete is NULL otherwise
m_btnDelete->setHidden(!m_keyringIO->CanKeyBeDeleted(id));
m_keyringIO->PrepareExport(id, secret);
}
@@ -340,20 +340,28 @@ void K7Main::DisplayUids(const WString& fullKeyID, bool secret)
rootNode->setChildCountPolicy(ChildCountPolicy::Enabled);
m_ttbUids->setTreeRoot(unique_ptr<WTreeTableNode> (rootNode), TR("UIDs"));
rootNode->expand();
vector<WString> privateKeys = m_config->PrivateKeyIds();
vector<WString> ourKeys = m_config->PrivateKeyIds();
bool canAddRevokeUid = m_config->CanAddRevokeUid()
&& Tools::KeyHasSecret(k.primaryFingerprint())
&& Tools::IsOurKey(k.primaryFingerprint(), ourKeys);
for (uint i = 0; i < k.numUserIDs(); i++)
{
UserID uid = k.userID(i);
WTreeTableNode * uidNode = new WTreeTableNode(uid.name());
uidNode->setToolTip(Tools::GetUidStatus(uid));
TreeTableNodeText * ttntUidEmail = new TreeTableNodeText(uid.email(), uidNode, 1);
uidNode->setColumnWidget(1, unique_ptr<TreeTableNodeText> (ttntUidEmail));
WText * lblUidEmail = new WText(uid.email());
if (canAddRevokeUid)
{
lblUidEmail->setToolTip(TR("TTTDoubleCLick"));
lblUidEmail->doubleClicked().connect(std::bind(&KeyEdit::OnUidEmailClicked, m_keyEdit, uidNode, WString(k.primaryFingerprint())));
}
uidNode->setColumnWidget(1, unique_ptr<WText> (lblUidEmail));
// Show key certify popup on double click
WText * lblUidValidity = new WText(UidValidities[uid.validity()]);
if (m_config->CanEditUidValidity())
{
lblUidValidity->setToolTip(TR("TTTDoubleCLick"));
lblUidValidity->doubleClicked().connect(std::bind(&KeyEdit::OnUidValidityClicked, m_keyEdit, uidNode, privateKeys, WString(k.primaryFingerprint())));
lblUidValidity->doubleClicked().connect(std::bind(&KeyEdit::OnUidValidityClicked, m_keyEdit, uidNode, ourKeys, WString(k.primaryFingerprint())));
}
uidNode->setColumnWidget(2, unique_ptr<WText> (lblUidValidity));
TreeTableNodeText * ttntUidComment = new TreeTableNodeText(uid.comment(), uidNode, 3);

View File

@@ -22,6 +22,7 @@ KeyEdit::KeyEdit(K7Main * owner)
m_owner = owner;
m_popupUid = NULL;
m_popupExpiryTime = NULL;
m_popupAddUid = NULL;
m_targetUidValidityKeyFpr = WString::Empty;
m_expiryEditedKeyFpr = WString::Empty;
}
@@ -200,3 +201,60 @@ void KeyEdit::SetExpiryTime()
m_owner->DisplaySubKeys(m_expiryEditedKeyFpr, true);
}
void KeyEdit::OnUidEmailClicked(WTreeTableNode* uidNode, const WString& keyFpr)
{
WText * lblEmail = static_cast<WText*> (uidNode->columnWidget(1));
if (keyFpr != m_addUidKeyFpr)
{
delete m_popupAddUid;
m_popupAddUid = new PopupAddUid(lblEmail, m_owner->m_tmwMessage);
m_popupAddUid->Create();
m_addUidKeyFpr = keyFpr;
m_popupAddUid->GetApplyButton()->clicked().connect(this, &KeyEdit::AddOrRevokeUid);
}
WText * lblName = uidNode->label();
WText * lblComment = static_cast<WText*> (uidNode->columnWidget(3));
m_popupAddUid->SetNodeIdentity(lblName->text(), lblEmail->text(),
lblComment->text());
m_popupAddUid->show();
}
void KeyEdit::AddOrRevokeUid()
{
if (!m_popupAddUid->Validate())
{
m_owner->m_tmwMessage->SetText(TR("InvalidInput"));
return;
}
const WString name = m_popupAddUid->GetName();
const WString email = m_popupAddUid->GetEmail();
const WString comment = m_popupAddUid->GetComment();
const WString passphrase = m_popupAddUid->GetPassphrase();
Error e;
GpgMEWorker gpgw;
if (m_popupAddUid->WhatToDo() == PopupAddUid::What::Revoke)
{
e = gpgw.RevokeUserID(m_addUidKeyFpr.toUTF8().c_str(),
passphrase.toUTF8(),
name.toUTF8(), email.toUTF8(), comment.toUTF8());
}
else
{
e = gpgw.AddUserID(m_addUidKeyFpr.toUTF8().c_str(),
passphrase.toUTF8(),
name.toUTF8(), email.toUTF8(), comment.toUTF8());
}
m_popupAddUid->hide();
if (e.code() != 0)
{
m_popupAddUid->ShowPassphrase(true);
m_owner->m_tmwMessage->SetText(e.asString());
}
else
{
m_popupAddUid->ShowPassphrase(false);
}
// Key certifications are not listed on this refresh !
m_owner->DisplayUids(m_addUidKeyFpr, true);
}

View File

@@ -15,17 +15,19 @@
#include <Wt/WComboBox.h>
#include "PopupCertifyUserId.h"
#include "PopupExpiryTime.h"
#include "PopupAddUid.h"
using namespace Wt;
class K7Main;
/**
* Some key editing functionalities are or will be implemented here. For now,
* only owner trust level and key certification are implemented.
* Is a pseudo-extension of K7Main. Both classes are friends to each other, and
* Some key editing functionalities are or will be implemented here.
* Owner trust level, key certification adding and revoking user identities are
* implemented.
* \n Is a pseudo-extension of K7Main. Both classes are friends to each other, and
* everything is private here.
* Does not manage keyring.
* \n Does not manage keyring.
*/
class KeyEdit : public WObject
{
@@ -43,6 +45,9 @@ private:
PopupExpiryTime * m_popupExpiryTime;
WString m_expiryEditedKeyFpr;
PopupAddUid * m_popupAddUid;
WString m_addUidKeyFpr;
/**
* Unknown is common.
* \n If keyHasSecret is true, show only Ultimate level.
@@ -54,6 +59,7 @@ private:
void FillOwnerTrustCombo(WComboBox * cmb, bool keyHasSecret);
void CertifyKey();
void SetExpiryTime();
void AddOrRevokeUid();
/**
* Shows a combobox with all trust levels
@@ -81,6 +87,8 @@ private:
*/
void OnExpiryClicked(WTreeTableNode * subkeyNode, const WString& keyFpr);
void OnUidEmailClicked(WTreeTableNode * uidNode, const WString& keyFpr);
};
#endif /* KEYEDIT_H */

161
PopupAddUid.cpp Normal file
View File

@@ -0,0 +1,161 @@
/*
* File: PopupAddUid.cpp
* Author: SET - nmset@yandex.com
* License : GPL v2
* Copyright SET - © 2019
*
* Created on November 16, 2020, 3:59 PM
*/
#include "PopupAddUid.h"
#include "global.h"
#include <Wt/WHBoxLayout.h>
#include <Wt/WVBoxLayout.h>
#include <Wt/WGridLayout.h>
#include <Wt/WRadioButton.h>
#include <Wt/WRegExpValidator.h>
PopupAddUid::PopupAddUid(WWidget * anchorWidget,
TransientMessageWidget * txtMessage,
const WLength& width)
: WPopupWidget(cpp14::make_unique<WContainerWidget>())
{
m_tmwMessage = txtMessage;
m_cwMain = NULL;
m_leName = NULL;
m_leEmail = NULL;
m_leComment = NULL;
m_lePassphrase = NULL;
m_btnApply = NULL;
m_nodeName = WString::Empty;
m_nodeEmail = WString::Empty;
m_nodeComment = WString::Empty;
setTransient(true);
setAnchorWidget(anchorWidget);
setWidth(width);
}
PopupAddUid::~PopupAddUid()
{
}
void PopupAddUid::Create()
{
m_cwMain = static_cast<WContainerWidget*> (implementation());
m_cwMain->setStyleClass("popup");
WVBoxLayout * vblMain = new WVBoxLayout();
m_cwMain->setLayout(unique_ptr<WVBoxLayout> (vblMain));
WGridLayout * grlMain = new WGridLayout();
grlMain->setColumnStretch(1, 1);
vblMain->addLayout(unique_ptr<WGridLayout> (grlMain));
WText * lblName = new WText(TR("Name"));
grlMain->addWidget(unique_ptr<WText> (lblName), 0, 0);
m_leName = new WLineEdit();
grlMain->addWidget(unique_ptr<WLineEdit> (m_leName), 0, 1);
WText * lblEmail = new WText(TR("Email"));
grlMain->addWidget(unique_ptr<WText> (lblEmail), 1, 0);
m_leEmail = new WLineEdit();
grlMain->addWidget(unique_ptr<WLineEdit> (m_leEmail), 1, 1);
WText * lblComment = new WText(TR("Comment"));
grlMain->addWidget(unique_ptr<WText> (lblComment), 2, 0);
m_leComment = new WLineEdit();
grlMain->addWidget(unique_ptr<WLineEdit> (m_leComment), 2, 1);
m_lblPassphrase = new WText(TR("Passphrase"));
grlMain->addWidget(unique_ptr<WText> (m_lblPassphrase), 3, 0);
m_lePassphrase = new WLineEdit();
m_lePassphrase->setEchoMode(EchoMode::Password);
grlMain->addWidget(unique_ptr<WLineEdit> (m_lePassphrase), 3, 1);
WHBoxLayout * hblWhat = new WHBoxLayout();
WRadioButton * rbAdd = new WRadioButton(WString(TR("AddUid")));
hblWhat->addWidget(unique_ptr<WRadioButton> (rbAdd));
WRadioButton * rbRevoke = new WRadioButton(WString(TR("RevokeUid")));
hblWhat->addWidget(unique_ptr<WRadioButton> (rbRevoke));
m_bgWhat = make_shared<WButtonGroup>();
m_bgWhat->addButton(rbAdd, What::Add);
m_bgWhat->addButton(rbRevoke, What::Revoke);
m_bgWhat->setCheckedButton(rbAdd);
vblMain->addLayout(unique_ptr<WHBoxLayout> (hblWhat));
m_cbConfirm = new WCheckBox(TR("Confirm"));
vblMain->addWidget(unique_ptr<WCheckBox> (m_cbConfirm));
WHBoxLayout * hblButtons = new WHBoxLayout();
WPushButton * btnClose = new WPushButton(TR("Close"));
hblButtons->addWidget(unique_ptr<WPushButton> (btnClose));
m_btnApply = new WPushButton(TR("Apply"));
hblButtons->addWidget(unique_ptr<WPushButton> (m_btnApply));
vblMain->addLayout(unique_ptr<WHBoxLayout> (hblButtons));
m_bgWhat->checkedChanged().connect(this, &PopupAddUid::OnButtonGroupWhat);
btnClose->clicked().connect(this, &WPopupWidget::hide);
this->hidden().connect(m_cbConfirm, &WCheckBox::setUnChecked);
// From WRegExpValidator docs
shared_ptr<WRegExpValidator> validator
= make_shared<WRegExpValidator>
("[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}");
validator->setMandatory(true);
m_leEmail->setValidator(validator);
}
void PopupAddUid::ShowPassphrase(bool show)
{
// See comments in PopupCertifyUserId::ShowPassphrase
if (show)
{
m_lblPassphrase->show();
m_lePassphrase->show();
m_lePassphrase->setText(WString::Empty);
}
else
{
m_lblPassphrase->hide();
m_lePassphrase->hide();
}
}
void PopupAddUid::OnButtonGroupWhat(WRadioButton * btn)
{
m_leName->setDisabled(m_bgWhat->checkedId() == What::Revoke);
m_leEmail->setDisabled(m_bgWhat->checkedId() == What::Revoke);
m_leComment->setDisabled(m_bgWhat->checkedId() == What::Revoke);
m_leName->setText(m_nodeName);
if (m_bgWhat->checkedId() == What::Revoke)
{
m_leEmail->setText(m_nodeEmail);
m_leComment->setText(m_nodeComment);
}
else
{
m_leEmail->setText(WString::Empty);
m_leComment->setText(WString::Empty);
}
m_cbConfirm->setUnChecked();
}
void PopupAddUid::SetNodeIdentity(const WString& name, const WString& email,
const WString& comment)
{
m_nodeName = name;
m_nodeEmail = email;
m_nodeComment = comment;
OnButtonGroupWhat(m_bgWhat->checkedButton());
}
bool PopupAddUid::Validate() const
{
/*
* It's pointless to check if a passphrase is provided as the loopback
* passphrase provider won't ever be called as long as gpg-agent holds a
* valid passphrase from a previous transaction.
*/
if (m_bgWhat->checkedId() == What::Revoke)
return (m_cbConfirm->isChecked());
return (m_cbConfirm->isChecked()
&& (m_leEmail->validate() == ValidationState::Valid));
}

124
PopupAddUid.h Normal file
View File

@@ -0,0 +1,124 @@
/*
* File: PopupAddUid.h
* Author: SET - nmset@yandex.com
* License : GPL v2
* Copyright SET - © 2019
*
* Created on November 16, 2020, 3:59 PM
*/
#ifndef POPUPADDUID_H
#define POPUPADDUID_H
#include <Wt/WPopupWidget.h>
#include <Wt/WContainerWidget.h>
#include <Wt/WText.h>
#include <Wt/WLineEdit.h>
#include <Wt/WCheckBox.h>
#include <Wt/WPushButton.h>
#include <Wt/WButtonGroup.h>
#include "TransientMessageWidget.h"
using namespace Wt;
class PopupAddUid : public WPopupWidget
{
public:
enum What
{
Revoke = 0, Add
};
PopupAddUid(WWidget * anchorWidget, TransientMessageWidget * txtMessage,
const WLength& width = 400);
virtual ~PopupAddUid();
void Create();
const WString GetName() const
{
return m_leName->text();
}
/**
* GPGME enforces a rightly formatted email address here.
* @return
*/
const WString GetEmail() const
{
return m_leEmail->text();
}
const WString GetComment() const
{
return m_leComment->text();
}
/**
* Add or revoke uid.
* @return
*/
const What WhatToDo() const
{
return (What) m_bgWhat->checkedId();
}
/**
* Controls visibility of passphrase widgets.
* \n Need not be always visible as the passphrase is cached by gpg-agent.
* \n During that caching period, a wrong input passphrase will not be looked
* for by GPG engine, and may be confusing.
* @param show
*/
void ShowPassphrase(bool show = true);
/**
* Used to forward the passphrase to the loopback passphrase provider.
* @return
*/
const string GetPassphrase()
{
return m_lePassphrase->text().toUTF8();
}
/**
* Caller binds its function here.
* @return
*/
WPushButton* GetApplyButton()
{
return m_btnApply;
}
/**
* Identity values from the tree table node. Should not be modified here
* once assigned. Only KeyEdit::OnUidEmailClicked should do that.
* @param name
* @param email
* @param comment
*/
void SetNodeIdentity(const WString& name, const WString& email,
const WString& comment);
/**
* Confirmation is mandatory.
* \n If adding a uid, a rightly formatted email address is required.
* @return
*/
bool Validate() const;
private:
TransientMessageWidget * m_tmwMessage;
WContainerWidget * m_cwMain;
WLineEdit * m_leName;
WLineEdit * m_leEmail;
WLineEdit * m_leComment;
WText * m_lblPassphrase;
WLineEdit * m_lePassphrase;
shared_ptr<WButtonGroup> m_bgWhat;
WCheckBox * m_cbConfirm;
WPushButton * m_btnApply;
// Identity values from the tree table node.
WString m_nodeName, m_nodeEmail, m_nodeComment;
void OnButtonGroupWhat(WRadioButton * btn);
};
#endif /* POPUPADDUID_H */

View File

@@ -2,9 +2,8 @@
It is developed in C++ as a NetBeans project on the [WebToolkit](https://www.webtoolkit.eu/)(Wt) libraries.
It allows to view, import, export, create, delete keys. Certification trust level, secret key expiry date can also be changed, and user identities can be certified.
It allows to view, import, export, create, delete keys. Certification trust level, secret key expiry date can also be changed, and user identities can be added, revoked and certified.
Export concerns public keys only; secret keys cannot be technically exported on a web server.
Adding user identities is not (yet) implemented.
Available keys can then be used by other Wt applications, or web applications based on other libraries, to encrypt and sign data. As such, it suits my personal needs.

View File

@@ -132,4 +132,9 @@
<message id='Export'>Export</message>
<message id='TTTExport'>Exporting secret keys is technically impossible</message>
<message id='AddUid'>Add an identity</message>
<message id='RevokeUid'>Revoke identity</message>
<message id='InvalidInput'>Input is invalid</message>
</messages>

View File

@@ -132,4 +132,9 @@
<message id='Export'>Exporter</message>
<message id='TTTExport'>L'export des clés secrètes est techniquement impossible</message>
<message id='AddUid'>Ajouter une identité</message>
<message id='RevokeUid'>Revoquer une identité</message>
<message id='InvalidInput'>Saisie invalide</message>
</messages>

View File

@@ -7,6 +7,7 @@
"canEditUidValidity" : true,
"canEditExpiryTime" : true,
"canCreateKeys" : true,
"canAddRevokeUids" : true,
"privKeyIds" : [
"FullKeyId1",
"FullKeyId2"

View File

@@ -42,6 +42,7 @@ OBJECTFILES= \
${OBJECTDIR}/KeyEdit.o \
${OBJECTDIR}/KeyringIO.o \
${OBJECTDIR}/LoopbackPassphraseProvider.o \
${OBJECTDIR}/PopupAddUid.o \
${OBJECTDIR}/PopupCertifyUserId.o \
${OBJECTDIR}/PopupCreate.o \
${OBJECTDIR}/PopupDeleter.o \
@@ -112,6 +113,11 @@ ${OBJECTDIR}/LoopbackPassphraseProvider.o: LoopbackPassphraseProvider.cpp
${RM} "$@.d"
$(COMPILE.cc) -O2 -s -DLARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64 -I/usr/local/Wt/include -I/usr/include/gpgme++ -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/LoopbackPassphraseProvider.o LoopbackPassphraseProvider.cpp
${OBJECTDIR}/PopupAddUid.o: PopupAddUid.cpp
${MKDIR} -p ${OBJECTDIR}
${RM} "$@.d"
$(COMPILE.cc) -O2 -s -DLARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64 -I/usr/local/Wt/include -I/usr/include/gpgme++ -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/PopupAddUid.o PopupAddUid.cpp
${OBJECTDIR}/PopupCertifyUserId.o: PopupCertifyUserId.cpp
${MKDIR} -p ${OBJECTDIR}
${RM} "$@.d"

View File

@@ -42,6 +42,7 @@ OBJECTFILES= \
${OBJECTDIR}/KeyEdit.o \
${OBJECTDIR}/KeyringIO.o \
${OBJECTDIR}/LoopbackPassphraseProvider.o \
${OBJECTDIR}/PopupAddUid.o \
${OBJECTDIR}/PopupCertifyUserId.o \
${OBJECTDIR}/PopupCreate.o \
${OBJECTDIR}/PopupDeleter.o \
@@ -112,6 +113,11 @@ ${OBJECTDIR}/LoopbackPassphraseProvider.o: LoopbackPassphraseProvider.cpp
${RM} "$@.d"
$(COMPILE.cc) -g -DDEVTIME -I/usr/local/Wt-Debug/include -I/usr/include/gpgme++ -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/LoopbackPassphraseProvider.o LoopbackPassphraseProvider.cpp
${OBJECTDIR}/PopupAddUid.o: PopupAddUid.cpp
${MKDIR} -p ${OBJECTDIR}
${RM} "$@.d"
$(COMPILE.cc) -g -DDEVTIME -I/usr/local/Wt-Debug/include -I/usr/include/gpgme++ -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/PopupAddUid.o PopupAddUid.cpp
${OBJECTDIR}/PopupCertifyUserId.o: PopupCertifyUserId.cpp
${MKDIR} -p ${OBJECTDIR}
${RM} "$@.d"

View File

@@ -42,6 +42,7 @@ OBJECTFILES= \
${OBJECTDIR}/KeyEdit.o \
${OBJECTDIR}/KeyringIO.o \
${OBJECTDIR}/LoopbackPassphraseProvider.o \
${OBJECTDIR}/PopupAddUid.o \
${OBJECTDIR}/PopupCertifyUserId.o \
${OBJECTDIR}/PopupCreate.o \
${OBJECTDIR}/PopupDeleter.o \
@@ -112,6 +113,11 @@ ${OBJECTDIR}/LoopbackPassphraseProvider.o: LoopbackPassphraseProvider.cpp
${RM} "$@.d"
$(COMPILE.cc) -O2 -s -I/usr/local/Wt/include -I/usr/include/gpgme++ -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/LoopbackPassphraseProvider.o LoopbackPassphraseProvider.cpp
${OBJECTDIR}/PopupAddUid.o: PopupAddUid.cpp
${MKDIR} -p ${OBJECTDIR}
${RM} "$@.d"
$(COMPILE.cc) -O2 -s -I/usr/local/Wt/include -I/usr/include/gpgme++ -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/PopupAddUid.o PopupAddUid.cpp
${OBJECTDIR}/PopupCertifyUserId.o: PopupCertifyUserId.cpp
${MKDIR} -p ${OBJECTDIR}
${RM} "$@.d"

View File

@@ -11,6 +11,7 @@
<itemPath>KeyEdit.h</itemPath>
<itemPath>KeyringIO.h</itemPath>
<itemPath>LoopbackPassphraseProvider.h</itemPath>
<itemPath>PopupAddUid.h</itemPath>
<itemPath>PopupCertifyUserId.h</itemPath>
<itemPath>PopupCreate.h</itemPath>
<itemPath>PopupDeleter.h</itemPath>
@@ -39,6 +40,7 @@
<itemPath>KeyEdit.cpp</itemPath>
<itemPath>KeyringIO.cpp</itemPath>
<itemPath>LoopbackPassphraseProvider.cpp</itemPath>
<itemPath>PopupAddUid.cpp</itemPath>
<itemPath>PopupCertifyUserId.cpp</itemPath>
<itemPath>PopupCreate.cpp</itemPath>
<itemPath>PopupDeleter.cpp</itemPath>
@@ -120,6 +122,10 @@
</item>
<item path="LoopbackPassphraseProvider.h" ex="false" tool="3" flavor2="0">
</item>
<item path="PopupAddUid.cpp" ex="false" tool="1" flavor2="0">
</item>
<item path="PopupAddUid.h" ex="false" tool="3" flavor2="0">
</item>
<item path="PopupCertifyUserId.cpp" ex="false" tool="1" flavor2="0">
</item>
<item path="PopupCertifyUserId.h" ex="false" tool="3" flavor2="0">
@@ -232,6 +238,10 @@
</item>
<item path="LoopbackPassphraseProvider.h" ex="false" tool="3" flavor2="0">
</item>
<item path="PopupAddUid.cpp" ex="false" tool="1" flavor2="0">
</item>
<item path="PopupAddUid.h" ex="false" tool="3" flavor2="0">
</item>
<item path="PopupCertifyUserId.cpp" ex="false" tool="1" flavor2="0">
</item>
<item path="PopupCertifyUserId.h" ex="false" tool="3" flavor2="0">
@@ -348,6 +358,10 @@
</item>
<item path="LoopbackPassphraseProvider.h" ex="false" tool="3" flavor2="0">
</item>
<item path="PopupAddUid.cpp" ex="false" tool="1" flavor2="0">
</item>
<item path="PopupAddUid.h" ex="false" tool="3" flavor2="0">
</item>
<item path="PopupCertifyUserId.cpp" ex="false" tool="1" flavor2="0">
</item>
<item path="PopupCertifyUserId.h" ex="false" tool="3" flavor2="0">