mirror of
https://gitee.com/openharmony/useriam_user_auth_framework
synced 2024-11-23 15:49:52 +00:00
Merge https://gitee.com/openharmony/useriam_user_auth_framework into pr_765
This commit is contained in:
commit
118b055c6f
@ -48,6 +48,7 @@ constexpr const char *NOTICE_EVENT_AUTH_READY = "EVENT_AUTH_TYPE_READY";
|
||||
constexpr const char *NOTICE_EVENT_CANCEL_AUTH = "EVENT_AUTH_USER_CANCEL";
|
||||
constexpr const char *NOTICE_EVENT_USER_NAVIGATION = "EVENT_AUTH_USER_NAVIGATION";
|
||||
constexpr const char *NOTICE_EVENT_WIDGET_PARA_INVALID = "EVENT_AUTH_WIDGET_PARA_INVALID";
|
||||
constexpr const char *NOTICE_EVENT_END = "EVENT_AUTH_END";
|
||||
|
||||
// For API6
|
||||
enum class AuthenticationResult : int32_t {
|
||||
|
@ -38,6 +38,8 @@ namespace UserAuth {
|
||||
|
||||
const std::string NOTICE_VERSION_STR = "1";
|
||||
const std::string CMD_NOTIFY_AUTH_START = "CMD_NOTIFY_AUTH_START";
|
||||
const std::string CMD_NOTIFY_AUTH_RESULT = "CMD_NOTIFY_AUTH_RESULT";
|
||||
const std::string CMD_NOTIFY_AUTH_TIP = "CMD_NOTIFY_AUTH_TIP";
|
||||
const uint64_t MAX_ALLOWABLE_REUSE_DURATION = 5 * 60 * 1000;
|
||||
|
||||
/**
|
||||
|
@ -79,7 +79,7 @@ public:
|
||||
virtual void SetTraceAuthWidgetType(uint32_t authWidgetType) = 0;
|
||||
virtual void SetTraceAuthTrustLevel(AuthTrustLevel atl) = 0;
|
||||
virtual void SetTraceReuseUnlockResultType(uint32_t reuseUnlockResultType) = 0;
|
||||
virtual void SetTraceReuseUnlockResultDuration(uint32_t reuseUnlockResultDuration) = 0;
|
||||
virtual void SetTraceReuseUnlockResultDuration(uint64_t reuseUnlockResultDuration) = 0;
|
||||
virtual void SetCleaner(Context::ContextStopCallback callback) = 0;
|
||||
virtual void ProcessAuthResult(int32_t tip, const std::vector<uint8_t> &extraInfo) = 0;
|
||||
virtual sptr<IamCallbackInterface> GetIamCallback() = 0;
|
||||
|
@ -46,6 +46,7 @@ public:
|
||||
ResultCode OnNotice(NoticeType type, const std::string &eventData);
|
||||
void ReportWidgetResult(int32_t result, AuthType authType,
|
||||
int32_t lockoutDuration, int32_t remainAttempts);
|
||||
void ReportWidgetTip(int32_t tipType, AuthType authType, std::vector<uint8_t> tipInfo);
|
||||
|
||||
// others
|
||||
void SetPinSubType(const PinSubType &subType);
|
||||
|
@ -63,14 +63,15 @@ public:
|
||||
|
||||
// WidgetScheduleNodeCallback API
|
||||
bool LaunchWidget() override;
|
||||
void ExecuteAuthList(const std::set<AuthType> &authTypeList) override;
|
||||
void ExecuteAuthList(const std::set<AuthType> &authTypeList, bool endAfterFirstFail) override;
|
||||
void EndAuthAsCancel() override;
|
||||
void EndAuthAsNaviPin() override;
|
||||
void EndAuthAsWidgetParaInvalid() override;
|
||||
void StopAuthList(const std::vector<AuthType> &authTypeList) override;
|
||||
void SuccessAuth(AuthType authType) override;
|
||||
|
||||
void AuthResult(int32_t resultCode, int32_t at, const Attributes &finalResult);
|
||||
void AuthResult(int32_t resultCode, int32_t authType, const Attributes &finalResult);
|
||||
void AuthTipInfo(int32_t tipInfo, int32_t authType, const Attributes &extraInfo);
|
||||
protected:
|
||||
virtual bool OnStart();
|
||||
virtual void OnResult(int32_t resultCode, const std::shared_ptr<Attributes> &scheduleResultAttr);
|
||||
@ -79,7 +80,7 @@ protected:
|
||||
private:
|
||||
void SetLatestError(int32_t error) override;
|
||||
std::shared_ptr<Context> BuildTask(const std::vector<uint8_t> &challenge,
|
||||
AuthType authType, AuthTrustLevel authTrustLevel);
|
||||
AuthType authType, AuthTrustLevel authTrustLevel, bool endAfterFirstFail);
|
||||
bool BuildSchedule();
|
||||
bool ConnectExtension();
|
||||
int32_t ConnectExtensionAbility(const AAFwk::Want &want, const std::string commandStr);
|
||||
|
@ -44,6 +44,7 @@ struct WidgetNotice {
|
||||
std::string event {""};
|
||||
std::string version {""};
|
||||
std::vector<std::string> typeList {};
|
||||
bool endAfterFirstFail {false};
|
||||
};
|
||||
void to_json(nlohmann::json &jsonNotice, const WidgetNotice ¬ice);
|
||||
void from_json(const nlohmann::json &jsonNotice, WidgetNotice ¬ice);
|
||||
@ -63,9 +64,11 @@ struct WidgetCommand {
|
||||
int32_t result = -1;
|
||||
int32_t lockoutDuration = -1;
|
||||
int32_t remainAttempts = -1;
|
||||
std::string tip = {""};
|
||||
std::string sensorInfo {""};
|
||||
|
||||
int32_t tipType = -1;
|
||||
std::vector<uint8_t> tipInfo;
|
||||
|
||||
ExtraInfo extraInfo;
|
||||
};
|
||||
|
||||
|
@ -165,7 +165,7 @@ void ContextCallbackImpl::SetTraceReuseUnlockResultType(uint32_t reuseUnlockResu
|
||||
metaData_.reuseUnlockResultType = reuseUnlockResultType;
|
||||
}
|
||||
|
||||
void ContextCallbackImpl::SetTraceReuseUnlockResultDuration(uint32_t reuseUnlockResultDuration)
|
||||
void ContextCallbackImpl::SetTraceReuseUnlockResultDuration(uint64_t reuseUnlockResultDuration)
|
||||
{
|
||||
metaData_.reuseUnlockResultDuration = reuseUnlockResultDuration;
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ public:
|
||||
void SetTraceAuthWidgetType(uint32_t authWidgetType) override;
|
||||
void SetTraceAuthTrustLevel(AuthTrustLevel atl) override;
|
||||
void SetTraceReuseUnlockResultType(uint32_t reuseUnlockResultType) override;
|
||||
void SetTraceReuseUnlockResultDuration(uint32_t reuseUnlockResultDuration) override;
|
||||
void SetTraceReuseUnlockResultDuration(uint64_t reuseUnlockResultDuration) override;
|
||||
void SetCleaner(Context::ContextStopCallback callback) override;
|
||||
void ProcessAuthResult(int32_t tip, const std::vector<uint8_t> &extraInfo) override;
|
||||
sptr<IamCallbackInterface> GetIamCallback() override;
|
||||
|
@ -85,7 +85,7 @@ ResultCode WidgetClient::OnNotice(NoticeType type, const std::string &eventData)
|
||||
return ResultCode::INVALID_PARAMETERS;
|
||||
}
|
||||
if (notice.event == NOTICE_EVENT_AUTH_READY) {
|
||||
schedule_->StartAuthList(authTypeList);
|
||||
schedule_->StartAuthList(authTypeList, notice.endAfterFirstFail);
|
||||
} else if (notice.event == NOTICE_EVENT_CANCEL_AUTH) {
|
||||
if (authTypeList.size() == 1 && authTypeList[0] == AuthType::ALL) {
|
||||
schedule_->StopSchedule();
|
||||
@ -96,6 +96,8 @@ ResultCode WidgetClient::OnNotice(NoticeType type, const std::string &eventData)
|
||||
schedule_->NaviPinAuth();
|
||||
} else if (notice.event == NOTICE_EVENT_WIDGET_PARA_INVALID) {
|
||||
schedule_->WidgetParaInvalid();
|
||||
} else if (notice.event == NOTICE_EVENT_END) {
|
||||
schedule_->StopAuthList(authTypeList);
|
||||
}
|
||||
return ResultCode::SUCCESS;
|
||||
}
|
||||
@ -121,7 +123,7 @@ void WidgetClient::ReportWidgetResult(int32_t result, AuthType authType,
|
||||
};
|
||||
// sendCommand of CMD_NOTIFY_AUTH_RESULT
|
||||
WidgetCommand::Cmd cmd {
|
||||
.event = "CMD_NOTIFY_AUTH_RESULT",
|
||||
.event = CMD_NOTIFY_AUTH_RESULT,
|
||||
.version = NOTICE_VERSION_STR,
|
||||
.type = AuthType2Str(authType),
|
||||
.result = result,
|
||||
@ -148,6 +150,23 @@ void WidgetClient::ReportWidgetResult(int32_t result, AuthType authType,
|
||||
SendCommand(widgetCmd);
|
||||
}
|
||||
|
||||
void WidgetClient::ReportWidgetTip(int32_t tipType, AuthType authType, std::vector<uint8_t> tipInfo)
|
||||
{
|
||||
// sendCommand of CMD_NOTIFY_AUTH_TIP
|
||||
WidgetCommand::Cmd cmd {
|
||||
.event = CMD_NOTIFY_AUTH_TIP,
|
||||
.version = NOTICE_VERSION_STR,
|
||||
.type = AuthType2Str(authType),
|
||||
.tipType = tipType,
|
||||
.tipInfo = tipInfo
|
||||
};
|
||||
WidgetCommand widgetCmd {
|
||||
.widgetContextId = widgetContextId_,
|
||||
.cmdList = { cmd }
|
||||
};
|
||||
SendCommand(widgetCmd);
|
||||
}
|
||||
|
||||
void WidgetClient::SetWidgetContextId(uint64_t contextId)
|
||||
{
|
||||
widgetContextId_ = contextId;
|
||||
@ -266,7 +285,8 @@ bool WidgetClient::IsValidNoticeType(const WidgetNotice ¬ice)
|
||||
if (notice.event != NOTICE_EVENT_AUTH_READY &&
|
||||
notice.event != NOTICE_EVENT_CANCEL_AUTH &&
|
||||
notice.event != NOTICE_EVENT_USER_NAVIGATION &&
|
||||
notice.event != NOTICE_EVENT_WIDGET_PARA_INVALID) {
|
||||
notice.event != NOTICE_EVENT_WIDGET_PARA_INVALID &&
|
||||
notice.event != NOTICE_EVENT_END) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -140,7 +140,7 @@ std::shared_ptr<ContextCallback> WidgetContext::GetAuthContextCallback(AuthType
|
||||
}
|
||||
|
||||
std::shared_ptr<Context> WidgetContext::BuildTask(const std::vector<uint8_t> &challenge,
|
||||
AuthType authType, AuthTrustLevel authTrustLevel)
|
||||
AuthType authType, AuthTrustLevel authTrustLevel, bool endAfterFirstFail)
|
||||
{
|
||||
IF_FALSE_LOGE_AND_RETURN_VAL(callerCallback_ != nullptr, nullptr);
|
||||
auto userId = para_.userId;
|
||||
@ -158,7 +158,7 @@ std::shared_ptr<Context> WidgetContext::BuildTask(const std::vector<uint8_t> &ch
|
||||
para.authType = authType;
|
||||
para.atl = authTrustLevel;
|
||||
para.challenge = challenge;
|
||||
para.endAfterFirstFail = true;
|
||||
para.endAfterFirstFail = endAfterFirstFail;
|
||||
para.callerName = para_.callerName;
|
||||
para.sdkVersion = para_.sdkVersion;
|
||||
auto context = ContextFactory::CreateSimpleAuthContext(para, widgetCallback);
|
||||
@ -206,9 +206,9 @@ bool WidgetContext::OnStop()
|
||||
return true;
|
||||
}
|
||||
|
||||
void WidgetContext::AuthResult(int32_t resultCode, int32_t at, const Attributes &finalResult)
|
||||
void WidgetContext::AuthResult(int32_t resultCode, int32_t authType, const Attributes &finalResult)
|
||||
{
|
||||
IAM_LOGI("recv task result: %{public}d, authType: %{public}d", resultCode, at);
|
||||
IAM_LOGI("recv task result: %{public}d, authType: %{public}d", resultCode, authType);
|
||||
int32_t remainTimes = -1;
|
||||
int32_t freezingTime = -1;
|
||||
if (!finalResult.GetInt32Value(Attributes::ATTR_REMAIN_TIMES, remainTimes)) {
|
||||
@ -217,25 +217,34 @@ void WidgetContext::AuthResult(int32_t resultCode, int32_t at, const Attributes
|
||||
if (!finalResult.GetInt32Value(Attributes::ATTR_FREEZING_TIME, freezingTime)) {
|
||||
IAM_LOGI("get freezingTime failed.");
|
||||
}
|
||||
AuthType authType = static_cast<AuthType>(at);
|
||||
WidgetClient::Instance().ReportWidgetResult(resultCode, authType, freezingTime, remainTimes);
|
||||
AuthType authTypeTmp = static_cast<AuthType>(authType);
|
||||
WidgetClient::Instance().ReportWidgetResult(resultCode, authTypeTmp, freezingTime, remainTimes);
|
||||
IF_FALSE_LOGE_AND_RETURN(schedule_ != nullptr);
|
||||
IF_FALSE_LOGE_AND_RETURN(callerCallback_ != nullptr);
|
||||
callerCallback_->SetTraceAuthType(authType);
|
||||
callerCallback_->SetTraceAuthType(authTypeTmp);
|
||||
IAM_LOGI("call schedule:");
|
||||
if (resultCode == ResultCode::SUCCESS) {
|
||||
finalResult.GetUint8ArrayValue(Attributes::ATTR_SIGNATURE, authResultInfo_.token);
|
||||
finalResult.GetUint16Value(Attributes::ATTR_CREDENTIAL_DIGEST, authResultInfo_.credentialDigest);
|
||||
finalResult.GetUint16Value(Attributes::ATTR_CREDENTIAL_COUNT, authResultInfo_.credentialCount);
|
||||
authResultInfo_.authType = authType;
|
||||
schedule_->SuccessAuth(authType);
|
||||
authResultInfo_.authType = authTypeTmp;
|
||||
schedule_->SuccessAuth(authTypeTmp);
|
||||
} else {
|
||||
// failed
|
||||
SetLatestError(resultCode);
|
||||
schedule_->StopAuthList({authType});
|
||||
schedule_->StopAuthList({authTypeTmp});
|
||||
}
|
||||
}
|
||||
|
||||
void WidgetContext::AuthTipInfo(int32_t tipType, int32_t authType, const Attributes &extraInfo)
|
||||
{
|
||||
IAM_LOGI("recv tip: %{public}d, authType: %{public}d", tipType, authType);
|
||||
std::vector<uint8_t> tipInfo;
|
||||
bool getTipInfoRet = extraInfo.GetUint8ArrayValue(Attributes::ATTR_EXTRA_INFO, tipInfo);
|
||||
IF_FALSE_LOGE_AND_RETURN(getTipInfoRet);
|
||||
WidgetClient::Instance().ReportWidgetTip(tipType, static_cast<AuthType>(authType), tipInfo);
|
||||
}
|
||||
|
||||
// WidgetScheduleNodeCallback
|
||||
bool WidgetContext::LaunchWidget()
|
||||
{
|
||||
@ -247,13 +256,13 @@ bool WidgetContext::LaunchWidget()
|
||||
return true;
|
||||
}
|
||||
|
||||
void WidgetContext::ExecuteAuthList(const std::set<AuthType> &authTypeList)
|
||||
void WidgetContext::ExecuteAuthList(const std::set<AuthType> &authTypeList, bool endAfterFirstFail)
|
||||
{
|
||||
IAM_LOGI("execute auth list");
|
||||
// create task, and start it
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
for (auto &authType : authTypeList) {
|
||||
auto task = BuildTask(para_.challenge, authType, para_.atl);
|
||||
auto task = BuildTask(para_.challenge, authType, para_.atl, endAfterFirstFail);
|
||||
if (task == nullptr) {
|
||||
IAM_LOGE("failed to create task, authType: %{public}s", AuthType2Str(authType).c_str());
|
||||
continue;
|
||||
|
@ -43,6 +43,11 @@ void WidgetContextCallbackImpl::OnResult(int32_t result, const Attributes &extra
|
||||
|
||||
void WidgetContextCallbackImpl::OnAcquireInfo(int32_t module, int32_t acquireInfo, const Attributes &extraInfo)
|
||||
{
|
||||
std::lock_guard lock(mutex_);
|
||||
auto widgetContext = widgetContext_.lock();
|
||||
if (widgetContext != nullptr) {
|
||||
widgetContext->AuthTipInfo(acquireInfo, authType_, extraInfo);
|
||||
}
|
||||
}
|
||||
|
||||
sptr<IRemoteObject> WidgetContextCallbackImpl::AsObject()
|
||||
|
@ -40,11 +40,13 @@ const std::string JSON_WIDGET_CTX_ID = "widgetContextId";
|
||||
const std::string JSON_AUTH_EVENT = "event";
|
||||
const std::string JSON_AUTH_VERSION = "version";
|
||||
const std::string JSON_AUTH_PAYLOAD = "payload";
|
||||
const std::string JSON_AUTH_END_AFTER_FIRST_FAIL = "endAfterFirstFail";
|
||||
const std::string JSON_LOCKOUT_DURATION = "lockoutDuration";
|
||||
const std::string JSON_REMAIN_ATTEMPTS = "remainAttempts";
|
||||
const std::string JSON_AUTH_RESULT = "result";
|
||||
const std::string JSON_SENSOR_INFO = "sensorInfo";
|
||||
const std::string JSON_AUTH_TIP = "tip";
|
||||
const std::string JSON_AUTH_TIP_TYPE = "tipType";
|
||||
const std::string JSON_AUTH_TIP_INFO = "tipInfo";
|
||||
const std::string JSON_AUTH_TITLE = "title";
|
||||
const std::string JSON_AUTH_CMD = "cmd";
|
||||
const std::string JSON_AUTH_PIN_SUB_TYPE = "pinSubType";
|
||||
@ -58,6 +60,63 @@ const std::string JSON_CHALLENGE = "challenge";
|
||||
const std::string JSON_CALLER_BUNDLE_NAME = "callingBundleName";
|
||||
const std::string JSON_CMD_EXTRA_INFO = "extraInfo";
|
||||
|
||||
namespace {
|
||||
void GetJsonPayload(nlohmann::json &jsonPayload, const WidgetCommand::Cmd &cmd, bool setExtraInfo)
|
||||
{
|
||||
jsonPayload[JSON_AUTH_TYPE] = cmd.type;
|
||||
if (cmd.lockoutDuration != -1) {
|
||||
jsonPayload[JSON_LOCKOUT_DURATION] = cmd.lockoutDuration;
|
||||
}
|
||||
if (cmd.remainAttempts != -1) {
|
||||
jsonPayload[JSON_REMAIN_ATTEMPTS] = cmd.remainAttempts;
|
||||
}
|
||||
if (cmd.event == CMD_NOTIFY_AUTH_RESULT) {
|
||||
jsonPayload[JSON_AUTH_RESULT] = cmd.result;
|
||||
}
|
||||
if (cmd.event == CMD_NOTIFY_AUTH_TIP) {
|
||||
jsonPayload[JSON_AUTH_TIP_TYPE] = cmd.tipType;
|
||||
jsonPayload[JSON_AUTH_TIP_INFO] = cmd.tipInfo;
|
||||
}
|
||||
if (!cmd.sensorInfo.empty()) {
|
||||
jsonPayload[JSON_SENSOR_INFO] = cmd.sensorInfo;
|
||||
}
|
||||
if (setExtraInfo) {
|
||||
auto jsonCmdExtraInfo = nlohmann::json({{JSON_CHALLENGE, cmd.extraInfo.challenge},
|
||||
{JSON_CALLER_BUNDLE_NAME, cmd.extraInfo.callingBundleName}});
|
||||
jsonPayload[JSON_CMD_EXTRA_INFO] = jsonCmdExtraInfo;
|
||||
}
|
||||
}
|
||||
|
||||
void GetJsonCmd(nlohmann::json &jsonCommand, const WidgetCommand &command, bool setExtraInfo)
|
||||
{
|
||||
std::vector<nlohmann::json> jsonCmdList;
|
||||
for (auto &cmd : command.cmdList) {
|
||||
auto jsonCmd = nlohmann::json({{JSON_AUTH_EVENT, cmd.event},
|
||||
{JSON_AUTH_VERSION, cmd.version}
|
||||
});
|
||||
nlohmann::json jsonPayload;
|
||||
GetJsonPayload(jsonPayload, cmd, setExtraInfo);
|
||||
jsonCmd[JSON_AUTH_PAYLOAD] = jsonPayload;
|
||||
jsonCmdList.push_back(jsonCmd);
|
||||
}
|
||||
|
||||
jsonCommand = nlohmann::json({{JSON_WIDGET_CTX_ID, command.widgetContextId},
|
||||
{JSON_AUTH_TYPE, command.typeList},
|
||||
{JSON_AUTH_TITLE, command.title},
|
||||
{JSON_AUTH_CMD, jsonCmdList}
|
||||
});
|
||||
if (command.pinSubType != "") {
|
||||
jsonCommand[JSON_AUTH_PIN_SUB_TYPE] = command.pinSubType;
|
||||
}
|
||||
if (command.windowModeType != "") {
|
||||
jsonCommand[JSON_AUTH_WINDOW_MODE] = command.windowModeType;
|
||||
}
|
||||
if (command.navigationButtonText != "") {
|
||||
jsonCommand[JSON_AUTH_NAVI_BTN_TEXT] = command.navigationButtonText;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// utils
|
||||
AuthType Str2AuthType(const std::string &strAuthType)
|
||||
{
|
||||
@ -125,7 +184,8 @@ std::string PinSubType2Str(const PinSubType &subType)
|
||||
|
||||
void to_json(nlohmann::json &jsonNotice, const WidgetNotice ¬ice)
|
||||
{
|
||||
auto type = nlohmann::json({{JSON_AUTH_TYPE, notice.typeList}});
|
||||
auto type = nlohmann::json({{JSON_AUTH_TYPE, notice.typeList},
|
||||
{JSON_AUTH_END_AFTER_FIRST_FAIL, notice.endAfterFirstFail}});
|
||||
jsonNotice = nlohmann::json({{JSON_WIDGET_CTX_ID, notice.widgetContextId},
|
||||
{JSON_AUTH_EVENT, notice.event},
|
||||
{JSON_AUTH_VERSION, notice.version},
|
||||
@ -143,8 +203,10 @@ void from_json(const nlohmann::json &jsonNotice, WidgetNotice ¬ice)
|
||||
if (jsonNotice.find(JSON_AUTH_VERSION) != jsonNotice.end() && jsonNotice[JSON_AUTH_VERSION].is_string()) {
|
||||
jsonNotice.at(JSON_AUTH_VERSION).get_to(notice.version);
|
||||
}
|
||||
if (jsonNotice.find(JSON_AUTH_PAYLOAD) != jsonNotice.end() &&
|
||||
jsonNotice[JSON_AUTH_PAYLOAD].find(JSON_AUTH_TYPE) != jsonNotice[JSON_AUTH_PAYLOAD].end() &&
|
||||
if (jsonNotice.find(JSON_AUTH_PAYLOAD) == jsonNotice.end()) {
|
||||
return;
|
||||
}
|
||||
if (jsonNotice[JSON_AUTH_PAYLOAD].find(JSON_AUTH_TYPE) != jsonNotice[JSON_AUTH_PAYLOAD].end() &&
|
||||
jsonNotice[JSON_AUTH_PAYLOAD][JSON_AUTH_TYPE].is_array()) {
|
||||
for (size_t index = 0; index < jsonNotice[JSON_AUTH_PAYLOAD][JSON_AUTH_TYPE].size(); index++) {
|
||||
if (!jsonNotice[JSON_AUTH_PAYLOAD][JSON_AUTH_TYPE].at(index).is_string()) {
|
||||
@ -154,101 +216,26 @@ void from_json(const nlohmann::json &jsonNotice, WidgetNotice ¬ice)
|
||||
notice.typeList.emplace_back(jsonNotice[JSON_AUTH_PAYLOAD][JSON_AUTH_TYPE].at(index).get<std::string>());
|
||||
}
|
||||
}
|
||||
if ((jsonNotice[JSON_AUTH_PAYLOAD].find(JSON_AUTH_END_AFTER_FIRST_FAIL) !=
|
||||
jsonNotice[JSON_AUTH_PAYLOAD].end()) &&
|
||||
jsonNotice[JSON_AUTH_PAYLOAD][JSON_AUTH_END_AFTER_FIRST_FAIL].is_boolean()) {
|
||||
jsonNotice[JSON_AUTH_PAYLOAD].at(JSON_AUTH_END_AFTER_FIRST_FAIL).get_to(notice.endAfterFirstFail);
|
||||
}
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &jsonCommand, const WidgetCommand &command)
|
||||
{
|
||||
std::vector<nlohmann::json> jsonCmdList;
|
||||
for (auto &cmd : command.cmdList) {
|
||||
auto jsonCmd = nlohmann::json({{JSON_AUTH_EVENT, cmd.event},
|
||||
{JSON_AUTH_VERSION, cmd.version}
|
||||
});
|
||||
auto jsonPayload = nlohmann::json({{JSON_AUTH_TYPE, cmd.type}});
|
||||
if (cmd.lockoutDuration != -1) {
|
||||
jsonPayload[JSON_LOCKOUT_DURATION] = cmd.lockoutDuration;
|
||||
}
|
||||
if (cmd.remainAttempts != -1) {
|
||||
jsonPayload[JSON_REMAIN_ATTEMPTS] = cmd.remainAttempts;
|
||||
}
|
||||
if (cmd.event == "CMD_NOTIFY_AUTH_RESULT") {
|
||||
jsonPayload[JSON_AUTH_RESULT] = cmd.result;
|
||||
}
|
||||
if (cmd.sensorInfo != "") {
|
||||
jsonPayload[JSON_SENSOR_INFO] = cmd.sensorInfo;
|
||||
}
|
||||
if (cmd.tip != "") {
|
||||
jsonPayload[JSON_AUTH_TIP] = cmd.tip;
|
||||
}
|
||||
auto jsonCmdExtraInfo = nlohmann::json({{JSON_CHALLENGE, cmd.extraInfo.challenge},
|
||||
{JSON_CALLER_BUNDLE_NAME, cmd.extraInfo.callingBundleName}});
|
||||
jsonPayload[JSON_CMD_EXTRA_INFO] = jsonCmdExtraInfo;
|
||||
|
||||
jsonCmd[JSON_AUTH_PAYLOAD] = jsonPayload;
|
||||
jsonCmdList.push_back(jsonCmd);
|
||||
}
|
||||
|
||||
jsonCommand = nlohmann::json({{JSON_WIDGET_CTX_ID, command.widgetContextId},
|
||||
{JSON_AUTH_TYPE, command.typeList},
|
||||
{JSON_AUTH_TITLE, command.title},
|
||||
{JSON_AUTH_CMD, jsonCmdList}
|
||||
});
|
||||
if (command.pinSubType != "") {
|
||||
jsonCommand[JSON_AUTH_PIN_SUB_TYPE] = command.pinSubType;
|
||||
}
|
||||
if (command.windowModeType != "") {
|
||||
jsonCommand[JSON_AUTH_WINDOW_MODE] = command.windowModeType;
|
||||
}
|
||||
if (command.navigationButtonText != "") {
|
||||
jsonCommand[JSON_AUTH_NAVI_BTN_TEXT] = command.navigationButtonText;
|
||||
}
|
||||
GetJsonCmd(jsonCommand, command, true);
|
||||
}
|
||||
|
||||
// WidgetCmdParameters
|
||||
void to_json(nlohmann::json &jsWidgetCmdParam, const WidgetCmdParameters &widgetCmdParameters)
|
||||
{
|
||||
std::vector<nlohmann::json> jsonCmdList;
|
||||
for (auto &cmd : widgetCmdParameters.useriamCmdData.cmdList) {
|
||||
auto jsonCmd = nlohmann::json({{JSON_AUTH_EVENT, cmd.event},
|
||||
{JSON_AUTH_VERSION, cmd.version}
|
||||
});
|
||||
auto jsonPayload = nlohmann::json({{JSON_AUTH_TYPE, cmd.type}});
|
||||
if (cmd.lockoutDuration != -1) {
|
||||
jsonPayload[JSON_LOCKOUT_DURATION] = cmd.lockoutDuration;
|
||||
}
|
||||
if (cmd.remainAttempts != -1) {
|
||||
jsonPayload[JSON_REMAIN_ATTEMPTS] = cmd.remainAttempts;
|
||||
}
|
||||
if (cmd.event == "CMD_NOTIFY_AUTH_RESULT") {
|
||||
jsonPayload[JSON_AUTH_RESULT] = cmd.result;
|
||||
}
|
||||
if (cmd.sensorInfo != "") {
|
||||
jsonPayload[JSON_SENSOR_INFO] = cmd.sensorInfo;
|
||||
}
|
||||
if (cmd.tip != "") {
|
||||
jsonPayload[JSON_AUTH_TIP] = cmd.tip;
|
||||
}
|
||||
jsonCmd[JSON_AUTH_PAYLOAD] = jsonPayload;
|
||||
jsonCmdList.push_back(jsonCmd);
|
||||
}
|
||||
|
||||
auto jsCommand = nlohmann::json({{JSON_WIDGET_CTX_ID, widgetCmdParameters.useriamCmdData.widgetContextId},
|
||||
{JSON_AUTH_TYPE, widgetCmdParameters.useriamCmdData.typeList},
|
||||
{JSON_AUTH_TITLE, widgetCmdParameters.useriamCmdData.title},
|
||||
{JSON_AUTH_CMD, jsonCmdList}
|
||||
});
|
||||
|
||||
if (widgetCmdParameters.useriamCmdData.pinSubType != "") {
|
||||
jsCommand[JSON_AUTH_PIN_SUB_TYPE] = widgetCmdParameters.useriamCmdData.pinSubType;
|
||||
}
|
||||
if (widgetCmdParameters.useriamCmdData.windowModeType != "") {
|
||||
jsCommand[JSON_AUTH_WINDOW_MODE] = widgetCmdParameters.useriamCmdData.windowModeType;
|
||||
}
|
||||
if (widgetCmdParameters.useriamCmdData.navigationButtonText != "") {
|
||||
jsCommand[JSON_AUTH_NAVI_BTN_TEXT] = widgetCmdParameters.useriamCmdData.navigationButtonText;
|
||||
}
|
||||
nlohmann::json jsonCommand;
|
||||
GetJsonCmd(jsonCommand, widgetCmdParameters.useriamCmdData, false);
|
||||
|
||||
jsWidgetCmdParam = nlohmann::json({{JSON_UI_EXTENSION_TYPE, widgetCmdParameters.uiExtensionType},
|
||||
{JSON_USER_IAM_CMD_DATA, jsCommand}
|
||||
{JSON_USER_IAM_CMD_DATA, jsonCommand}
|
||||
});
|
||||
}
|
||||
} // namespace UserAuth
|
||||
|
@ -53,7 +53,7 @@ public:
|
||||
virtual ~WidgetScheduleNode() = default;
|
||||
virtual bool StartSchedule() = 0;
|
||||
virtual bool StopSchedule() = 0;
|
||||
virtual bool StartAuthList(const std::vector<AuthType> &authTypeList) = 0;
|
||||
virtual bool StartAuthList(const std::vector<AuthType> &authTypeList, bool endAfterFirstFail) = 0;
|
||||
virtual bool StopAuthList(const std::vector<AuthType> &authTypeList) = 0;
|
||||
virtual bool SuccessAuth(AuthType authType) = 0;
|
||||
virtual bool NaviPinAuth() = 0;
|
||||
|
@ -32,7 +32,7 @@ class WidgetScheduleNodeCallback {
|
||||
public:
|
||||
virtual ~WidgetScheduleNodeCallback() = default;
|
||||
virtual bool LaunchWidget() = 0;
|
||||
virtual void ExecuteAuthList(const std::set<AuthType> &authTypeList) = 0;
|
||||
virtual void ExecuteAuthList(const std::set<AuthType> &authTypeList, bool endAfterFirstFail) = 0;
|
||||
virtual void EndAuthAsCancel() = 0;
|
||||
virtual void EndAuthAsNaviPin() = 0;
|
||||
virtual void EndAuthAsWidgetParaInvalid() = 0;
|
||||
|
@ -102,7 +102,7 @@ bool WidgetScheduleNodeImpl::StopSchedule()
|
||||
return TryKickMachine(E_CANCEL_AUTH);
|
||||
}
|
||||
|
||||
bool WidgetScheduleNodeImpl::StartAuthList(const std::vector<AuthType> &authTypeList)
|
||||
bool WidgetScheduleNodeImpl::StartAuthList(const std::vector<AuthType> &authTypeList, bool endAfterFirstFail)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
startAuthTypeList_.clear();
|
||||
@ -110,6 +110,7 @@ bool WidgetScheduleNodeImpl::StartAuthList(const std::vector<AuthType> &authType
|
||||
startAuthTypeList_.emplace_back(authType);
|
||||
IAM_LOGI("Command(type:%{public}d) on result start.", authType);
|
||||
}
|
||||
endAfterFirstFail_ = endAfterFirstFail;
|
||||
return TryKickMachine(E_START_AUTH);
|
||||
}
|
||||
|
||||
@ -173,7 +174,7 @@ void WidgetScheduleNodeImpl::OnStartAuth(FiniteStateMachine &machine, uint32_t e
|
||||
startAuthTypeSet.emplace(authType);
|
||||
}
|
||||
}
|
||||
callback->ExecuteAuthList(startAuthTypeSet);
|
||||
callback->ExecuteAuthList(startAuthTypeSet, endAfterFirstFail_);
|
||||
}
|
||||
|
||||
void WidgetScheduleNodeImpl::OnStopAuthList(FiniteStateMachine &machine, uint32_t event)
|
||||
|
@ -39,7 +39,7 @@ public:
|
||||
~WidgetScheduleNodeImpl() override = default;
|
||||
bool StartSchedule() override;
|
||||
bool StopSchedule() override;
|
||||
bool StartAuthList(const std::vector<AuthType> &authTypeList) override;
|
||||
bool StartAuthList(const std::vector<AuthType> &authTypeList, bool endAfterFirstFail) override;
|
||||
bool StopAuthList(const std::vector<AuthType> &authTypeList) override;
|
||||
bool SuccessAuth(AuthType authType) override;
|
||||
bool NaviPinAuth() override;
|
||||
@ -67,6 +67,7 @@ private:
|
||||
std::weak_ptr<WidgetScheduleNodeCallback> callback_;
|
||||
AuthType successAuthType_ {0};
|
||||
std::vector<AuthType> startAuthTypeList_;
|
||||
bool endAfterFirstFail_ {false};
|
||||
std::vector<AuthType> stopAuthTypeList_;
|
||||
std::set<AuthType> runningAuthTypeSet_;
|
||||
};
|
||||
|
@ -82,7 +82,7 @@ private:
|
||||
int32_t CheckValidSolution(int32_t userId, const AuthParam &authParam, const WidgetParam &widgetParam,
|
||||
std::vector<AuthType> &validType);
|
||||
int32_t GetCallerNameAndUserId(ContextFactory::AuthWidgetContextPara ¶,
|
||||
std::shared_ptr<ContextCallback> contextCallback);
|
||||
std::shared_ptr<ContextCallback> &contextCallback);
|
||||
};
|
||||
} // namespace UserAuth
|
||||
} // namespace UserIam
|
||||
|
@ -635,7 +635,7 @@ int32_t UserAuthService::CheckValidSolution(int32_t userId, const AuthParam &aut
|
||||
}
|
||||
|
||||
int32_t UserAuthService::GetCallerNameAndUserId(ContextFactory::AuthWidgetContextPara ¶,
|
||||
std::shared_ptr<ContextCallback> contextCallback)
|
||||
std::shared_ptr<ContextCallback> &contextCallback)
|
||||
{
|
||||
bool isBundleName = false;
|
||||
std::string callerName = "";
|
||||
|
@ -48,7 +48,7 @@ public:
|
||||
MOCK_METHOD1(SetTraceAuthTrustLevel, void(AuthTrustLevel atl));
|
||||
MOCK_METHOD1(SetTraceAuthWidgetType, void(uint32_t authWidgetType));
|
||||
MOCK_METHOD1(SetTraceReuseUnlockResultType, void(uint32_t reuseUnlockResultType));
|
||||
MOCK_METHOD1(SetTraceReuseUnlockResultDuration, void(uint32_t reuseUnlockResultDuration));
|
||||
MOCK_METHOD1(SetTraceReuseUnlockResultDuration, void(uint64_t reuseUnlockResultDuration));
|
||||
MOCK_METHOD1(SetCleaner, void(Context::ContextStopCallback callback));
|
||||
MOCK_METHOD2(ProcessAuthResult, void(int32_t tip, const std::vector<uint8_t> &extraInfo));
|
||||
MOCK_METHOD0(GetIamCallback, sptr<IamCallbackInterface>());
|
||||
|
@ -26,7 +26,7 @@ class MockWidgetScheduleNode final : public WidgetScheduleNode {
|
||||
public:
|
||||
MOCK_METHOD0(StartSchedule, bool());
|
||||
MOCK_METHOD0(StopSchedule, bool());
|
||||
MOCK_METHOD1(StartAuthList, bool(const std::vector<AuthType> &));
|
||||
MOCK_METHOD2(StartAuthList, bool(const std::vector<AuthType> &authTypeList, bool endAfterFirstFail));
|
||||
MOCK_METHOD1(StopAuthList, bool(const std::vector<AuthType> &));
|
||||
MOCK_METHOD1(SuccessAuth, bool(AuthType));
|
||||
MOCK_METHOD0(NaviPinAuth, bool());
|
||||
|
@ -28,7 +28,7 @@ namespace UserAuth {
|
||||
class MockWidgetScheduleNodeCallback final : public WidgetScheduleNodeCallback {
|
||||
public:
|
||||
MOCK_METHOD0(LaunchWidget, bool());
|
||||
MOCK_METHOD1(ExecuteAuthList, void(const std::set<AuthType> &authTypeList));
|
||||
MOCK_METHOD2(ExecuteAuthList, void(const std::set<AuthType> &authTypeList, bool endAfterFirstFail));
|
||||
MOCK_METHOD0(EndAuthAsCancel, void());
|
||||
MOCK_METHOD0(EndAuthAsNaviPin, void());
|
||||
MOCK_METHOD0(EndAuthAsWidgetParaInvalid, void());
|
||||
|
@ -296,7 +296,7 @@ HWTEST_F(WidgetContextTest, WidgetContextTestStopAuthList_003, TestSize.Level0)
|
||||
std::set<AuthType> authTypeList;
|
||||
authTypeList.insert(FACE);
|
||||
authTypeList.insert(ALL);
|
||||
widgetContext->ExecuteAuthList(authTypeList);
|
||||
widgetContext->ExecuteAuthList(authTypeList, false);
|
||||
|
||||
std::vector<AuthType> testTypeList = {ALL, PIN, FACE};
|
||||
widgetContext->StopAuthList(testTypeList);
|
||||
@ -340,7 +340,7 @@ HWTEST_F(WidgetContextTest, WidgetContextTestSuccessAuth_003, TestSize.Level0)
|
||||
auto widgetContext = CreateWidgetContext(contextId, para);
|
||||
std::set<AuthType> authTypeList;
|
||||
authTypeList.insert(FACE);
|
||||
widgetContext->ExecuteAuthList(authTypeList);
|
||||
widgetContext->ExecuteAuthList(authTypeList, true);
|
||||
AuthType authType = ALL;
|
||||
widgetContext->SuccessAuth(authType);
|
||||
EXPECT_NE(widgetContext, nullptr);
|
||||
@ -354,7 +354,7 @@ HWTEST_F(WidgetContextTest, WidgetContextTestExecuteAuthList_0001, TestSize.Leve
|
||||
ContextFactory::AuthWidgetContextPara para;
|
||||
auto widgetContext = Common::MakeShared<WidgetContext>(contextId, para, nullptr);
|
||||
std::set<AuthType> authTypeList;
|
||||
widgetContext->ExecuteAuthList(authTypeList);
|
||||
widgetContext->ExecuteAuthList(authTypeList, false);
|
||||
EXPECT_NE(widgetContext, nullptr);
|
||||
auto handler = ThreadHandler::GetSingleThreadInstance();
|
||||
handler->EnsureTask(nullptr);
|
||||
@ -367,7 +367,7 @@ HWTEST_F(WidgetContextTest, WidgetContextTestExecuteAuthList_0002, TestSize.Leve
|
||||
auto widgetContext = Common::MakeShared<WidgetContext>(contextId, para, nullptr);
|
||||
std::set<AuthType> authTypeList;
|
||||
authTypeList.insert(AuthType::PIN);
|
||||
widgetContext->ExecuteAuthList(authTypeList);
|
||||
widgetContext->ExecuteAuthList(authTypeList, true);
|
||||
EXPECT_NE(widgetContext, nullptr);
|
||||
auto handler = ThreadHandler::GetSingleThreadInstance();
|
||||
handler->EnsureTask(nullptr);
|
||||
@ -383,7 +383,7 @@ HWTEST_F(WidgetContextTest, WidgetContextTestExecuteAuthList_0003, TestSize.Leve
|
||||
auto widgetContext = Common::MakeShared<WidgetContext>(contextId, para, contextCallback);
|
||||
std::set<AuthType> authTypeList;
|
||||
authTypeList.insert(AuthType::PIN);
|
||||
widgetContext->ExecuteAuthList(authTypeList);
|
||||
widgetContext->ExecuteAuthList(authTypeList, false);
|
||||
EXPECT_NE(widgetContext, nullptr);
|
||||
auto handler = ThreadHandler::GetSingleThreadInstance();
|
||||
handler->EnsureTask(nullptr);
|
||||
|
@ -161,7 +161,7 @@ HWTEST_F(WidgetJsonTest, WidgetJsonto_json_001, TestSize.Level0)
|
||||
auto &profile = item.second;
|
||||
typeList.push_back(AuthType2Str(at));
|
||||
WidgetCommand::Cmd cmd {
|
||||
.event = "CMD_NOTIFY_AUTH_START",
|
||||
.event = CMD_NOTIFY_AUTH_START,
|
||||
.version = "1",
|
||||
.type = AuthType2Str(at)
|
||||
};
|
||||
@ -193,12 +193,11 @@ HWTEST_F(WidgetJsonTest, WidgetJsonto_json_002, TestSize.Level0)
|
||||
auto &at = item.first;
|
||||
typeList.push_back(AuthType2Str(at));
|
||||
WidgetCommand::Cmd cmd {
|
||||
.event = "CMD_NOTIFY_AUTH_RESULT",
|
||||
.event = CMD_NOTIFY_AUTH_RESULT,
|
||||
.version = "1",
|
||||
.type = AuthType2Str(at)
|
||||
};
|
||||
cmd.sensorInfo = "sensorInfo";
|
||||
cmd.tip = "tip";
|
||||
cmd.remainAttempts = -1;
|
||||
cmd.lockoutDuration = -1;
|
||||
widgetCommand.cmdList.push_back(cmd);
|
||||
@ -231,7 +230,7 @@ HWTEST_F(WidgetJsonTest, WidgetJsonto_json_003, TestSize.Level0)
|
||||
auto &profile = item.second;
|
||||
typeList.push_back(AuthType2Str(at));
|
||||
WidgetCommand::Cmd cmd {
|
||||
.event = "CMD_NOTIFY_AUTH_START",
|
||||
.event = CMD_NOTIFY_AUTH_START,
|
||||
.version = "1",
|
||||
.type = AuthType2Str(at)
|
||||
};
|
||||
@ -264,12 +263,11 @@ HWTEST_F(WidgetJsonTest, WidgetJsonto_json_004, TestSize.Level0)
|
||||
auto &at = item.first;
|
||||
typeList.push_back(AuthType2Str(at));
|
||||
WidgetCommand::Cmd cmd {
|
||||
.event = "CMD_NOTIFY_AUTH_RESULT",
|
||||
.event = CMD_NOTIFY_AUTH_RESULT,
|
||||
.version = "1",
|
||||
.type = AuthType2Str(at)
|
||||
};
|
||||
cmd.sensorInfo = "sensorInfo";
|
||||
cmd.tip = "tip";
|
||||
widgetCmdParameters.useriamCmdData.cmdList.push_back(cmd);
|
||||
}
|
||||
widgetCmdParameters.useriamCmdData.typeList = typeList;
|
||||
@ -284,7 +282,7 @@ HWTEST_F(WidgetJsonTest, WidgetJsonto_json_005, TestSize.Level0)
|
||||
{
|
||||
WidgetNotice widgetNotice;
|
||||
widgetNotice.widgetContextId = 1;
|
||||
widgetNotice.event = "CMD_NOTIFY_AUTH_START";
|
||||
widgetNotice.event = CMD_NOTIFY_AUTH_START;
|
||||
widgetNotice.typeList.push_back("pin");
|
||||
widgetNotice.typeList.push_back("face");
|
||||
widgetNotice.typeList.push_back("fingerprint");
|
||||
@ -352,6 +350,20 @@ HWTEST_F(WidgetJsonTest, WidgetJsonto_json_012, TestSize.Level0)
|
||||
EXPECT_EQ(notice.typeList.size(), static_cast<size_t>(1));
|
||||
EXPECT_EQ(notice.typeList[0], "pin");
|
||||
}
|
||||
|
||||
HWTEST_F(WidgetJsonTest, WidgetJsonto_json_013, TestSize.Level0)
|
||||
{
|
||||
auto root = nlohmann::json::parse("{\"payload\":{\"endAfterFirstFail\":123}}", nullptr, false);
|
||||
WidgetNotice notice = root.get<WidgetNotice>();
|
||||
EXPECT_EQ(notice.endAfterFirstFail, false);
|
||||
}
|
||||
|
||||
HWTEST_F(WidgetJsonTest, WidgetJsonto_json_014, TestSize.Level0)
|
||||
{
|
||||
auto root = nlohmann::json::parse("{\"payload\":{\"endAfterFirstFail\":true}}", nullptr, false);
|
||||
WidgetNotice notice = root.get<WidgetNotice>();
|
||||
EXPECT_EQ(notice.endAfterFirstFail, true);
|
||||
}
|
||||
} // namespace UserAuth
|
||||
} // namespace UserIam
|
||||
} // namespace OHOS
|
@ -81,7 +81,7 @@ HWTEST_F(WidgetScheduleNodeImplTest, WidgetScheduleNodeImplStartAuthList, TestSi
|
||||
std::vector<AuthType> authTypeList = {AuthType::ALL, AuthType::PIN, AuthType::FACE, AuthType::FINGERPRINT};
|
||||
schedule->SetCallback(widgetContext);
|
||||
schedule->StartSchedule();
|
||||
EXPECT_TRUE(schedule->StartAuthList(authTypeList));
|
||||
EXPECT_TRUE(schedule->StartAuthList(authTypeList, true));
|
||||
widgetContext->LaunchWidget();
|
||||
auto handler = ThreadHandler::GetSingleThreadInstance();
|
||||
handler->EnsureTask(nullptr);
|
||||
@ -94,7 +94,7 @@ HWTEST_F(WidgetScheduleNodeImplTest, WidgetScheduleNodeImplStopAuthList, TestSiz
|
||||
std::vector<AuthType> authTypeList = {AuthType::ALL, AuthType::PIN, AuthType::FACE, AuthType::FINGERPRINT};
|
||||
schedule->SetCallback(widgetContext);
|
||||
schedule->StartSchedule();
|
||||
schedule->StartAuthList(authTypeList);
|
||||
schedule->StartAuthList(authTypeList, false);
|
||||
EXPECT_TRUE(schedule->StopAuthList(authTypeList));
|
||||
widgetContext->LaunchWidget();
|
||||
auto handler = ThreadHandler::GetSingleThreadInstance();
|
||||
@ -108,7 +108,7 @@ HWTEST_F(WidgetScheduleNodeImplTest, WidgetScheduleNodeImplSuccessAuth, TestSize
|
||||
std::vector<AuthType> authTypeList = {AuthType::ALL, AuthType::PIN, AuthType::FACE, AuthType::FINGERPRINT};
|
||||
schedule->SetCallback(widgetContext);
|
||||
schedule->StartSchedule();
|
||||
schedule->StartAuthList(authTypeList);
|
||||
schedule->StartAuthList(authTypeList, true);
|
||||
EXPECT_TRUE(schedule->SuccessAuth(AuthType::PIN));
|
||||
widgetContext->LaunchWidget();
|
||||
auto handler = ThreadHandler::GetSingleThreadInstance();
|
||||
|
Loading…
Reference in New Issue
Block a user