From 3fd997e2ad6c03d655296fc7a59476a2d02e544d Mon Sep 17 00:00:00 2001 From: chujiaqi Date: Tue, 26 Nov 2024 21:12:03 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20=E6=89=B9=E9=87=8F=E5=90=8C=E6=AD=A5ge?= =?UTF-8?q?rrit=E4=BB=A3=E7=A0=81=E5=88=B0GitHub=E4=B8=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix: 过滤前2s内的人脸认证错误 原因:摄像模组重新上电后,会有2s的由暗变亮过程,此时获取的图片会报错,影响体验; 方法:定时器过滤前2s的错误 Log: Bug: https://pms.uniontech.com/bug-view-285495.html Influence: 人脸认证前2s的错误 Change-Id: If6a8cf12b5fa21001854cc8c1f9a26d1a2615b11 (cherry picked from commit 8f8f3c7a2f04bdc0162c5d8d9068dee6a4dbe2e4) (cherry picked from commit 83efc5cc5f0e092f318428257fa1b01be7b7ffa4) fix:greeter界面触控板无法点击异常-脚本异常,补充脚本 有些厂商系统,未默认设置触控点击属性,致使greeter界面点击无效 Log: 修复greeter触控点击异常异常问题 Bug: https://pms.uniontech.com/task-view-368567.html Influence: 触控 Change-Id: Ic3b3c3e35b3998a27aae27b5db009a8d7eb73ede (cherry picked from commit bba3f4d2abe086d1ab9bf63c106ed5aabaca71dd) (cherry picked from commit c9a5097f83adefed81e1a85b11da19ef338d9138) fix: 唤醒后从送显到锁屏出现时间较长 prepareForSleep信号有时候会会延迟,使用定时器来确认是否已经唤醒,尽早显示出锁屏。 Log: Bug: https://pms.uniontech.com/bug-view-306351.html Influence: 唤醒显示锁屏界面的时间 Change-Id: I001215b4e91be2722b5885b6f2a7546ed4c4a2f7 (cherry picked from commit 425676f80ffbb8fdd12740d94507d51441d3f9a1) (cherry picked from commit 8680e2b95032ffe9d4048ccbd2864e619a019ae0) fix: 修复登录界面外接屏显示不全的问题 窗口位置变化时,qt当前屏幕信息未及时更新,获取的原点坐标错误,导致窗口位置错误 Log: Bug: https://pms.uniontech.com/bug-view-307369.html Influence: 登录界面外接屏显示 Change-Id: Iddbad03568cc0bec40fc3354c5c53e71cfc59e32 (cherry picked from commit 5c83d124550d165c7b7c5099003d278462812857) (cherry picked from commit 46a3eb7f2da7ffec43a14c5640dc6f9dfc095c61) chore: 日志清理 调整日志内容 Change-Id: I31be393ca32113d49fc3c60fd9ce3d42b37198d9 (cherry picked from commit 62b09536d8a39b1f89e6cd56ddbb4d4e5c79f8c4) (cherry picked from commit 2a6b196c56c752230d1e647cab3d200a7c7294f4) feat: 增加密码扩展插件类型 增加密码扩展插件类型,插件内容直接显示密码框下面,插件根据业务情况可以动态显示/隐藏插件 Log: Task: https://pms.uniontech.com/task-view-361575.html Influence: 单点登录、密码认证、工行验证码认证 Change-Id: Id15a0da8fc1650d1ba450cb3c6fce31f5c00d916 (cherry picked from commit 02481f6bdf197e52bf1ca67c8c588bd602f4803f) feat: 工行手势功能合入-part1 合入登录插件内容 Log: 插件与cmake文件合入 Task: https://pms.uniontech.com/task-view-369327.html Change-Id: I1b0a11c7cb6c62d540cb40e2df984977aea6a7bc (cherry picked from commit 378b9ef6080c552c86c132703b8678484810a573) feat: 工行手势功能合入-part2 功能适配性改动 Log: dss逻辑变动 Task: https://pms.uniontech.com/task-view-369327.html Change-Id: Id5f51ffbc984efac1825a913e8e97420bebf53d1 (cherry picked from commit 9021742371eecc4af1a497912c4395a9393e9089) feat: 工行手势功能合入-part4 更新翻译工具 Log: 翻译内容 Task: https://pms.uniontech.com/task-view-369327.html Change-Id: I81b6e904e5a660d7061645a4b7d06a01f73b6aad (cherry picked from commit 838cc34c467001d4c46fe76ed61db5cb7c110885) feat: 工行手势功能合入-part3 debian打包内容变动--解决插件包安装冲突 Log: deb打包 Task: https://pms.uniontech.com/task-view-369327.html Change-Id: I129fa7d43cc45697a19e39007f10e116218fcd54 (cherry picked from commit ef6383c3a1c7bbe2549a6d1995768e7f84c6c105) chore: 添加认证过程日志 小概率出现认证界面停留在认证过程中而无结果的情况 补充日志记录认证状态 Log: 补充日志 Bug: https://pms.uniontech.com/bug-view-294151.html Change-Id: If71390c5a7fc72a07f30c58b5ca31a4ba37d56a2 (cherry picked from commit 4dad812a1f6c5b8d4625196856ecc536ab93a22f) chore: shell安装时因控制中心尚未安装,指定的用户未创建 添加用户 回合主线时,该用户应由daemon而不是应用创建 log: 添加用户 Task: https://pms.uniontech.com/task-view-369327.html Change-Id: I5fd26d9ee5338877b57e982277d1a2c9c87d99de (cherry picked from commit 544c8a8de253028b7b69a89853934744c7eba551) chore: 补充依赖 添加依赖项,该插件在打包镜像时需要在dde-daemon,startdde后安装 log: 添加依赖 Task: https://pms.uniontech.com/task-view-369327.html Change-Id: I16e0a0a01a6b84c788181f37cec41c00ff4f7f17 (cherry picked from commit c05b6ca7c02105da8c68502ebd3646884f06c658) fix: 避免手势插件在未开启认证时加入认证 手势插件在单因下意外加入认证,导致单因认证窗口高度计算错误 Log: 手势认证在单因下的加载问题 Bug: https://pms.uniontech.com/bug-view-294985.html Change-Id: Ie44376c919d6f5fb3ac52c2ff78fded8c04e8682 (cherry picked from commit e4a1f9295d538f03c1d22bcb05e14fefadb881ed) fix: 重置密码对话框字体不明显 dtk控件问题 Log: 控件样式调整 Bug: https://pms.uniontech.com/bug-view-295859.html Change-Id: If5c9f151715b1b7fd8eeff515b3baabc7f45a79f (cherry picked from commit ad7002274d89fd87039f088f6737368e32a069f6) fix: 有验证码时点击按钮无法验证 原因:点击登录按钮的时候没有发送验证码 Log: Bug: https://pms.uniontech.com/bug-view-299127.html Influence: 登录、锁屏验证码登录 Change-Id: I6bd4a166a56d6582f8c7e90093b886ef41ead761 (cherry picked from commit 7dbd01e3757b6a7c9cd7c044805817beff607f08) chore: 修改微信扫码登录和认证码认证为强制依赖 把微信扫码登录和认证码认证改成强制依赖,避免低概率出现升级没有安装这两个包的问题。这两个包本身主要是依赖一些qt的底层库,理论上不会造成dde-session-shell升级失败。 Log: Task: https://pms.uniontech.com/task-view-361575.html Influence: 升级dde-session-shell Change-Id: Ia5e1102014a2837e93a0809c0a9429f59a818c35 (cherry picked from commit 95d5885c6f7ff0252d75600cf4d43d6f494d1d2d) fix: 切换到禁用用户时,未更新认证界面 多因次序认证需要处理AT_NONE类型的认证 Log: 多因次序认证需要处理AT_NONE类型的认证 Bug: https://pms.uniontech.com/bug-edit-300519.html Change-Id: I55dd18dd883a608ed807432f3547c2a04da26504 (cherry picked from commit c77a2489de500913c9c7f9d6eaa6c3828a6d9ab0) fix: 开启手势认证后输入正确验证码无法认证通过 原因:开启手势认证后,认证方式切换成了多因,多因没有处理验证码的逻辑 Log: Bug: https://pms.uniontech.com/bug-view-299127.html Influence: 验证码认证 Change-Id: I7db926b9a9187f1165984f18fec312d1623e2977 (cherry picked from commit 5ae1ea38a8e443163671014d94516920ffaf7a86) fix: 概率出现提示need verify code的文案 验证状态是AS_VerifyCode时会收到这个文案,这个无需显示出来,因为马上会收到AS_Verify状态提示密码错误 Log: Bug: https://pms.uniontech.com/bug-view-300293.html Influence: 认证失败错误文案 Change-Id: Icfb8049efd846d74134294890cca2e2a95260a85 (cherry picked from commit ed894a25765e27b3e80a91ef33f9931af09d1fd4) fix: 配置为小眼睛长按显示密码时,一些场景上功能失效 因为其它控件的可见性导致位置变化,且无法触发release事件 Log: 调整控件位置,确保不受其它控件影响 Bug: https://pms.uniontech.com/bug-view-300407.html Change-Id: If681caa1f8e211e15b534c0ed8dbd5e8e116353f (cherry picked from commit ed400fdcbd6424a00392b2fbac78e311eb390f8c) fix: 锁屏和登录界面取消圆角设置 修复flemingXS锁屏界面圆角的问题 Log: Task: https://pms.uniontech.com/task-view-375757.html Influence: 锁屏和登录界面圆角 Change-Id: Ibf0aa0e12175b0b882b7ec20db9e330a41bcb50d (cherry picked from commit 324c96bf8fc626b2feb2cedd437997509df6de21) chore: 调整定制引入的依赖项 定制引入的依赖项不作为强制依赖,会引起构建问题 定制项目插件作为推荐依赖引入(微信认证保留为强制依赖) Log: 依赖项调整 Influence:构建/编译 Change-Id: I2d2bbfdece6768c5b38eb4b8c63979e354bf0acc (cherry picked from commit 95dc9a796880081c00130e09e824eca65623b1a9) fix: 部分机器关机、重启点一下键盘或鼠标会闪桌面 部分机器关机、重启点一下键盘或鼠标会闪桌面 Log: 部分机器关机、重启点一下键盘或鼠标会闪桌面 Influence: 关机、重启点击键鼠闪桌面 Bug: https://pms.uniontech.com/bug-view-312647.html Bug: https://pms.uniontech.com/bug-view-310403.html Change-Id: I55849cacb9d0fc221b4cf310e540713fca0efdb2 (cherry picked from commit 248359d7b949fa3d80627e617e9f4bbeacadf19c) (cherry picked from commit 4a0b576f41353c950922d06e633b472db4daca4a) fix: 优化部分机器关机、重启点一下键盘或鼠标会闪桌面 部分机器关机、重启点一下键盘或鼠标会闪桌面 Log: 部分机器关机、重启点一下键盘或鼠标会闪桌面 Influence: 关机、重启点击键鼠闪桌面 Bug: https://pms.uniontech.com/bug-view-312647.html Bug: https://pms.uniontech.com/bug-view-310403.html Change-Id: I17691684b28be15ae7135d5ff688f345c6198869 (cherry picked from commit fbd600726127030a1d24e9fe43c501ea681506e7) (cherry picked from commit 717502712c6ccaf8fa8c66a5b6d9a7b3a88f2151) fix: 账户输入框添加Table按键事件处理 低版本系统上使用table按键可以开启验证流程,1071后因为其他问题去掉了功能 使用其他方案重新支持table按键事件开启验证流程 Log: 修复平安科技-107x登录密码框使用tab按键时不显示问题 Bug: https://pms.uniontech.com/bug-view-316375.html Influence: 按table按键开启验证 Change-Id: I235475d040e83ac6fb45d3ab7546f5ae90563e10 (cherry picked from commit dfd72ed7dbcb15ca37d3f09d4cfd2fad5b3c6390) fix: 主线不使用手势认证插件 1.主线默认不使用这个功能,移除推荐依赖 2.增加配置,后续可以使用配置+os-config 来灵活控制是否要启用这个插件 Log: Task: https://pms.uniontech.com/task-view-376653.html Influence: 一键登录功能 Change-Id: I0e9795d25974126af538197f8aded7abfbee1335 (cherry picked from commit bc03e7a03ed37072710b676050a35f0f52f06306) fix: 登录插件请求切换认证类型异常 1.多个自定义插件时需要匹配当前的类型。 2.支持非当前认证插件请求切换认证类型。 Log: Task: https://pms.uniontech.com/task-view-376747.html Influence: 单点登录、一键登录 Change-Id: I0bf2c709557addeebd53e07b61a41a0cbda84c00 (cherry picked from commit 50a6e7ded76d8eb498eaa765a716cc22835a8c91) fix: 使用AI清理DDE编译警告 Fix gesture enable check in GestureLoginModule initialization Move the `gestrueEnable` assignment inside the `m_userName.isEmpty()` condition to ensure it is only set to false when the username is empty, preventing unintended behavior. Log: 变量生命周期修改 Influence: 编译警告 Task: https://pms.uniontech.com/task-view-377061.html Change-Id: I8643848c9807b143b74aad9a0404ba8d494adfee (cherry picked from commit 71b917368e78ead2f6e4552dafe92938068af67f) fix: 修复微信认证插件不能加载问题 defaultAuthType在部分插件上不会提供正确信息(使用0作为默认值) 因此需要明确不加载插件的类型 Log: 插件兼容性问题 Bug: https://pms.uniontech.com/bug-view-318841.html Change-Id: I7bda6380fb7d5aa3c093393bdf39e30214f17a1b (cherry picked from commit 8a74067bc7f797b8ee93d4a364ae7ddae91cc1ec) fix: 修复锁屏界面一键登录失败切换到指纹认证的问题 详细说明: 1. 修改一键登录插件中发送认证类型的方式,将AT_Fingerprint改为AT_All,让登录器根据上次认证成功的类型自动选择合适的认证方式 2. 优化SFAWidget中onRequestChangeAuth函数的认证类型切换逻辑: - 当认证按钮组被禁用且请求的认证类型不是密码认证时,自动切换到密码认证 - 增加对AT_All类型的处理,让登录器根据上次认证成功的类型自动选择合适的认证方式 - 修复自定义认证类型切换时的类型转换问题 Log: 优化锁屏界面指纹认证的切换逻辑,提升用户体验 Bug: https://pms.uniontech.com/bug-view-320405.html Influence: 一键登录功能 Change-Id: I0f745adf7423ea2b9e8ee7bb9e7bcfbb8739b9ba (cherry picked from commit 4bf74ad8e4f364fe4d5ffba396d8d19e046df58e) fix: sleepLock配置为false,lock在切换用户时意外隐藏 场景:已登录用户在切换用户到未登录用户,在greeter电源选项待机 唤醒后切换到已登录用户,锁屏被隐藏 Log: 补充setLock(true)场景 Bug: https://pms.uniontech.com/bug-view-314491.html Change-Id: I59aaddabd21384e42b0ca41d3e5a77232e448c61 (cherry picked from commit 37b8ee5faf0751c74adef7cc60e74c5f669e57bd) (cherry picked from commit cb44887ef3b23c2e0257e73791d75012421b8709) fix: 修复关机黑屏页面出来时,部分机器会闪现鼠标光标 1.关机黑屏show出来时,隐藏鼠标光标; 2.删除关机、重启时调用dde-balckwidget,后端接口里面已经有调用了不需要重复调用 Log: 修复关机黑屏闪鼠标光标 Influence: 关机、重启 Bug: https://pms.uniontech.com/bug-view-312153.html Change-Id: Ia560196cd9c2d150264f9a055858e64a2c60f6c0 (cherry picked from commit bda85a03bddc143482e2eb8111aa207115110295) fix: 处理关机1/15概率会闪鼠标光标 之前将隐藏鼠标放在了showEvent里面,还是有概率会闪鼠标 Log: 修复关机概率闪鼠标 Influence: 关机闪鼠标 Change-Id: If4486dff745abfc6fcb699519e94a43bf88e1b24 (cherry picked from commit 3c6a213dcb6baeaa7f0e1fd1d1175af53d54d0c2) fix: 给shutdown黑屏增加配置开关 有部分机器加了这个黑屏闪鼠标光标,增加配置可以不开启该黑屏界面 Log: 对shutdown黑屏界面增加配置开关 Influence: 关机、重启 Bug: https://pms.uniontech.com/bug-view-312153.html Change-Id: I784a99f91cc475963a76fea46bbca97a89be7cb6 (cherry picked from commit fbd9a9e0ed0f9a2c85811556550cfad348c1c216) fix: 增加dde-lock待机/休眠黑屏时间,避免待机/休眠耗时太长在后面过程中又亮屏 在唤醒时会将黑屏取消掉,因此定时器中这里时间设置长一些,让黑屏显示更久会比较合理 Log: Influence: 待机/休眠过程中出现黑屏又亮屏 Change-Id: I6318e2f2ff180e6dc60e508adc8514180d5306ca (cherry picked from commit 78d8f54cc98a255ffaeac1232c2ccd21e8f08789) fix: user same name UI return logo low 问题原因:代码中手动调整了返回图标的大小,但是实际上调整的时候button按钮未布局 解决方式:删除掉手动调整的逻辑,使用DHiDPIHelper::loadNxPixmap自动加载 Log: 域账户选择同名账户列表的返回图标没有适配高分屏 Influence: Bug: https://pms.uniontech.com/bug-view-275387.html Change-Id: Ic0567e03272298d6e2c1b1cd4bc275a39e70685e (cherry picked from commit 343ab0c19d22f222c66636f7757de8fbea47bc70) fix: user same name UI userr panel text add right elide 1. 用户登录item文本添加右缺省 Log: 域账户登录界面同名账户菜单选择界面,账户名或全名的长度过长时,未省略形式展示 Influence: Bug: https://pms.uniontech.com/bug-view-274949.html Change-Id: I80b05ad378a6aafc1c8321bdaa2e8c16b1042527 (cherry picked from commit e478ad83c1bf00735a12d9a1b3c3e8b6f95903e1) fix: sameuser login item fullname 1. 域管全名信息未提供导致获取错误 Log: 【域账户同名搜索】未登录过的域账户,在同名账户搜索选择界面和其登录界面上,域账户名称显示都有多余的字符 Influence: Bug: https://pms.uniontech.com/bug-view-274979.html Change-Id: I4d2eca17f0d1b8c2903674cddc1eb7a4ab6b6d04 (cherry picked from commit 3bc1a19458e3083cede1072539126cd96498833c) --- .gitignore | 2 +- .tx/config | 6 + CMakeLists.txt | 8 + ...org.deepin.dde.lightdm-deepin-greeter.json | 10 + configs/org.deepin.dde.lock.json | 20 + debian/control | 22 +- debian/dde-gesture-login.install | 6 + debian/dde-gesture-login.postinst | 21 + debian/dde-gesture-login.sysusers | 4 + debian/dde-session-shell.install | 15 +- files/wayland/lightdm-deepin-greeter-wayland | 2 +- interface/base_module_interface.h | 3 +- interface/login_module_interface.h | 5 +- lupdate.sh | 6 +- plugins/CMakeLists.txt | 1 + plugins/login-gesture/CMakeLists.txt | 72 ++++ .../login-gesture/configs/login-gesture.json | 20 + .../org.deepin.dde.dss-login-gesture.json | 16 + plugins/login-gesture/gesture.qrc | 5 + plugins/login-gesture/icons/firstEnroll.svg | 35 ++ plugins/login-gesture/login.json | 3 + .../loginPlugin/gestureloginmodule.cpp | 230 +++++++++++ .../loginPlugin/gestureloginmodule.h | 70 ++++ plugins/login-gesture/lupdate.sh | 5 + .../resetDialog/gesturedialog.cpp | 260 +++++++++++++ .../login-gesture/resetDialog/gesturedialog.h | 82 ++++ plugins/login-gesture/resetDialog/main.cpp | 138 +++++++ .../login-gesture/resetDialog/resetdialog.qrc | 5 + .../resetDialog/resetpatterncontroller.cpp | 106 +++++ .../resetDialog/resetpatterncontroller.h | 49 +++ plugins/login-gesture/resetDialog/success.svg | 19 + .../login-gesture/src/gestureauthworker.cpp | 260 +++++++++++++ plugins/login-gesture/src/gestureauthworker.h | 53 +++ .../login-gesture/src/gesturemodifyworker.cpp | 96 +++++ .../login-gesture/src/gesturemodifyworker.h | 37 ++ plugins/login-gesture/src/gesturepannel.cpp | 165 ++++++++ plugins/login-gesture/src/gesturepannel.h | 69 ++++ plugins/login-gesture/src/modulewidget.cpp | 167 ++++++++ plugins/login-gesture/src/modulewidget.h | 48 +++ plugins/login-gesture/src/waypointmodel.cpp | 367 ++++++++++++++++++ plugins/login-gesture/src/waypointmodel.h | 146 +++++++ plugins/login-gesture/src/waypointwidget.cpp | 93 +++++ plugins/login-gesture/src/waypointwidget.h | 41 ++ plugins/login-gesture/translate_generation.sh | 11 + .../translations/login-gesture_en.ts | 99 +++++ .../translations/login-gesture_zh_CN.ts | 99 +++++ .../translations/login-gesture_zh_HK.ts | 99 +++++ .../translations/login-gesture_zh_TW.ts | 99 +++++ plugins/login-gesture/utils/tokenCrypt.h | 85 ++++ .../login-gesture/utils/translastiondoc.cpp | 86 ++++ plugins/login-gesture/utils/translastiondoc.h | 62 +++ plugins/login-gesture/utils/userservice.cpp | 137 +++++++ plugins/login-gesture/utils/userservice.h | 47 +++ plugins/one-key-login/login_module.cpp | 22 +- src/dde-lock/lockframe.cpp | 13 +- src/dde-lock/lockworker.cpp | 90 +++-- src/dde-lock/lockworker.h | 1 + .../plugin_manager/login_plugin.cpp | 32 ++ src/global_util/plugin_manager/login_plugin.h | 4 + .../plugin_manager/plugin_manager.cpp | 85 +++- .../plugin_manager/plugin_manager.h | 5 + src/global_util/public_func.cpp | 15 - src/global_util/public_func.h | 5 - src/global_util/qt-compat-helper.cpp | 17 + src/global_util/qt-compat-helper.h | 32 ++ src/global_util/signal_bridge.h | 29 ++ src/libdde-auth/authcommon.h | 5 +- src/libdde-auth/deepinauthframework.cpp | 35 +- src/libdde-auth/deepinauthframework.h | 2 +- src/lightdm-deepin-greeter/greeterworker.cpp | 22 +- src/lightdm-deepin-greeter/greeterworker.h | 1 + src/session-widgets/assist_login_widget.cpp | 59 ++- src/session-widgets/assist_login_widget.h | 8 + src/session-widgets/auth_custom.cpp | 54 ++- src/session-widgets/auth_custom.h | 10 +- src/session-widgets/auth_face.cpp | 17 +- src/session-widgets/auth_face.h | 1 + src/session-widgets/auth_module.cpp | 8 +- src/session-widgets/auth_password.cpp | 107 +++-- src/session-widgets/auth_password.h | 4 + src/session-widgets/auth_widget.cpp | 12 + src/session-widgets/auth_widget.h | 2 + src/session-widgets/lockcontent.cpp | 83 ++-- src/session-widgets/mfa_widget.cpp | 83 +++- src/session-widgets/mfa_widget.h | 3 + src/session-widgets/mfasequencecontrol.cpp | 248 ++++++++++++ src/session-widgets/mfasequencecontrol.h | 67 ++++ src/session-widgets/sessionbasemodel.cpp | 17 +- src/session-widgets/sessionbasemodel.h | 4 + src/session-widgets/sfa_widget.cpp | 61 ++- src/session-widgets/sfa_widget.h | 2 +- src/session-widgets/userpanel.cpp | 12 +- src/session-widgets/userpanel.h | 7 +- src/session-widgets/userswiththesamename.cpp | 12 +- src/widgets/dlineeditex.cpp | 20 + src/widgets/dlineeditex.h | 2 + src/widgets/fullscreenbackground.cpp | 15 + src/widgets/fullscreenbackground.h | 2 + src/widgets/shutdown_black_widget.cpp | 123 ++++++ src/widgets/shutdown_black_widget.h | 27 ++ src/widgets/shutdownwidget.cpp | 3 +- tests/dde-lock/CMakeLists.txt | 1 + tests/lightdm-deepin-greeter/CMakeLists.txt | 1 + 103 files changed, 4907 insertions(+), 195 deletions(-) create mode 100644 debian/dde-gesture-login.install create mode 100644 debian/dde-gesture-login.postinst create mode 100644 debian/dde-gesture-login.sysusers create mode 100644 plugins/login-gesture/CMakeLists.txt create mode 100644 plugins/login-gesture/configs/login-gesture.json create mode 100644 plugins/login-gesture/configs/org.deepin.dde.dss-login-gesture.json create mode 100644 plugins/login-gesture/gesture.qrc create mode 100644 plugins/login-gesture/icons/firstEnroll.svg create mode 100644 plugins/login-gesture/login.json create mode 100644 plugins/login-gesture/loginPlugin/gestureloginmodule.cpp create mode 100644 plugins/login-gesture/loginPlugin/gestureloginmodule.h create mode 100755 plugins/login-gesture/lupdate.sh create mode 100644 plugins/login-gesture/resetDialog/gesturedialog.cpp create mode 100644 plugins/login-gesture/resetDialog/gesturedialog.h create mode 100644 plugins/login-gesture/resetDialog/main.cpp create mode 100644 plugins/login-gesture/resetDialog/resetdialog.qrc create mode 100644 plugins/login-gesture/resetDialog/resetpatterncontroller.cpp create mode 100644 plugins/login-gesture/resetDialog/resetpatterncontroller.h create mode 100644 plugins/login-gesture/resetDialog/success.svg create mode 100644 plugins/login-gesture/src/gestureauthworker.cpp create mode 100644 plugins/login-gesture/src/gestureauthworker.h create mode 100644 plugins/login-gesture/src/gesturemodifyworker.cpp create mode 100644 plugins/login-gesture/src/gesturemodifyworker.h create mode 100644 plugins/login-gesture/src/gesturepannel.cpp create mode 100644 plugins/login-gesture/src/gesturepannel.h create mode 100644 plugins/login-gesture/src/modulewidget.cpp create mode 100644 plugins/login-gesture/src/modulewidget.h create mode 100644 plugins/login-gesture/src/waypointmodel.cpp create mode 100644 plugins/login-gesture/src/waypointmodel.h create mode 100644 plugins/login-gesture/src/waypointwidget.cpp create mode 100644 plugins/login-gesture/src/waypointwidget.h create mode 100755 plugins/login-gesture/translate_generation.sh create mode 100644 plugins/login-gesture/translations/login-gesture_en.ts create mode 100644 plugins/login-gesture/translations/login-gesture_zh_CN.ts create mode 100644 plugins/login-gesture/translations/login-gesture_zh_HK.ts create mode 100644 plugins/login-gesture/translations/login-gesture_zh_TW.ts create mode 100644 plugins/login-gesture/utils/tokenCrypt.h create mode 100644 plugins/login-gesture/utils/translastiondoc.cpp create mode 100644 plugins/login-gesture/utils/translastiondoc.h create mode 100644 plugins/login-gesture/utils/userservice.cpp create mode 100644 plugins/login-gesture/utils/userservice.h create mode 100644 src/global_util/qt-compat-helper.cpp create mode 100644 src/global_util/qt-compat-helper.h create mode 100644 src/global_util/signal_bridge.h create mode 100644 src/session-widgets/mfasequencecontrol.cpp create mode 100644 src/session-widgets/mfasequencecontrol.h create mode 100644 src/widgets/shutdown_black_widget.cpp create mode 100644 src/widgets/shutdown_black_widget.h diff --git a/.gitignore b/.gitignore index 74d8701a..f4f7381b 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,6 @@ build*/ tests/report/* .transifexrc .cache/ - +.cursor/ # for snipe src/global_util/dbus/* diff --git a/.tx/config b/.tx/config index cc140ace..37e5948a 100644 --- a/.tx/config +++ b/.tx/config @@ -8,3 +8,9 @@ file_filter = translations/dde-session-shell_.ts source_file = translations/dde-session-shell_en.ts source_lang = en type = QT + +[o:linuxdeepin:p:deepin-desktop-environment:r:login-gesture] +file_filter = plugins/login-gesture/translations/login-gesture_.ts +source_file = plugins/login-gesture/translations/login-gesture_en.ts +source_lang = en +type = QT diff --git a/CMakeLists.txt b/CMakeLists.txt index 283ed3ac..903028dc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -260,6 +260,7 @@ set(LOCK_SRCS src/widgets/passworderrortipswidget.cpp src/widgets/passworderrortipswidget.h src/global_util/dbusconstant.h + src/global_util/signal_bridge.h ) link_libraries( @@ -324,8 +325,15 @@ set(GREETER_SRCS src/lightdm-deepin-greeter/logincontent.cpp src/lightdm-deepin-greeter/logintipswindow.cpp src/lightdm-deepin-greeter/sessionwidget.cpp + src/global_util/signal_bridge.h ) +if (USE_DEEPIN_WAYLAND) + set(GREETER_SRCS + ${GREETER_SRCS} + ) +endif(USE_DEEPIN_WAYLAND) + add_executable(lightdm-deepin-greeter ${GREETER_SRCS} ${QRCS} diff --git a/configs/org.deepin.dde.lightdm-deepin-greeter.json b/configs/org.deepin.dde.lightdm-deepin-greeter.json index 4b51bdd3..73fa46c8 100644 --- a/configs/org.deepin.dde.lightdm-deepin-greeter.json +++ b/configs/org.deepin.dde.lightdm-deepin-greeter.json @@ -301,6 +301,16 @@ "description": "是否长按小眼睛显示密码,true-鼠按长按小眼睛才显示密码,false-鼠标点击一下就一直显示密码", "permissions": "readwrite", "visibility": "private" + }, + "mfaSequence":{ + "value": {"userType":"","authSequence":[]}, + "serial": 0, + "flags": ["global"], + "name": "mfaSequence", + "name[zh_CN]": "多因认证时控制前端按认证顺序展示UI", + "description": "userType可用值,all:代表所有用户,adDomain:代表域用户,其它可选还有default、native,authSequence中的数组即代表认证类型的顺序", + "permissions": "readwrite", + "visibility": "private" } } } diff --git a/configs/org.deepin.dde.lock.json b/configs/org.deepin.dde.lock.json index 2de40b78..78c96fe8 100755 --- a/configs/org.deepin.dde.lock.json +++ b/configs/org.deepin.dde.lock.json @@ -281,6 +281,26 @@ "description": "是否长按小眼睛显示密码,true-鼠按长按小眼睛才显示密码,false-鼠标点击一下就一直显示密码", "permissions": "readwrite", "visibility": "private" + }, + "mfaSequence":{ + "value": {"userType":"","authSequence":[]}, + "serial": 0, + "flags": ["global"], + "name": "mfaSequence", + "name[zh_CN]": "多因认证时控制前端按认证顺序展示UI", + "description": "userType可用值,all:代表所有用户,adDomain:代表域用户,其它可选还有default、native,authSequence中的数组即代表认证类型的顺序", + "permissions": "readwrite", + "visibility": "private" + }, + "enableShutdownBlackWidget":{ + "value": true, + "serial": 0, + "flags": ["global"], + "name": "enableShutdownBlackWidget", + "name[zh_CN]": "是否打开关机、重启黑屏界面", + "description": "是否打开关机、重启黑屏界面;false:不开,true:开启。默认值为true;", + "permissions": "readwrite", + "visibility": "private" } } } diff --git a/debian/control b/debian/control index 2e3ae146..ae942184 100644 --- a/debian/control +++ b/debian/control @@ -44,13 +44,14 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${dist:Depends}, libssl1.1, libxcb-util0 | libxcb-util1, dde-dconfig-daemon, - dde-wayland-config(>>1.0.10-1) + dde-wayland-config(>>1.0.10-1), + dss-wechat-auth-plugin Provides: lightdm-greeter, deepin-greeter-wayland Recommends: onboard, + dss-captcha-plugin, dss-network-plugin, - dde-wallpaper-cache, - dss-wechat-auth-plugin + dde-wallpaper-cache Conflicts: dde-workspace, deepin-notifications, dde-session-ui (<< 5.0.0), @@ -70,3 +71,18 @@ Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Description: deepin desktop-environment - dde-session-shell module development files DDE Session Shell module development files of deepin desktop-environment + +Package: dde-gesture-login +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends}, ${dist:Depends}, + deepin-desktop-schemas (>=5.9.14), + dde-daemon (>=5.13.12), + startdde (>=5.10.24), + deepin-authenticate(>=1.2.27), + libssl-dev, + dde-dconfig-daemon, + dde-wayland-config(>>1.0.10-1) +Conflicts: dss-gesture-login-plugin +Description: deepin desktop-environment - dde-session-shell login plugin files + reset-pattern-dialog - modify gesture password enrolled, or create new gesture password + liblogin-gesture.so - support gesture password login on greeter and lock diff --git a/debian/dde-gesture-login.install b/debian/dde-gesture-login.install new file mode 100644 index 00000000..4b5c5494 --- /dev/null +++ b/debian/dde-gesture-login.install @@ -0,0 +1,6 @@ +usr/share/dde-gesture-login/translations +usr/lib/dde-session-shell/modules/liblogin-gesture.so +usr/lib/dde-control-center/reset-pattern-dialog +usr/share/dsg/configs/org.deepin.dde.lock/org.deepin.dde.dss-login-gesture.json +usr/share/dsg/configs/org.deepin.dde.lightdm-deepin-greeter/org.deepin.dde.dss-login-gesture.json +lib/dde-session-shell/modules/config.d/login-gesture.json diff --git a/debian/dde-gesture-login.postinst b/debian/dde-gesture-login.postinst new file mode 100644 index 00000000..5304b499 --- /dev/null +++ b/debian/dde-gesture-login.postinst @@ -0,0 +1,21 @@ +#!/bin/sh + +set -e + +if getent passwd deepin-password-admin >/dev/null; then + if which deluser >/dev/null; then + deluser --system deepin-password-admin -q || echo "Could not remove deepin-password-admin user." + fi +fi + +if getent group deepin-password-admin >/dev/null; then + if which delgroup >/dev/null; then + delgroup --system deepin-password-admin || echo "Could not remove deepin-password-admin group." + fi +fi + +chown deepin-daemon:deepin-daemon /usr/lib/dde-control-center/reset-pattern-dialog +chmod 500 /usr/lib/dde-control-center/reset-pattern-dialog + +#DEBHELPER# +exit 0 diff --git a/debian/dde-gesture-login.sysusers b/debian/dde-gesture-login.sysusers new file mode 100644 index 00000000..23ce2ba6 --- /dev/null +++ b/debian/dde-gesture-login.sysusers @@ -0,0 +1,4 @@ +# This file is part of dde-control-center +#Type Name ID GECOS Home directory Shell +u deepin-daemon - "" - - + diff --git a/debian/dde-session-shell.install b/debian/dde-session-shell.install index 28d24944..61b7dc97 100644 --- a/debian/dde-session-shell.install +++ b/debian/dde-session-shell.install @@ -1,9 +1,14 @@ etc usr/bin -usr/share +usr/share/applications +usr/share/deepin-debug-config +usr/share/dsg +usr/share/glib-2.0 usr/share/xgreeters -usr/share/dde-session-shell/greeters.d/x -usr/share/dde-session-shell/greeters.d/wayland +usr/share/dbus-1 +usr/share/deepin-authentication +usr/share/deepin-log-viewer +usr/share/dde-session-shell usr/share/lightdm/lightdm.conf.d -usr/lib/dde-session-shell/modules -usr/lib/*/security \ No newline at end of file +usr/lib/dde-session-shell/modules/libone-key-login.so +usr/lib/*/security diff --git a/files/wayland/lightdm-deepin-greeter-wayland b/files/wayland/lightdm-deepin-greeter-wayland index e9493a94..98f4caae 100755 --- a/files/wayland/lightdm-deepin-greeter-wayland +++ b/files/wayland/lightdm-deepin-greeter-wayland @@ -33,7 +33,7 @@ device_handle(){ local touchpad=$(dbus_values_get_bool "$dbus_touchpad") if [ "$touchpad" = "true" ]; then - dbus-send --session --dest=org.kde.KWin "$input" org.freedesktop.DBus.Properties.Set string:"org.kde.KWin.InputDevice" string:"tapToClick" variant:boolean:true + dbus-send --session --print-reply --dest=org.kde.KWin "$input" org.freedesktop.DBus.Properties.Set string:"org.kde.KWin.InputDevice" string:"tapToClick" variant:boolean:true fi } diff --git a/interface/base_module_interface.h b/interface/base_module_interface.h index 423217a6..56f8be22 100644 --- a/interface/base_module_interface.h +++ b/interface/base_module_interface.h @@ -38,7 +38,8 @@ class BaseModuleInterface LoginType, // 登陆插件 TrayType, // 托盘插件 FullManagedLoginType, // 全托管插件 - IpcAssistLoginType // 用于接收厂商密码插件 + IpcAssistLoginType, // 用于接收厂商密码插件 + PasswordExtendLoginType, // 密码认证扩展插件,需要两个都认证通过后 }; /** diff --git a/interface/login_module_interface.h b/interface/login_module_interface.h index cd273d1e..90eff7cb 100644 --- a/interface/login_module_interface.h +++ b/interface/login_module_interface.h @@ -75,6 +75,7 @@ enum AuthType { AT_FingerVein = 1 << 5, // 指静脉 AT_Iris = 1 << 6, // 虹膜 AT_Passkey = 1 << 7, // 安全密钥 + AT_Pattern = 1 << 8, // 手势 AT_PAM = 1 << 29, // PAM AT_Custom = 1 << 30, // 自定义 AT_All = -1 // all @@ -98,7 +99,9 @@ enum AuthState { AS_Ended, // 认证已结束,调用 End 之后,每种成功关闭的都会发送此信号,当某种认证类型被锁定时,也会触发此信号 AS_Locked, // 认证已锁定,当认证类型锁定时,触发此信号。该信号不会给出锁定等待时间信息 AS_Recover, // 设备恢复,需要调用 Start 重新开启认证,对应 AS_Exception - AS_Unlocked // 认证解锁,对应 AS_Locked + AS_Unlocked, // 认证解锁,对应 AS_Locked + AS_Unknown, // 未知状态 + AS_VerifyCode, // 需要验证码 }; /** diff --git a/lupdate.sh b/lupdate.sh index a878242b..ba29ec63 100755 --- a/lupdate.sh +++ b/lupdate.sh @@ -1,4 +1,8 @@ #!/bin/bash -lupdate ./ -ts -no-obsolete -locations none translations/dde-session-shell_en.ts +lupdate ./interface ./src ./tests -ts -no-obsolete -locations none translations/dde-session-shell_en.ts +cd plugins/login-gesture +./lupdate.sh +cd ../../ + #tx push -s -b m20 # Push the files that need to be translated #tx pull -s -b m20 # Pull the translated files diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index edba21ae..23b436e5 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -3,3 +3,4 @@ if (DISABLE_DSS_SNIPE) endif() # add_subdirectory(examples) # add_subdirectory(assist_login) +# add_subdirectory("login-gesture") diff --git a/plugins/login-gesture/CMakeLists.txt b/plugins/login-gesture/CMakeLists.txt new file mode 100644 index 00000000..2ce96ce6 --- /dev/null +++ b/plugins/login-gesture/CMakeLists.txt @@ -0,0 +1,72 @@ +find_package(DtkWidget REQUIRED) +find_package(DtkCore REQUIRED) + +include(GNUInstallDirs) + +include_directories( + ${DFrameworkDBus_INCLUDE_DIRS} + global + src + resetDialog + utils +) + +link_libraries( + ${Qt_LIBS} + ${DtkWidget_LIBRARIES} + ${DtkCore_LIBRARIES} + crypt +) + +set(LIB_NAME login-gesture) +set(BIN_NAME reset-pattern-dialog) + +file(GLOB_RECURSE SHARED_SOURCE + ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/utils/*.cpp +) + +file(GLOB_RECURSE LIB_SOURCE + ${CMAKE_CURRENT_SOURCE_DIR}/loginPlugin/*.cpp +) + +file(GLOB_RECURSE RESET_SOURCE + ${CMAKE_CURRENT_SOURCE_DIR}/resetDialog/*.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/resetDialog/*.qrc +) + +if (CMAKE_BUILD_TYPE STREQUAL "Debug") + ADD_DEFINITIONS(-DQM_FILES_DIR="${CMAKE_BINARY_DIR}/plugins/login-gesture/translations") +else() + ADD_DEFINITIONS(-DQM_FILES_DIR="/usr/share/dde-gesture-login/translations") +endif() + +set(QRCS + ${CMAKE_CURRENT_SOURCE_DIR}/gesture.qrc +) + +add_library(${LIB_NAME} SHARED + ${SHARED_SOURCE} + ${LIB_SOURCE} + ${QRCS} +) +set_target_properties(${LIB_NAME} PROPERTIES POSITION_INDEPENDENT_CODE ON) + +add_executable(${BIN_NAME} + ${SHARED_SOURCE} + ${RESET_SOURCE} + ${QRCS} +) + +execute_process(COMMAND bash "${CMAKE_CURRENT_SOURCE_DIR}/translate_generation.sh" + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) + +install(TARGETS ${LIB_NAME} LIBRARY DESTINATION lib/dde-session-shell/modules) +install(TARGETS ${BIN_NAME} DESTINATION lib/dde-control-center) + +file(GLOB QM_FILES "${CMAKE_CURRENT_SOURCE_DIR}/translations/*.qm") +install(FILES ${QM_FILES} DESTINATION share/dde-gesture-login/translations) + +install(FILES configs/login-gesture.json DESTINATION /lib/dde-session-shell/modules/config.d) +install(FILES configs/org.deepin.dde.dss-login-gesture.json DESTINATION ${CMAKE_INSTALL_DATADIR}/dsg/configs/org.deepin.dde.lock) +install(FILES configs/org.deepin.dde.dss-login-gesture.json DESTINATION ${CMAKE_INSTALL_DATADIR}/dsg/configs/org.deepin.dde.lightdm-deepin-greeter) \ No newline at end of file diff --git a/plugins/login-gesture/configs/login-gesture.json b/plugins/login-gesture/configs/login-gesture.json new file mode 100644 index 00000000..241eb1b8 --- /dev/null +++ b/plugins/login-gesture/configs/login-gesture.json @@ -0,0 +1,20 @@ +{ + "pluginEnabled": { + "lock": { + "dconfig": { + "appid": "org.deepin.dde.lock", + "resource": "org.deepin.dde.dss-login-gesture", + "subpath": "", + "key": "pluginEnabled" + } + }, + "greeter": { + "dconfig": { + "appid": "org.deepin.dde.lightdm-deepin-greeter", + "resource": "org.deepin.dde.dss-login-gesture", + "subpath": "", + "key": "pluginEnabled" + } + } + } +} \ No newline at end of file diff --git a/plugins/login-gesture/configs/org.deepin.dde.dss-login-gesture.json b/plugins/login-gesture/configs/org.deepin.dde.dss-login-gesture.json new file mode 100644 index 00000000..c209d0a9 --- /dev/null +++ b/plugins/login-gesture/configs/org.deepin.dde.dss-login-gesture.json @@ -0,0 +1,16 @@ +{ + "magic": "dsg.config.meta", + "version": "1.0", + "contents": { + "pluginEnabled": { + "value": false, + "serial": 0, + "flags": ["global"], + "name": "PluginEnabled", + "name[zh_CN]": "是否启用插件", + "description[zh_CN]": "是否启用插件,默认为否。", + "permissions": "readwrite", + "visibility": "private" + } + } +} diff --git a/plugins/login-gesture/gesture.qrc b/plugins/login-gesture/gesture.qrc new file mode 100644 index 00000000..29890590 --- /dev/null +++ b/plugins/login-gesture/gesture.qrc @@ -0,0 +1,5 @@ + + + icons/firstEnroll.svg + + diff --git a/plugins/login-gesture/icons/firstEnroll.svg b/plugins/login-gesture/icons/firstEnroll.svg new file mode 100644 index 00000000..ca0cf976 --- /dev/null +++ b/plugins/login-gesture/icons/firstEnroll.svg @@ -0,0 +1,35 @@ + + + icon/手势密码@2x + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/login-gesture/login.json b/plugins/login-gesture/login.json new file mode 100644 index 00000000..bec81f0d --- /dev/null +++ b/plugins/login-gesture/login.json @@ -0,0 +1,3 @@ +{ + "api": "2.0.0" +} diff --git a/plugins/login-gesture/loginPlugin/gestureloginmodule.cpp b/plugins/login-gesture/loginPlugin/gestureloginmodule.cpp new file mode 100644 index 00000000..7f21c5f3 --- /dev/null +++ b/plugins/login-gesture/loginPlugin/gestureloginmodule.cpp @@ -0,0 +1,230 @@ +// SPDX-FileCopyrightText: 2021 - 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "gestureloginmodule.h" +#include "waypointmodel.h" +#include "modulewidget.h" +#include "userservice.h" +#include "gestureauthworker.h" +#include "gesturemodifyworker.h" + +using namespace gestureLogin; +using namespace gestureSetting; + +namespace dss { +namespace module_v2 { + +GestureLoginModule::GestureLoginModule(QObject *parent) + : QObject(parent) + , m_appData(nullptr) + , m_authCallback(nullptr) + , m_messageCallback(nullptr) + , m_loginWidget(nullptr) + , m_appType(AppType::Lock) +{ + setObjectName(QStringLiteral("gesture-login-plugin")); + // CAUTION: do not call initui here + // when object created, it's not in GUI thread +} + +GestureLoginModule::~GestureLoginModule() +{ + if (m_loginWidget) { + delete m_loginWidget.data(); + } +} + +void GestureLoginModule::init() +{ + updateInfo(); + initWorker(); +} + +QWidget *GestureLoginModule::content() +{ + return m_loginWidget; +} + +void GestureLoginModule::reset() +{ + initUI(); + init(); +} + +void GestureLoginModule::sendToken(const QString &token) +{ + AuthCallbackData data; + data.account = ""; + data.token = token; + data.result = 1; // 固定值,代表本地验证通过,通知dss去校验密码 + m_authCallback(&data, m_appData); +} + +void GestureLoginModule::enroll(const QString &token) +{ + UserService::instance()->enroll(token); +} + +void GestureLoginModule::initUI() +{ + if (m_loginWidget) { + qobject_cast(m_loginWidget)->reset(); + return; + } + + m_loginWidget = new ModuleWidget(); +} + +void GestureLoginModule::setAppData(AppDataPtr appData) +{ + m_appData = appData; +} + +void GestureLoginModule::setAuthCallback(AuthCallbackFun authCallback) +{ + m_authCallback = authCallback; +} + +void GestureLoginModule::setMessageCallback(MessageCallbackFunc messageCallback) +{ + m_messageCallback = messageCallback; +} + +void GestureLoginModule::updateInfo() +{ + if (!m_messageCallback) { + qWarning() << "message callback func is nullptr"; + return; + } + + // 发送获取属性的请求 + QJsonObject message; + message.insert("CmdType", "GetProperties"); + QJsonArray array; + array.append("AppType"); + array.append("CurrentUser"); + message["Data"] = array; + QJsonDocument doc; + doc.setObject(message); + QString ret = m_messageCallback(doc.toJson(), m_appData); + + // 解析返回值 + QJsonParseError jsonParseError; + const QJsonDocument retDoc = QJsonDocument::fromJson(ret.toLatin1(), &jsonParseError); + if (jsonParseError.error != QJsonParseError::NoError || retDoc.isEmpty()) { + qWarning() << "Failed to analysis AppType info from shell!: " << ret; + return; + } + + QJsonObject obj = retDoc.object(); + if (obj.value("Code").toInt() != 0) { + qWarning() << "Get properties failed, message: " << obj.value("Message").toString(); + return; + } + + QJsonObject data = obj.value("Data").toObject(); + if (data.contains("AppType")) { + m_appType = data.value("AppType").toInt(); + qInfo() << "App type: " << m_appType; + } + + if (data.contains("CurrentUser")) { + QJsonObject user = data.value("CurrentUser").toObject(); + updateUser(user.value("Name").toString()); + qInfo() << "Current user: " << user; + } +} + +void GestureLoginModule::initWorker() +{ + UserService::instance()->setUserName(m_userName); + + auto model = WayPointModel::instance(); + auto gestureState = model->getGestureState(); + + // 无手势,直接开始录入 + if (gestureState == GestureState::NotSet || gestureState == GestureState::SetAndRemoved) { + model->setCurrentMode(Mode::Enroll); + } else { + model->setCurrentMode(Mode::Auth); + } + + // 创建认证流程控制 + auto connectType = Qt::AutoConnection | Qt::UniqueConnection; + // 注意只创建一个对象 + static GestureAuthWorker *authWorker = new GestureAuthWorker(this); + authWorker->setActive(model->currentMode() == Mode::Auth); + connect(this, &GestureLoginModule::onAuthStateChanged, authWorker, &GestureAuthWorker::onAuthStateChanged, Qt::ConnectionType(connectType)); + connect(this, &GestureLoginModule::onLimitsInfoChanged, authWorker, &GestureAuthWorker::onLimitsInfoChanged, Qt::ConnectionType(connectType)); + connect(authWorker, &GestureAuthWorker::requestSendToken, this, &GestureLoginModule::sendToken, Qt::ConnectionType(connectType)); + + // 创建录入流程控制 + static GestureModifyWorker *enrollWorker = new GestureModifyWorker(this); + enrollWorker->setActive(model->currentMode() == Mode::Enroll); + connect(enrollWorker, &GestureModifyWorker::requestSaveToken, this, &GestureLoginModule::enroll, Qt::ConnectionType(connectType)); +} + +// 是否可以由dss去控制这个配置变化 +void GestureLoginModule::updateUser(const QString &userName) +{ + if (m_userName == userName) { + return; + } + + m_userName = userName; + initWorker(); +} + +QString GestureLoginModule::message(const QString &message) +{ + QJsonParseError jsonParseError; + const QJsonDocument messageDoc = QJsonDocument::fromJson(message.toLatin1(), &jsonParseError); + if (jsonParseError.error != QJsonParseError::NoError || messageDoc.isEmpty()) { + qWarning() << "Failed to obtain message from shell!: " << message; + return ""; + } + + QJsonObject retObj; + retObj["Code"] = 0; + retObj["Message"] = "Success"; + + QJsonObject msgObj = messageDoc.object(); + QString cmdType = msgObj.value("CmdType").toString(); + QJsonObject data = msgObj.value("Data").toObject(); + qInfo() << "Cmd type: " << cmdType; + if (cmdType == "CurrentUserChanged") { + updateUser(data.value("Name").toString()); + qDebug() << "Current user changed, user name: " << m_userName; + } else if (cmdType == "GetConfigs") { + QJsonObject retDataObj; + retDataObj["ShowAvatar"] = false; + retDataObj["ShowUserName"] = false; + retDataObj["ShowSwitchButton"] = false; + retDataObj["ShowLockButton"] = false; + retDataObj["DefaultAuthLevel"] = DefaultAuthLevel::Default; + retDataObj["DisableOtherAuthentications"] = false; + retDataObj["AuthType"] = AuthType::AT_Pattern; + retObj["Data"] = retDataObj; + } else if (cmdType == "AuthState") { + int authType = data.value("AuthType").toInt(); + int authState = data.value("AuthState").toInt(); + // 处理认证结果 + // 注意录入相关不在此处理 + Q_EMIT onAuthStateChanged(authType, authState); + } else if (cmdType == "LimitsInfo") { + bool locked = data.value("Locked").toBool(); + int maxTries = data.value("MaxTries").toInt(); + int numFailures = data.value("NumFailures").toInt(); + int unlockSecs = data.value("UnlockSecs").toInt(); + QString unlockTime = data.value("UnlockTime").toString(); + Q_EMIT onLimitsInfoChanged(locked, maxTries, numFailures, unlockSecs, unlockTime); + } + + QJsonDocument doc; + doc.setObject(retObj); + return doc.toJson(); +} + +} // namespace module_v2 +} // namespace dss diff --git a/plugins/login-gesture/loginPlugin/gestureloginmodule.h b/plugins/login-gesture/loginPlugin/gestureloginmodule.h new file mode 100644 index 00000000..f06814f7 --- /dev/null +++ b/plugins/login-gesture/loginPlugin/gestureloginmodule.h @@ -0,0 +1,70 @@ +// SPDX-FileCopyrightText: 2021 - 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef GESTURELOGINMODULE_H +#define GESTURELOGINMODULE_H + +#include "login_module_interface_v2.h" + +#include +#include + +class GesturePannel; + +namespace dss { +namespace module_v2 { + +class GestureLoginModule : public QObject + , public LoginModuleInterfaceV2 +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "com.deepin.dde.shell.Modules_v2.Login" FILE "login.json") + Q_INTERFACES(dss::module_v2::LoginModuleInterfaceV2) + +public: + enum DefaultAuthLevel { + NoDefault = 0, + Default, + StrongDefault + }; + + explicit GestureLoginModule(QObject *parent = nullptr); + ~GestureLoginModule() override; + + void init() override; + inline QString key() const override { return objectName(); } + QWidget *content() override; + inline QString icon() const override { return "code-gesture"; } + void setAppData(AppDataPtr) override; + void setAuthCallback(AuthCallbackFun) override; + void setMessageCallback(MessageCallbackFunc) override; + QString message(const QString &) override; + void reset() override; + +public Q_SLOTS: + void sendToken(const QString &); + void enroll(const QString &); + +Q_SIGNALS: + void onAuthStateChanged(int, int); + void onLimitsInfoChanged(bool, int, int, int, const QString &); + +private: + void initUI(); + void updateInfo(); + void initWorker(); + void updateUser(const QString &); + +private: + AppDataPtr m_appData; + AuthCallbackFun m_authCallback; + MessageCallbackFunc m_messageCallback; + QPointer m_loginWidget; + QString m_userName; + int m_appType; +}; + +} // namespace module_v2 +} // namespace dss +#endif // GESTURELOGINMODULE_H diff --git a/plugins/login-gesture/lupdate.sh b/plugins/login-gesture/lupdate.sh new file mode 100755 index 00000000..1f477afb --- /dev/null +++ b/plugins/login-gesture/lupdate.sh @@ -0,0 +1,5 @@ +#!/bin/bash +lupdate ./ -ts -no-obsolete -locations none translations/login-gesture_en.ts +lupdate ./ -ts -no-obsolete -locations none translations/login-gesture_zh_CN.ts +lupdate ./ -ts -no-obsolete -locations none translations/login-gesture_zh_TW.ts +lupdate ./ -ts -no-obsolete -locations none translations/login-gesture_zh_HK.ts diff --git a/plugins/login-gesture/resetDialog/gesturedialog.cpp b/plugins/login-gesture/resetDialog/gesturedialog.cpp new file mode 100644 index 00000000..af525835 --- /dev/null +++ b/plugins/login-gesture/resetDialog/gesturedialog.cpp @@ -0,0 +1,260 @@ +// SPDX-FileCopyrightText: 2021 - 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "gesturedialog.h" +#include "gesturepannel.h" +#include "modulewidget.h" +#include "resetpatterncontroller.h" +#include "waypointmodel.h" +#include "translastiondoc.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace gestureSetting; +using namespace gestureLogin; + +GestureDialog::GestureDialog(ResetPatternController *worker, QWidget *parent) + : QWidget(parent) + , m_handle(new DPlatformWindowHandle(this)) + , m_titleBar(new QWidget(this)) + , m_mainContent(new QWidget(this)) + , m_mainLayout(new QStackedLayout(m_mainContent)) + , m_authWidget(new ModuleWidget(m_mainContent)) + , m_successWidget(new QWidget(m_mainContent)) + , m_confirmButton(nullptr) + , m_worker(worker) +{ + initUi(); + initConnection(); +} + +GestureDialog::~GestureDialog() {} + +void GestureDialog::initUi() +{ + setAccessibleName("ResetGesturePage"); + + m_handle->setEnableSystemMove(false); + m_handle->setShadowOffset(QPoint(5, 5)); + m_handle->setShadowRadius(20); + + m_titleBar->setFixedHeight(50 * qApp->devicePixelRatio()); + QHBoxLayout *layoutTitle = new QHBoxLayout(m_titleBar); + layoutTitle->setContentsMargins(10, 0, 10, 0); + layoutTitle->setSpacing(0); + // 警告图标 + layoutTitle->addWidget(createTitleIcon(QIcon::fromTheme("dialog-warning"))); + layoutTitle->addStretch(); + // 右侧关闭按钮 + QLabel *closeButton = createTitleIcon(QIcon::fromTheme("dialog-close")); + closeButton->setCursor(Qt::PointingHandCursor); + closeButton->setObjectName("close_button"); + closeButton->installEventFilter(this); + layoutTitle->addWidget(closeButton); + + auto *mainContentLayout = new QVBoxLayout(this); + mainContentLayout->setContentsMargins(0, 0, 0, 0); + mainContentLayout->addWidget(m_titleBar, 0, Qt::AlignTop); + mainContentLayout->addSpacing(30); + mainContentLayout->addWidget(m_mainContent); + mainContentLayout->addWidget(createCancelButton(), 0, Qt::AlignBottom); + + m_mainContent->setMinimumHeight(605); + m_mainLayout->addWidget(m_authWidget); + createSuccessWidget(); + m_mainLayout->addWidget(m_successWidget); + m_mainLayout->setCurrentWidget(m_authWidget); +} + +void GestureDialog::initConnection() +{ + // token输入完成并且已经调用父进程写入 + connect(m_worker, &ResetPatternController::inputFinished, this, &GestureDialog::onInputFinished); + connect(m_confirmButton, &QPushButton::clicked, this, &GestureDialog::close); + m_titleBar->installEventFilter(this); +} + +QWidget* GestureDialog::createCancelButton() +{ + auto *cancelWidget = new QWidget(this); + QHBoxLayout *layout = new QHBoxLayout(cancelWidget); + layout->setContentsMargins(10, 0, 10, 10); + m_confirmButton = new QPushButton(TranslastionDoc::instance()->getDoc(DocIndex::Cancel), cancelWidget); + m_confirmButton->setAttribute(Qt::WA_NoMousePropagation); + layout->addWidget(m_confirmButton); + + return cancelWidget; +} + +QLabel *GestureDialog::createTitleIcon(const QIcon &icon) +{ + QLabel *label = new QLabel(m_titleBar); + QPixmap pixmap = icon.pixmap(QSize(40, 40) * qApp->devicePixelRatio()); + pixmap.setDevicePixelRatio(qApp->devicePixelRatio()); + label->setFixedHeight(40 * qApp->devicePixelRatio()); + label->setPixmap(pixmap); + return label; +} + +void GestureDialog::createSuccessWidget() +{ + TranslastionDoc *transDoc = TranslastionDoc::instance(); + auto *resultLayout = new QVBoxLayout(m_successWidget); + resultLayout->setContentsMargins(0, 8, 0, 0); + + QLabel *labelTitle = new QLabel(m_successWidget); + DFontSizeManager::instance()->bind(labelTitle, DFontSizeManager::T3); + labelTitle->setAlignment(Qt::AlignCenter); + labelTitle->setText(transDoc->getDoc(DocIndex::SetPasswd)); + resultLayout->addWidget(labelTitle); + + auto *indicatorWidget = new QWidget(m_successWidget); + indicatorWidget->setFixedHeight(128); + indicatorWidget->setObjectName("indicatorSuccess"); + indicatorWidget->installEventFilter(this); + + resultLayout->addSpacing(10); + resultLayout->addWidget(indicatorWidget); + + auto *labelSuccess = new QLabel(transDoc->getDoc(DocIndex::EnrollDone), m_successWidget); + DFontSizeManager::instance()->bind(labelSuccess, DFontSizeManager::T5); + labelSuccess->setAlignment(Qt::AlignHCenter); + resultLayout->setSpacing(35); + resultLayout->addWidget(labelSuccess); + resultLayout->addStretch(); +} + +bool GestureDialog::eventFilter(QObject *watched, QEvent *event) +{ + if (watched == m_titleBar) { + switch (event->type()) { + case QEvent::MouseButtonPress: { + QMouseEvent *mouseEvent = static_cast(event); + if ((mouseEvent->buttons().testFlag(Qt::LeftButton))) { + m_dragPosition = mapFromGlobal(mouseEvent->globalPos()); + } + } break; + case QEvent::MouseButtonRelease: { + m_dragPosition = QPoint(); + } break; + case QEvent::MouseMove: { + // 如果按下的点为空,则不做任何处理 + if (m_dragPosition.isNull()) + break; + + QMouseEvent *mouseEvent = static_cast(event); + int x = mouseEvent->globalX() - m_dragPosition.x(); + int y = mouseEvent->globalY() - m_dragPosition.y(); + + move(x, y); + } break; + default: break; + } + } else if (watched->isWidgetType()) { + QWidget *widget = qobject_cast(watched); + if (widget) { + if (widget->objectName() == "indicatorSuccess" && event->type() == QEvent::Paint) { + // 绘制图标 + QSvgRenderer renderer(QString(":/success.svg")); + QPixmap pixmap(QSize(128, 128) * qApp->devicePixelRatio()); + pixmap.fill(Qt::transparent); + QPainter painter; + painter.begin(&pixmap); + painter.setRenderHints(QPainter::SmoothPixmapTransform | QPainter::Antialiasing); + painter.setPen(Qt::NoPen); + renderer.render(&painter); + painter.end(); + QSize pixmapSize(widget->height(), widget->height()); + QRect pixmapRect((widget->width() - pixmapSize.width()) / 2, + (widget->height() - pixmapSize.height()) / 2, + widget->height(), widget->height()); + painter.begin(widget); + painter.drawPixmap(pixmapRect, pixmap); + painter.end(); + } else if (widget->objectName() == "close_button" + && event->type() == QEvent::MouseButtonRelease) { + close(); + } + } + } + + return QWidget::eventFilter(watched, event); +} + +void GestureDialog::onInputFinished() +{ + // 两次校验通过 + m_mainLayout->setCurrentWidget(m_successWidget); + m_confirmButton->setText(TranslastionDoc::instance()->getDoc(DocIndex::Ok)); +} + +ResetPatternController *Manager::m_controller = nullptr; + +Manager::Manager(int old_file_id, int new_file_id, QObject *parent) + : QObject (parent) + , m_gestureDialog(nullptr) + , m_old_file_id (old_file_id) + , m_new_file_id(new_file_id) +{ +} + +Manager::~Manager() +{ + free(); + m_gestureDialog->deleteLater(); +} + +void Manager::start() +{ + if (m_gestureDialog) + return; + + if (!m_controller) { + m_controller = new ResetPatternController; + if (m_old_file_id >= 0) + m_controller->setOldPasswordFileId(m_old_file_id); + + if (m_new_file_id >= 0) + m_controller->setNewPasswordFileId(m_new_file_id); + + m_controller->start(); + } + + m_gestureDialog = new GestureDialog(m_controller); + + QDesktopWidget *desktopWidget = QApplication::desktop(); + m_gestureDialog->setFixedSize(380, 566); + int x = (desktopWidget->size().width() - m_gestureDialog->width()) / 2; + int y = (desktopWidget->size().height() - m_gestureDialog->height()) / 2; + m_gestureDialog->move(x, y); + m_gestureDialog->setWindowFlags(m_gestureDialog->windowFlags() | Qt::FramelessWindowHint); + m_gestureDialog->show(); +} + +void Manager::exit(int retCode) +{ + free(); + qDebug() << "exit code" << retCode; + qApp->quit(); +} + +void Manager::free() +{ + qDebug() << "free manager"; + if (m_controller) { + m_controller->close(); + m_controller->deleteLater(); + } +} diff --git a/plugins/login-gesture/resetDialog/gesturedialog.h b/plugins/login-gesture/resetDialog/gesturedialog.h new file mode 100644 index 00000000..9dcbcbc3 --- /dev/null +++ b/plugins/login-gesture/resetDialog/gesturedialog.h @@ -0,0 +1,82 @@ +// SPDX-FileCopyrightText: 2021 - 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef GESTUREDIALOG_H +#define GESTUREDIALOG_H + +#include + +DWIDGET_USE_NAMESPACE + +class QStackedLayout; +class ResetPatternController; + +namespace gestureLogin { +class GesturePannel; +class ModuleWidget; +} + +DWIDGET_BEGIN_NAMESPACE +class DTitlebar; +class DPlatformWindowHandle; +DWIDGET_END_NAMESPACE + +namespace gestureSetting { + +class GestureDialog : public QWidget +{ + Q_OBJECT + +public: + explicit GestureDialog(ResetPatternController *worker, QWidget *parent = nullptr); + ~GestureDialog() override; + +protected: + bool eventFilter(QObject *watched, QEvent *event) override; // 过滤鼠标事件 + +private: + void initUi(); + void initConnection(); + void createSuccessWidget(); + QWidget* createCancelButton(); + QLabel *createTitleIcon(const QIcon &icon); + +private slots: + void onInputFinished(); + +private: + DPlatformWindowHandle *m_handle; + QWidget *m_titleBar; + QWidget *m_mainContent; + QStackedLayout *m_mainLayout; + gestureLogin::ModuleWidget *m_authWidget; + QWidget *m_successWidget; + QPushButton *m_confirmButton; + ResetPatternController *m_worker; + QPoint m_dragPosition; +}; + +class Manager : public QObject +{ + Q_OBJECT + +public: + explicit Manager(int old_file_id, int new_file_id, QObject *parent = nullptr); + ~Manager() override; + + void start(); + + static void exit(int retCode); + static void free(); + +private: + static ResetPatternController *m_controller; + GestureDialog *m_gestureDialog; + int m_old_file_id; + int m_new_file_id; +}; + +} // namespace gestureSetting + +#endif // GESTUREDIALOG_H diff --git a/plugins/login-gesture/resetDialog/main.cpp b/plugins/login-gesture/resetDialog/main.cpp new file mode 100644 index 00000000..ea524ae8 --- /dev/null +++ b/plugins/login-gesture/resetDialog/main.cpp @@ -0,0 +1,138 @@ +// SPDX-FileCopyrightText: 2018 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "gesturedialog.h" + +#include +#include + +#include +#include +#include + +#include + +DCORE_USE_NAMESPACE +DWIDGET_USE_NAMESPACE + +using namespace gestureLogin; +using namespace gestureSetting; + +const bool IsWayland = qgetenv("XDG_SESSION_TYPE").contains("wayland"); + +double getScaleFormConfig() +{ + double defaultScaleFactor = 1.0; + QDBusInterface configInter("com.deepin.system.Display", + "/com/deepin/system/Display", + "com.deepin.system.Display", + QDBusConnection::systemBus()); + if (!configInter.isValid()) { + return defaultScaleFactor; + } + QDBusReply configReply = configInter.call("GetConfig"); + if (configReply.isValid()) { + QString config = configReply.value(); + QJsonParseError jsonError; + QJsonDocument jsonDoc(QJsonDocument::fromJson(config.toLatin1(), &jsonError)); + if (jsonError.error == QJsonParseError::NoError) { + QJsonObject rootObj = jsonDoc.object(); + QJsonObject Config = rootObj.value("Config").toObject(); + double scaleFactor = Config.value("ScaleFactors").toObject().value("ALL").toDouble(); + qDebug() << "Scale factor from system display config: " << scaleFactor; + if(scaleFactor == 0.0) { + scaleFactor = defaultScaleFactor; + } + return scaleFactor; + } + return defaultScaleFactor; + } + + qWarning() << "DBus call `GetConfig` failed, reply is invaild, error: " << configReply.error().message(); + return defaultScaleFactor; +} + +void setQtScaleFactorEnv() +{ + const double scaleFactor = getScaleFormConfig(); + qDebug() << "Final scale factor: " << scaleFactor; + if (scaleFactor > 0.0) { + qputenv("QT_SCALE_FACTOR", QByteArray::number(scaleFactor).toStdString().c_str()); + } else { + qputenv("QT_SCALE_FACTOR", "1"); + } +} + +int main(int argc, char *argv[]) +{ + if (qEnvironmentVariableIsEmpty("XDG_CURRENT_DESKTOP")) { + qputenv("XDG_CURRENT_DESKTOP", "Deepin"); + } + + if (IsWayland) { + setQtScaleFactorEnv(); + } + + QString user(qgetenv("USER")); + if (!user.isEmpty()) { + QString runtimeDirStr = QString("/tmp/runtime-%1").arg(user); + QFileInfo runtimeDir(runtimeDirStr); + if (runtimeDir.exists()) { + if (runtimeDir.isDir() && runtimeDir.owner() == user) { + qputenv("XDG_RUNTIME_DIR", runtimeDirStr.toUtf8()); + qputenv("DBUS_SESSION_BUS_ADDRESS", QString("%1/bus").arg(runtimeDirStr).toUtf8()); + } + } else { + qputenv("XDG_RUNTIME_DIR", runtimeDirStr.toUtf8()); + qputenv("DBUS_SESSION_BUS_ADDRESS", QString("%1/bus").arg(runtimeDirStr).toUtf8()); + } + } + + DApplication a(argc, argv); + a.setQuitOnLastWindowClosed(true); + a.setAttribute(Qt::AA_EnableHighDpiScaling, true); + a.setAttribute(Qt::AA_UseHighDpiPixmaps, true); + + a.setOrganizationName("deepin"); + a.setApplicationName("reset-pattern-dialog"); + + // 由于这个应用是由deepin-password-admin管理,因此需要指定日志目录 + QString logPath = QString("/tmp/%1/%1.log").arg(a.applicationName()); + QDir logDir = QFileInfo(logPath).dir(); + if (!logDir.exists()) + logDir.mkdir(logDir.path()); + DLogManager::setlogFilePath(logPath); + DLogManager::registerFileAppender(); + DLogManager::registerConsoleAppender(); + + QCommandLineOption oldPwdOption(QStringList() << "old", "read current password which set it", "oldPasswordId"); + QCommandLineOption newPwdOption(QStringList() << "new", "write the new password which you will set", "newPasswordId"); + QCommandLineOption userOption(QStringList() << "u", "caller's name", "user"); + QCommandLineOption fOption(QStringList() << "f", "caller's full name", "f"); + QCommandLineOption aOption(QStringList() << "a", "call's app", "a"); + QCommandLineParser parser; + parser.addOption(oldPwdOption); + parser.addOption(newPwdOption); + parser.addOption(userOption); + parser.addOption(fOption); + parser.addOption(aOption); + parser.process(a); + + + int old_file_id = -1; + int new_file_id = -1; + if (parser.isSet(oldPwdOption)) { + old_file_id = parser.value(oldPwdOption).toInt(); + } + if (parser.isSet(newPwdOption)) { + new_file_id = parser.value(newPwdOption).toInt(); + } + Manager manager(old_file_id, new_file_id); + manager.start(); + + std::signal(SIGTERM, Manager::exit); + std::signal(SIGKILL, Manager::exit); + + return a.exec(); +} diff --git a/plugins/login-gesture/resetDialog/resetdialog.qrc b/plugins/login-gesture/resetDialog/resetdialog.qrc new file mode 100644 index 00000000..388fff89 --- /dev/null +++ b/plugins/login-gesture/resetDialog/resetdialog.qrc @@ -0,0 +1,5 @@ + + + success.svg + + diff --git a/plugins/login-gesture/resetDialog/resetpatterncontroller.cpp b/plugins/login-gesture/resetDialog/resetpatterncontroller.cpp new file mode 100644 index 00000000..e449174e --- /dev/null +++ b/plugins/login-gesture/resetDialog/resetpatterncontroller.cpp @@ -0,0 +1,106 @@ +// SPDX-FileCopyrightText: 2021 - 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "resetpatterncontroller.h" +#include "gestureauthworker.h" +#include "gesturemodifyworker.h" +#include "waypointmodel.h" +#include "translastiondoc.h" + +#include +#include + +using namespace gestureLogin; +using namespace gestureSetting; + +ResetPatternController::ResetPatternController(QObject *parent) + : QObject(parent) + , m_fileId(0) + , m_file(new QFile(this)) + , m_authWorker(new gestureLogin::GestureAuthWorker(this)) + , m_modifyWorker(new gestureSetting::GestureModifyWorker(this)) +{ + initConnection(); +} + +ResetPatternController::~ResetPatternController() +{ +} + +void ResetPatternController::setOldPasswordFileId(int fileId) +{ + QFile file; + if (!file.open(fileId, QFile::ReadOnly)) { + qWarning() << "Failed to open file for writing, fileId:" << fileId; + return; + } + m_localPassword = file.readLine(); + // 需要去掉后面的\n,否则无法校验通过 + m_localPassword = m_localPassword.trimmed(); + m_authWorker->setLocalPassword(m_localPassword); +} + +void ResetPatternController::setNewPasswordFileId(int fileId) +{ + m_fileId = fileId; +} + +void ResetPatternController::start() +{ + auto *wayPointModel = WayPointModel::instance(); + + if (m_localPassword.isEmpty()) { + // 如果本地密码为空,则不进入校验阶段,直接请求密码 + wayPointModel->setCurrentMode(Mode::Enroll); + m_modifyWorker->setActive(true); + } else { + // 如果本地密码不为空,则先进行本地密码校验 + wayPointModel->setCurrentMode(Mode::Auth); + m_authWorker->setActive(true); + } +} + +void ResetPatternController::close() +{ + if (m_file->isOpen()) { + m_file->close(); + } +} + +void ResetPatternController::initConnection() +{ + auto *wayPointModel = WayPointModel::instance(); + + connect(wayPointModel, &WayPointModel::authDone, this, [this, wayPointModel] { + // 如果当前是重置密码模式,不做任何处理 + if (wayPointModel->currentMode() == Mode::Enroll) + return; + + // 停止认证的worker + m_authWorker->setActive(false); + // 密码验证通过后,设置当前为重置密码模式 + wayPointModel->setCurrentMode(Mode::Enroll); + // 进入设置密码页面后,设置标题和提示内容 + m_modifyWorker->setActive(true); + }); + + connect(m_modifyWorker, &GestureModifyWorker::inputFinished, this, &ResetPatternController::inputFinished); + connect(m_modifyWorker, &GestureModifyWorker::requestSaveToken, this, &ResetPatternController::onSaveToken); +} + +void ResetPatternController::onSaveToken(const QString &token) +{ + // 向父进程写入token + if (m_file->isOpen()) + m_file->close(); + + if (!m_file->open(m_fileId, QFile::WriteOnly)) { + qWarning() << "Failed to open file for writing, fileId:" << m_fileId; + return; + } + + qDebug() << "Writing token"; + QByteArray tmpData = QString("%1\n").arg(token).toLocal8Bit(); + m_file->write(tmpData, tmpData.length()); +} diff --git a/plugins/login-gesture/resetDialog/resetpatterncontroller.h b/plugins/login-gesture/resetDialog/resetpatterncontroller.h new file mode 100644 index 00000000..20ae4dba --- /dev/null +++ b/plugins/login-gesture/resetDialog/resetpatterncontroller.h @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: 2021 - 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RESETPATTERNCONTROLLER_H +#define RESETPATTERNCONTROLLER_H + +#include + +class QFile; + +namespace gestureLogin { +class GestureAuthWorker; +} + +namespace gestureSetting { +class GestureModifyWorker; +} + +class ResetPatternController : public QObject +{ + Q_OBJECT + +public: + explicit ResetPatternController(QObject *parent = nullptr); + ~ResetPatternController() override; + void setOldPasswordFileId(int fileId); + void setNewPasswordFileId(int fileId); + void start(); + void close(); + +signals: + void inputFinished(); // 第二次输入token合法并完成 + +private: + void initConnection(); + +private slots: + void onSaveToken(const QString &token); + +private: + int m_fileId; + QString m_localPassword; + QFile *m_file; + gestureLogin::GestureAuthWorker *m_authWorker; + gestureSetting::GestureModifyWorker *m_modifyWorker; +}; + +#endif // RESETPATTERNCONTROLLER_H diff --git a/plugins/login-gesture/resetDialog/success.svg b/plugins/login-gesture/resetDialog/success.svg new file mode 100644 index 00000000..3e5e1461 --- /dev/null +++ b/plugins/login-gesture/resetDialog/success.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/plugins/login-gesture/src/gestureauthworker.cpp b/plugins/login-gesture/src/gestureauthworker.cpp new file mode 100644 index 00000000..a905780b --- /dev/null +++ b/plugins/login-gesture/src/gestureauthworker.cpp @@ -0,0 +1,260 @@ +// SPDX-FileCopyrightText: 2021 - 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "gestureauthworker.h" +#include "waypointmodel.h" +#include "login_module_interface_v2.h" +#include "translastiondoc.h" +#include "tokenCrypt.h" + +#include + +using namespace gestureLogin; + +// 负责手势认证过程 +GestureAuthWorker::GestureAuthWorker(QObject *parent) + : QObject(parent) + , m_userService(nullptr) + , m_stateMachine(nullptr) + +{ + m_unLockTimer.setSingleShot(true); + m_remaindTimer.setSingleShot(false); + m_remaindTimer.setInterval(1000 * 60); + + initConnection(); +} + +void GestureAuthWorker::setLocalPassword(const QString &localPassword) +{ + m_localPasswd = localPassword; +} + +bool GestureAuthWorker::gestureEnabled() +{ + return true; +} + +bool GestureAuthWorker::gestureExist() +{ + return true; +} + +void GestureAuthWorker::setActive(bool active) +{ + if (active) { + initState(); + } else { + if (m_stateMachine) { + m_stateMachine->stop(); + m_stateMachine->deleteLater(); + m_stateMachine = nullptr; + } + } +} + +void GestureAuthWorker::initConnection() +{ + WayPointModel *model = WayPointModel::instance(); + connect(model, &WayPointModel::gestureStateChanged, this, [this, model](int state) { + if (model->appType() == ModelAppType::LoginLock) { + bool enrolled = state == GestureState::Set; + setActive(enrolled); + model->setCurrentMode(enrolled ? Mode::Auth : Mode::Enroll); + } + }); + + // 只处理认证过程 + connect(model, &WayPointModel::pathDone, this, [this, model] { + // 仅处理认证过程 + auto mode = model->currentMode(); + auto type = model->appType(); + if (mode == Mode::Auth) { + // 在登录锁屏上解锁 + if (type == ModelAppType::LoginLock) { + qDebug() << "worker send token"; + Q_EMIT requestSendToken(model->getToken()); + } + + // 重置密码对话框上的认证是一个单纯的本地密文对比的过程 + if (type == ModelAppType::Reset) { + QString token = gestureEncrypt::cryptUserPassword(model->getToken(), m_localPasswd.toUtf8()); + // 从外部获取的密文与本地对比 + if (token == m_localPasswd) { + Q_EMIT model->authDone(); + } else { + Q_EMIT model->authError(); + } + } + } + }); +} + +void GestureAuthWorker::initState() +{ + if (m_stateMachine) { + delete m_stateMachine; + m_stateMachine = nullptr; + } + + m_stateMachine = new QStateMachine(m_stateMachine); + + auto model = WayPointModel::instance(); + auto tanslator = TranslastionDoc::instance(); + + QString startTitle = tanslator->getDoc(model->appType() == ModelAppType::Reset ? DocIndex::ModifyPasswd : DocIndex::LoginStart); + + // 开始认证 + auto startState = new QState(m_stateMachine); + startState->assignProperty(model, "title", startTitle); + startState->assignProperty(model, "tip", tanslator->getDoc(model->appType() == ModelAppType::Reset ? DocIndex::DrawCurrentPasswd : DocIndex::RequestDrawing)); + // 认证失败,需要更新锁定时间与错误次数 + auto authError = new QState(m_stateMachine); + auto authErrTip = model->errorTextStyle().arg(tanslator->getDoc(DocIndex::AuthErrorWithTimes)); + switch (model->appType()) { + case ModelAppType::LoginLock: + // authError->assignProperty(model, "title", tanslator->getDoc(DocIndex::LoginStart)); + // authError->assignProperty(model, "tip", authErrTip); + break; + case ModelAppType::Reset: + authError->assignProperty(model, "title", tanslator->getDoc(DocIndex::ModifyPasswd)); + authError->assignProperty(model, "tip", tanslator->getDoc(DocIndex::ContactAdmin)); + break; + default: + break; + } + + // 输入错误提示 + auto inputError = new QState(m_stateMachine); + inputError->assignProperty(model, "title", startTitle); + auto pathErrorTip = model->errorTextStyle().arg(tanslator->getDoc(DocIndex::PathError)); + inputError->assignProperty(model, "tip", pathErrorTip); + + startState->addTransition(model, SIGNAL(pathError()), inputError); + startState->addTransition(model, SIGNAL(authError()), authError); + startState->addTransition(model, SIGNAL(authDone()), startState); + + inputError->addTransition(model, SIGNAL(authDone()), startState); + inputError->addTransition(model, SIGNAL(authError()), authError); + inputError->addTransition(model, SIGNAL(pathError()), inputError); + + authError->addTransition(model, SIGNAL(pathError()), inputError); + authError->addTransition(model, SIGNAL(authDone()), startState); + authError->addTransition(model, SIGNAL(authError()), authError); + + m_stateMachine->setInitialState(startState); + m_stateMachine->start(); +} + +void GestureAuthWorker::onAuthStateChanged(int authType, int authState) +{ + using dss::module::AuthType; + using dss::module::AuthState; + + if (WayPointModel::instance()->currentMode() != Mode::Auth) { + return; + } + + if (authType == AuthType::AT_All && authState == AuthState::AS_Success) { + Q_EMIT WayPointModel::instance()->authDone(); + } + + if (authType != AuthType::AT_Pattern) { + return; + } + + switch (authState) { + case AuthState::AS_Success: + Q_EMIT WayPointModel::instance()->authDone(); + break; + case AuthState::AS_Failure: + Q_EMIT WayPointModel::instance()->authError(); + break; + case AuthState::AS_Locked: + // 认证发送的lock状态,代表当前认证锁定 + WayPointModel::instance()->setLocked(true); + break; + case AuthState::AS_Unlocked: + // 认证发送的unlock,代表当前认证解锁 + WayPointModel::instance()->setLocked(false); + break; + case AuthState::AS_Started: + // 插件框架响应的计时器重置认证,代表当前认证解锁 + WayPointModel::instance()->setLocked(false); + break; + default: + break; + } +} + +// 这里只处理UI响应,model的状态设置交给authStatus控制 +void GestureAuthWorker::onLimitsInfoChanged(bool locked, int maxTries, int numFailures, int unlockSecs, const QString &unlockTime) +{ + auto model = WayPointModel::instance(); + if (model->currentMode() != Mode::Auth) { + return; + } + + do { + auto translator = TranslastionDoc::instance(); + // 未锁定状态 + if (numFailures && maxTries && numFailures < maxTries && !unlockSecs) { + uint remains = static_cast(maxTries - numFailures); + auto remainStr = model->errorTextStyle().arg(translator->getDoc(DocIndex::AuthErrorWithTimes).arg(remains)); + model->setProperty("tip", remainStr); + break; + } + + m_unLockTimer.stop(); + m_unLockTimer.disconnect(); + + m_remaindTimer.stop(); + m_remaindTimer.disconnect(); + + model->setProperty("title", TranslastionDoc::instance()->getDoc(DocIndex::LoginStart)); + model->setProperty("tip", TranslastionDoc::instance()->getDoc(DocIndex::RequestDrawing)); + + // unlockSecs 是唯一判断条件 + if (unlockSecs) { + qDebug() << "input should be locked from limit info content"; + // 显示锁定时间 + uint intervalSeconds = QDateTime::fromString(unlockTime, Qt::ISODateWithMs).toLocalTime().toSecsSinceEpoch() + - QDateTime::currentDateTimeUtc().toSecsSinceEpoch(); + // 每次设置时,强制更新计时器 + m_unLockTimer.setInterval(static_cast(intervalSeconds) * 1000); + connect(&m_unLockTimer, &QTimer::timeout, this, [this] { + m_remaindTimer.stop(); + // 信号通知解除界面禁用 + auto model = WayPointModel::instance(); + model->setProperty("title", TranslastionDoc::instance()->getDoc(DocIndex::LoginStart)); + model->setProperty("tip", TranslastionDoc::instance()->getDoc(DocIndex::RequestDrawing)); + }); + + m_lockMinutes = unlockSecs / 60; + if (unlockSecs % 60) { + m_lockMinutes += 1; + } + + auto lockString = translator->getDoc(DocIndex::DeviceLock).arg(m_lockMinutes); + model->setProperty("title", lockString); + model->setProperty("tip", model->errorTextStyle().arg(translator->getDoc(DocIndex::DeviceLockHelp))); + + if (m_lockMinutes) { + connect(&m_remaindTimer, &QTimer::timeout, this, [this] { + // 更新锁定时间 + m_lockMinutes--; + if (m_lockMinutes) { + auto errStr = TranslastionDoc::instance()->getDoc(DocIndex::DeviceLock).arg(m_lockMinutes); + WayPointModel::instance()->setProperty("title", errStr); + } + }); + } + + m_remaindTimer.start(); + m_unLockTimer.start(); + + break; + } + } while (false); +} diff --git a/plugins/login-gesture/src/gestureauthworker.h b/plugins/login-gesture/src/gestureauthworker.h new file mode 100644 index 00000000..3bc25abf --- /dev/null +++ b/plugins/login-gesture/src/gestureauthworker.h @@ -0,0 +1,53 @@ +// SPDX-FileCopyrightText: 2021 - 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef GESTUREWORKER_H +#define GESTUREWORKER_H + +#include +#include +#include +#include + +namespace gestureLogin { +class GestureAuthWorker : public QObject +{ + Q_OBJECT + +public: + GestureAuthWorker(QObject *parent = nullptr); + void setLocalPassword(const QString &localPassword); + + // 功能已开启 + bool gestureEnabled(); + + // 手势已录入 + bool gestureExist(); + + void setActive(bool); + +private: + void initConnection(); + void initState(); + +public Q_SLOTS: + void onAuthStateChanged(int, int); + void onLimitsInfoChanged(bool, int, int, int, const QString &); + +Q_SIGNALS: + void requestSendToken(const QString &); + +private: + QDBusInterface *m_userService; + QStateMachine *m_stateMachine; + QString m_localPasswd; + + int m_lockMinutes; + + QTimer m_remaindTimer; + QTimer m_unLockTimer; +}; +} // namespace gestureLogin + +#endif // GESTUREWORKER_H diff --git a/plugins/login-gesture/src/gesturemodifyworker.cpp b/plugins/login-gesture/src/gesturemodifyworker.cpp new file mode 100644 index 00000000..62171394 --- /dev/null +++ b/plugins/login-gesture/src/gesturemodifyworker.cpp @@ -0,0 +1,96 @@ +// SPDX-FileCopyrightText: 2021 - 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "gesturemodifyworker.h" +#include "waypointmodel.h" +#include "gestureauthworker.h" +#include "translastiondoc.h" + +using namespace gestureSetting; +using namespace gestureLogin; + +GestureModifyWorker::GestureModifyWorker(QObject *parent) + : QObject(parent) +{ + initConnect(); +} + +void GestureModifyWorker::setActive(bool active) +{ + // 无论进入还是离开这个状态都清空内部数据 + m_token = ""; + + auto *wayPointModel = WayPointModel::instance(); + auto connectType = Qt::AutoConnection | Qt::UniqueConnection; + if (active) { + connect(wayPointModel, &WayPointModel::pathError, this, &GestureModifyWorker::onPathError, Qt::ConnectionType(connectType)); + connect(wayPointModel, &WayPointModel::pathDone, this, &GestureModifyWorker::onPathDone, Qt::ConnectionType(connectType)); + wayPointModel->setProperty("title", TranslastionDoc::instance()->getDoc(DocIndex::SetPasswd)); + wayPointModel->setProperty("tip", TranslastionDoc::instance()->getDoc(DocIndex::RequestDrawing)); + } +} + +void GestureModifyWorker::onPathDone() +{ + auto *wayPointModel = WayPointModel::instance(); + + // 这里只处理录入密码的模式 + if (wayPointModel->currentMode() != Mode::Enroll) + return; + + QString token = wayPointModel->getToken(); + // 密码为空,一般是从密码验证成功后进入到当前重设密码的过程,此时无需继续 + if (token.isEmpty()) + return; + + TranslastionDoc *transDoc = TranslastionDoc::instance(); + wayPointModel->clearPath(); + + if (m_token.isEmpty()) { + m_token = token; + // 如果存储的token为空,说明是第一次设置密码成功,此时更改提示为(请重新录入手势密码) + wayPointModel->setProperty("tip", transDoc->getDoc(DocIndex::RequestDrawingAgain)); + } else if (m_token == token) { + // 如果token不为空且两次的token值相同,则认为本次校验通过 + Q_EMIT requestSaveToken(m_token); + Q_EMIT inputFinished(); + } else { + // 如果两次的token不同,回到上一次输入密码的界面来重新录入 + m_token.clear(); + // 给出提示,认为本次和上次校验不一致 + auto tip = wayPointModel->errorTextStyle().arg(transDoc->getDoc(DocIndex::Inconsistent)); + wayPointModel->setProperty("tip", tip); + // 2秒过后文案变为“请绘制手势密码” + QTimer::singleShot(2000, this, [=] { + // 请绘制手势密码 + wayPointModel->setProperty("tip", transDoc->getDoc(DocIndex::RequestDrawing)); + }); + } +} + +void GestureModifyWorker::onPathError() +{ + auto *wayPointModel = WayPointModel::instance(); + + // 这里只处理录入密码的模式 + if (wayPointModel->currentMode() != Mode::Enroll) + return; + + auto pathErrorTip = wayPointModel->errorTextStyle().arg(TranslastionDoc::instance()->getDoc(DocIndex::PathError)); + wayPointModel->setProperty("tip", pathErrorTip); + +} + +void GestureModifyWorker::initConnect() +{ + auto model = WayPointModel::instance(); + if (model->appType() == ModelAppType::LoginLock) { + connect(WayPointModel::instance(), &WayPointModel::gestureStateChanged, this, [&] (int state) { + setActive(state != GestureState::Set ? true : false); + }); + } + + connect(model, &WayPointModel::pathDone, this, &GestureModifyWorker::onPathDone); + connect(model, &WayPointModel::pathError, this, &GestureModifyWorker::onPathError); +} diff --git a/plugins/login-gesture/src/gesturemodifyworker.h b/plugins/login-gesture/src/gesturemodifyworker.h new file mode 100644 index 00000000..c6474bd7 --- /dev/null +++ b/plugins/login-gesture/src/gesturemodifyworker.h @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: 2021 - 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef GESTUREMODIFYWORKER_H +#define GESTUREMODIFYWORKER_H + +#include + +namespace gestureSetting { + +class GestureModifyWorker : public QObject +{ + Q_OBJECT + +public: + explicit GestureModifyWorker(QObject *parent = nullptr); + void setActive(bool active); + +signals: + void inputFinished(); // 第二次输入token合法并完成 + void requestSaveToken(const QString &token); // 请求保存token + +private slots: + void onPathDone(); + void onPathError(); + +private: + void initConnect(); + +private: + QString m_token; +}; + +} // namespace gestureSetting + +#endif // GESTUREMODIFYWORKER_H diff --git a/plugins/login-gesture/src/gesturepannel.cpp b/plugins/login-gesture/src/gesturepannel.cpp new file mode 100644 index 00000000..87b47c45 --- /dev/null +++ b/plugins/login-gesture/src/gesturepannel.cpp @@ -0,0 +1,165 @@ +// SPDX-FileCopyrightText: 2021 - 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "gesturepannel.h" + +#include "waypointwidget.h" +#include "waypointmodel.h" + +#include +#include +#include +#include +#include +#include +#include + +using namespace gestureLogin; + +static int itemsAccount = 9; + +GesturePannel::GesturePannel(QWidget *parent) + : QWidget(parent) + , m_pathStarted(false) +{ + setFocusPolicy(Qt::NoFocus); + setMouseTracking(true); + setFixedSize(250, 250); + setWindowFlag(Qt::FramelessWindowHint); + setAttribute(Qt::WA_TranslucentBackground); + setContentsMargins(0, 0, 0, 0); + init(); + initConnect(); +} + +void GesturePannel::init() +{ + if (!itemsAccount) { + return; + } + + qreal root = qSqrt(itemsAccount); + int column = static_cast(root); + + // 只处理N*N的排列图形 + if (itemsAccount % column != 0) { + return; + } + + int solidSpaceWidth = this->width() / column; + int fixWidth = (solidSpaceWidth - 50) / 2; + + m_itemRects.clear(); + for (int index = 0; index < itemsAccount; index++) { + auto wid = new WayPointWidget(this); + wid->setFocusPolicy(Qt::NoFocus); + wid->setId(index); + // 布局 + int x = this->contentsRect().top() + solidSpaceWidth * (index % column); + int y = this->contentsRect().left() + solidSpaceWidth * (index / column); + wid->setFixedSize(52, 52); + wid->move(x + fixWidth * (index % column), y + fixWidth * (index / column)); + m_widRects.insert(wid, wid->geometry()); + m_itemRects[index] = wid->geometry(); + + connect(WayPointModel::instance(), &WayPointModel::selected, wid, &WayPointWidget::onSelectedById); + connect(wid, &WayPointWidget::arrived, WayPointModel::instance(), &WayPointModel::onPathArrived); + connect(this, &GesturePannel::pathStarted, wid, &WayPointWidget::onPathStarted); + connect(this, &GesturePannel::pathStopped, wid, &WayPointWidget::onPathStopped); + } +} + +void GesturePannel::initConnect() +{ + connect(WayPointModel::instance(), &WayPointModel::selected, this, [this](int, bool) { + update(); + }); + connect(WayPointModel::instance(), &WayPointModel::widgetColorChanged, this, [this] { + update(); + }); + + connect(this, &GesturePannel::pathStopped, WayPointModel::instance(), &WayPointModel::onUserInputDone); + + connect(WayPointModel::instance(), &WayPointModel::inputLocked, this, &QWidget::setDisabled); + setDisabled(WayPointModel::instance()->locked()); +} + +void GesturePannel::reset() +{ +} + +void GesturePannel::mousePressEvent(QMouseEvent *event) +{ + if (!WayPointModel::instance()->locked()) { + // 起点不一定在结点上,仅记录起始点,交给mouseMove处理连接 + releaseMouse(); + // 当输入小于4个点时,将保留界面直到用户再输入 + WayPointModel::instance()->clearPath(); + m_pathStarted = true; + Q_EMIT pathStarted(event->pos()); + + m_currentPos = event->pos(); + + update(); + } + + QWidget::mousePressEvent(event); +} + +void GesturePannel::mouseReleaseEvent(QMouseEvent *event) +{ + if (!WayPointModel::instance()->locked()) { + m_pathStarted = false; + Q_EMIT pathStopped(); + + update(); + } + + QWidget::mouseReleaseEvent(event); +} + +// 最后一个结点的连接线 +void GesturePannel::mouseMoveEvent(QMouseEvent *event) +{ + if (m_pathStarted) { + m_currentPos = event->pos(); + foreach (auto rect, m_widRects) { + if (rect.contains(m_currentPos)) { + auto wid = m_widRects.key(rect); + Q_EMIT wid->arrived(wid->id()); + } + } + update(); + } + + QWidget::mouseMoveEvent(event); +} + +// 选中点的样式变化自己去处理,这里只画连接线 +void GesturePannel::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event) + + if (WayPointModel::instance()->currentPoints().size()) { + QVector wayPoints; + auto currentSelected = WayPointModel::instance()->currentPoints(); + foreach (auto index, currentSelected) { + wayPoints.push_back(m_itemRects.value(index).center()); + } + + if (m_pathStarted) { + wayPoints.push_back(m_currentPos); + } + + auto model = WayPointModel::instance(); + auto colorConfig = model->colorConfig(); + QColor lineColor = model->showErrorStyle() ? colorConfig.warningLine : colorConfig.line; + + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing); + QPen pen(lineColor, 2); + painter.setPen(pen); + painter.drawPolyline(wayPoints.data(), wayPoints.size()); + } +} diff --git a/plugins/login-gesture/src/gesturepannel.h b/plugins/login-gesture/src/gesturepannel.h new file mode 100644 index 00000000..4adc72e2 --- /dev/null +++ b/plugins/login-gesture/src/gesturepannel.h @@ -0,0 +1,69 @@ +// SPDX-FileCopyrightText: 2021 - 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef GESTUREPANNEL_H +#define GESTUREPANNEL_H + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace gestureLogin { +class WayPointWidget; + +class GesturePannel : public QWidget +{ + Q_OBJECT + +public: + explicit GesturePannel(QWidget *parent = nullptr); + + void init(); + void initConnect(); + + inline bool getStarted() const { return m_pathStarted; } + void reset(); + +Q_SIGNALS: + void pathStarted(const QPoint &); + void pathStopped(); + void gestureAuthFaile(); + void gestureAuthDone(); + + void inputError(); + void inputDone(); + + void authError(); + void authDone(); + +protected: + virtual void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; + virtual void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE; + virtual void mouseMoveEvent(QMouseEvent *event) Q_DECL_OVERRIDE; + + virtual void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE; + +private: + void initPannel(); + void initConnections(); + +private: + bool m_pathStarted; + QPainterPath m_gusturePath; + QPoint m_currentPos; + QMap m_itemRects; + + QStateMachine *m_stateMachine; + QMap m_widRects; +}; +} // namespace gestureLogin +#endif // GESTUREPANNEL_H diff --git a/plugins/login-gesture/src/modulewidget.cpp b/plugins/login-gesture/src/modulewidget.cpp new file mode 100644 index 00000000..24a186ec --- /dev/null +++ b/plugins/login-gesture/src/modulewidget.cpp @@ -0,0 +1,167 @@ +// SPDX-FileCopyrightText: 2021 - 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "modulewidget.h" +#include "waypointmodel.h" +#include "translastiondoc.h" + +#include +#include + +#include +#include +#include + +using namespace gestureLogin; + +// 文案、图片与按钮 +// 考虑到 +ModuleWidget::ModuleWidget(QWidget *parent) + : QWidget(parent) + , m_title(nullptr) + , m_tip(nullptr) + , m_firstEnroll(nullptr) + , m_mainLayout(nullptr) + , m_startEnrollBtn(nullptr) + , m_inputWid(nullptr) + , m_layout(nullptr) +{ + this->setContentsMargins(0, 0, 0, 0); + + init(); + initConnection(); +} + +void ModuleWidget::reset() +{ +} + +void ModuleWidget::init() +{ + DGUI_USE_NAMESPACE + + auto model = WayPointModel::instance(); + m_title = new DLabel; + m_title->setFixedHeight(35); + + m_tip = new DTipLabel; + if (model->appType() == ModelAppType::LoginLock) { + m_tip->setForegroundRole(QPalette::ColorRole::Light); + } + + m_tip->setFixedHeight(26); + m_startEnrollBtn = new DPushButton; + m_startEnrollBtn->setFixedSize(200, 54); + + m_title->setVisible(false); + m_tip->setVisible(false); + m_startEnrollBtn->setVisible(false); + + DFontSizeManager::instance()->bind(m_title, DFontSizeManager::T3); + DFontSizeManager::instance()->bind(m_tip, DFontSizeManager::T5); + + m_iconLabel = new DLabel; + m_iconLabel->setVisible(false); + + m_inputWid = new GesturePannel; + m_inputWid->setVisible(false); + + m_layout = new QVBoxLayout; + this->setLayout(m_layout); + + if (model->getGestureState() == GestureState::NotSet && model->appType() == ModelAppType::LoginLock) { + showFirstEnroll(); + } else { + showUserInput(); + } +} + +void ModuleWidget::initConnection() +{ + auto model = WayPointModel::instance(); + + connect(model, &WayPointModel::titleChanged, m_title, [&](const QString &text) { + m_title->setVisible(true); + m_title->setEnabled(true); + m_title->setText(text); + }); + connect(model, &WayPointModel::tipChanged, m_tip, [&](const QString &text) { + m_tip->setVisible(true); + m_tip->setEnabled(true); + m_tip->setText(text); + }); +} + +void ModuleWidget::showFirstEnroll() +{ + DWIDGET_USE_NAMESPACE + + int count = m_layout->count(); + if (count) { + while (count != -1) { + m_layout->takeAt(--count); + } + } + + // 首次录入的布局 + m_layout->addStretch(50); + m_layout->addWidget(m_title, 0, Qt::AlignCenter); + m_layout->addSpacing(10); + m_layout->addWidget(m_tip, 0, Qt::AlignCenter); + m_layout->addSpacing(60); + + QPixmap iconPix = DHiDPIHelper::loadNxPixmap(":/icons/firstEnroll.svg"); + auto scaledPixmap = iconPix.scaled(120, 120, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + m_iconLabel->setPixmap(scaledPixmap); + m_layout->addWidget(m_iconLabel, 0, Qt::AlignCenter); + m_layout->addSpacing(130); + + m_layout->addWidget(m_startEnrollBtn, 0, Qt::AlignCenter); + + m_title->setVisible(true); + m_tip->setVisible(true); + m_startEnrollBtn->setVisible(true); + m_iconLabel->setVisible(true); + + m_layout->addStretch(100); + + // 固定文本,不参与动态变化 + auto trans = TranslastionDoc::instance(); + m_title->setText(trans->getDoc(DocIndex::Enabled)); + m_tip->setText(trans->getDoc(DocIndex::RequestFirstEnroll)); + m_startEnrollBtn->setText(trans->getDoc(DocIndex::SetPasswd)); + + connect(m_startEnrollBtn, &DPushButton::clicked, this, [this] { + showUserInput(); + }); +} + +void ModuleWidget::showUserInput() +{ + int count = m_layout->count(); + if (count) { + while (count != -1) { + m_layout->takeAt(--count); + } + } + + m_startEnrollBtn->setVisible(false); + m_iconLabel->setVisible(false); + + m_layout->setAlignment(Qt::AlignCenter); + m_layout->addWidget(m_title, 0, Qt::AlignCenter); + m_layout->addSpacing(10); + m_layout->addWidget(m_tip, 0, Qt::AlignCenter); + m_layout->addSpacing(40); + m_layout->addWidget(m_inputWid, 0, Qt::AlignCenter); + + m_title->setVisible(true); + m_tip->setVisible(true); + m_inputWid->setVisible(true); + + m_title->setText(WayPointModel::instance()->title()); + m_tip->setText(WayPointModel::instance()->tip()); + + m_layout->addStretch(100); +} diff --git a/plugins/login-gesture/src/modulewidget.h b/plugins/login-gesture/src/modulewidget.h new file mode 100644 index 00000000..bfa91218 --- /dev/null +++ b/plugins/login-gesture/src/modulewidget.h @@ -0,0 +1,48 @@ +// SPDX-FileCopyrightText: 2021 - 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef LOGINWIDGET_H +#define LOGINWIDGET_H + +#include "gesturepannel.h" + +#include +#include +#include + +#include +#include + +DWIDGET_USE_NAMESPACE + +namespace gestureLogin { +class ModuleWidget : public QWidget +{ + Q_OBJECT + +public: + explicit ModuleWidget(QWidget *parent = nullptr); + void reset(); + +public Q_SLOTS: + void showUserInput(); + +private: + void init(); + void initConnection(); + void showFirstEnroll(); // 登录锁屏上首次设置 + +private: + // 大标题与小标题 + DLabel *m_title; + DLabel *m_tip; + QLayout *m_firstEnroll; + QLayout *m_mainLayout; + DPushButton *m_startEnrollBtn; + DLabel *m_iconLabel; + GesturePannel *m_inputWid; + QVBoxLayout *m_layout; +}; +} // namespace gestureLogin +#endif // LOGINWIDGET_H diff --git a/plugins/login-gesture/src/waypointmodel.cpp b/plugins/login-gesture/src/waypointmodel.cpp new file mode 100644 index 00000000..98ae22e4 --- /dev/null +++ b/plugins/login-gesture/src/waypointmodel.cpp @@ -0,0 +1,367 @@ +// SPDX-FileCopyrightText: 2021 - 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "waypointmodel.h" +#include "tokenCrypt.h" + +#include +#include + +using namespace gestureLogin; + +// 明文内容字典 +const QMap kContent = {{0, "0"}, + {1, "1"}, + {2, "2"}, + {3, "3"}, + {4, "4"}, + {5, "5"}, + {6, "6"}, + {7, "7"}, + {8, "8"}}; + +WayPointModel::WayPointModel(QObject *parent) + : QObject(parent) + , m_size(9) // 3 * 3, 如果有需求,改从配置获取 + , m_currentMode(Mode::Auth) + , m_gestureStateFlag(-1) + , m_gestureEnable(false) + , m_showErrorStyle(false) + , m_locked(false) + , m_localeName("") +{ + initColorConfig(); + connect(this, &WayPointModel::authDone, this, &WayPointModel::clearPath); + connect(this, &WayPointModel::authError, this, &WayPointModel::clearPath); +} + +WayPointModel *WayPointModel::instance() +{ + static WayPointModel model; + return &model; +} + +const QList &WayPointModel::currentPoints() +{ + return m_selectedPoints; +} + +ModelAppType WayPointModel::appType() const +{ + auto name = QApplication::applicationName(); + if (name.contains("lock", Qt::CaseInsensitive) || name.contains("greeter", Qt::CaseInsensitive)) { + return ModelAppType::LoginLock; + } + + if (name.contains("reset", Qt::CaseInsensitive)) { + return ModelAppType::Reset; + } + + return ModelAppType::Unknow; +} + +bool WayPointModel::isLockApp() +{ + return QApplication::applicationName().contains("lock", Qt::CaseInsensitive); +} + +// 通过回调到查询 +bool WayPointModel::isGestureEnabled() +{ + return m_gestureEnable; +} + +const GestureColors &WayPointModel::colorConfig() +{ + return m_colorConfig; +} + +QString WayPointModel::errorTextStyle() const +{ + return "%1"; +} + +void WayPointModel::setLocaleName(const QString &locale) +{ + auto content = locale.split("."); + if (content.size()) { + auto name = content[0]; + if (m_localeName != name) { + m_localeName = name; + Q_EMIT localeChanged(m_localeName); + } + } +} + +void WayPointModel::initColorConfig() +{ + DGUI_USE_NAMESPACE; + // 登录锁屏样式无大变化 + if (appType() == ModelAppType::LoginLock) { + setColorByTheme(DGuiApplicationHelper::UnknownType); + } else { + // 对话框上存在明暗样式 + auto themeType = DGuiApplicationHelper::instance()->themeType(); + setColorByTheme(themeType); + + connect(DGuiApplicationHelper::instance(), &DGuiApplicationHelper::themeTypeChanged, this, [&] (DGuiApplicationHelper::ColorType t) { + setColorByTheme(t); + Q_EMIT widgetColorChanged(); + }); + } +} + +void WayPointModel::setColorByTheme(int type) +{ + DGUI_USE_NAMESPACE; + switch (type) { + case DGuiApplicationHelper::LightType: + m_colorConfig.line.setRgb(0, 129, 255); + m_colorConfig.unSelecteBoarder.setRgb(0, 0, 0, 0.3 * 255); + m_colorConfig.selectedBoarder.setRgb(0, 129, 255); + m_colorConfig.fill.setRgb(0, 129, 255, 0.1 * 255); + m_colorConfig.internalFill.setRgb(0, 129, 255); + m_colorConfig.warningFill.setRgb(255, 87, 54, 0.1 * 255); + m_colorConfig.warningLine.setRgb(255, 87, 54); + break; + case DGuiApplicationHelper::DarkType: + m_colorConfig.line.setRgb(0, 129, 255); + m_colorConfig.unSelecteBoarder.setRgb(255, 255, 255, 0.3 * 255); + m_colorConfig.selectedBoarder.setRgb(0, 129, 255); + m_colorConfig.fill.setRgb(0, 129, 255, 0.1 * 255); + m_colorConfig.internalFill.setRgb(0, 129, 255); + m_colorConfig.warningFill.setRgb(255, 87, 54, 0.1 * 255); + m_colorConfig.warningLine.setRgb(255, 87, 54); + break; + default: + m_colorConfig.line.setRgb(255, 255, 255); + m_colorConfig.unSelecteBoarder.setRgb(255, 255, 255, 0.74 * 255); + m_colorConfig.selectedBoarder.setRgb(255, 255, 255, 0.74 * 255); + m_colorConfig.fill.setRgb(255, 255, 255, 0.2 * 255); + m_colorConfig.internalFill.setRgb(255, 255, 255); + m_colorConfig.warningFill = m_colorConfig.fill; + m_colorConfig.warningLine = m_colorConfig.line; + break; + } +} + +QString WayPointModel::userName() const +{ + return m_userName; +} + +void WayPointModel::setUserName(const QString &name) +{ + m_userName = name; +} + +bool WayPointModel::isValidPath() const +{ + auto size = m_selectedPoints.size(); + return size >= 4; +} + +QString WayPointModel::getToken() const +{ + qDebug() << int(m_currentMode); + QString rst = ""; + foreach (auto index, m_selectedPoints) { + rst.append(kContent.value(index, "")); + } + + if (rst.isEmpty()) { + qDebug() << "skip crypt for empty input"; + return ""; + } + + if (appType() == ModelAppType::Reset && m_currentMode == Mode::Auth) + return rst; + + // 仅录入时加密,登录、解锁时由前端处理明文 + // 录入时两次需要使用相同盐值加密 + if (m_currentMode == Mode::Enroll || appType() == ModelAppType::Reset) { + static QString salt = gestureEncrypt::getSalt(); + rst = gestureEncrypt::cryptUserPassword(rst, salt.toLatin1()); + } + + return rst; +} + +Mode WayPointModel::currentMode() const +{ + return m_currentMode; +} + +void WayPointModel::setCurrentMode(Mode mode) +{ + m_currentMode = mode; + Q_EMIT modeChanged(m_currentMode); + + qDebug() << "model mode changed to:" << int(m_currentMode); +} + +void WayPointModel::setTitle(const QString &title) +{ + m_title = title; + Q_EMIT titleChanged(m_title); +} + +void WayPointModel::setTip(const QString &tip) +{ + m_tip = tip; + Q_EMIT tipChanged(m_tip); +} + +void WayPointModel::setGestureState(int flag) +{ + qDebug() << flag << m_gestureStateFlag; + if (m_gestureStateFlag != flag) { + m_gestureStateFlag = flag; + Q_EMIT gestureStateChanged(m_gestureStateFlag); + } +} + +void WayPointModel::setGestureEnabled(bool enable) +{ + m_gestureEnable = enable; +} + +int WayPointModel::getGestureState() +{ + return m_gestureStateFlag; +} + +void WayPointModel::onPathArrived(int index) +{ + if (index < 0) { + return; + } + + do { + const int current = index; + if (m_selectedPoints.contains(current)) { + // 如果索引与倒数第二个相同,代表撤消上一个选中 + if (m_selectedPoints.size() >= 2) { + if (m_selectedPoints.indexOf(current) == m_selectedPoints.size() - 2) { + // 先发信号再移除,注意顺序 + Q_EMIT selected(m_selectedPoints.last(), false); + m_selectedPoints.removeLast(); + + Q_EMIT dataChanged(); + } + } + break; + } + + // 不包含,判断是否有途经的点并优先插入 + if (!m_selectedPoints.size()) { + appendPath(current); + break; + } + + if (m_selectedPoints.size()) { + // 选中所有沿途点 + int currentX = current % 3; + int currentY = current / 3; + + auto last = m_selectedPoints.last(); + int lastX = last % 3; + int lastY = last / 3; + + auto step = (current - last) / qAbs(current - last); + + auto insertPassby = [&](int dx, int dy) { + auto passByX = lastX + dx; + auto passByY = lastY + dy; + + while (passByY != currentY || passByX != currentX) { + int passbyIndex = positionToIndex(passByX, passByY); + if (passbyIndex >= 0) { + appendPath(passbyIndex); + } + passByX += dx; + passByY += dy; + } + }; + + if (currentX == lastX) { + // 同列 + insertPassby(0, step); + } else if (currentY == lastY) { + // 同行 + insertPassby(step, 0); + } else if (qAbs(currentX - lastX) == qAbs(currentY - lastY)) { + // 同斜率 + auto stepX = (currentX - lastX) / qAbs(currentX - lastX); + auto stepY = (currentY - lastY) / qAbs(currentY - lastY); + insertPassby(stepX, stepY); + } + + // 插入路过的点后,判断当前点是否在期望的路径上 + last = m_selectedPoints.last(); + lastX = last % 3; + lastY = last / 3; + if (currentX == lastX || currentY == lastY || qAbs(currentX - lastX) == qAbs(currentY - lastY)) { + appendPath(current); + } + } + + } while (false); +} + +void WayPointModel::onUserInputDone() +{ + if (m_selectedPoints.size() < 4 || m_selectedPoints.size() > 9) { + m_showErrorStyle = true; + clearPath(); + Q_EMIT pathError(); + } else { + Q_EMIT pathDone(); + } +} + +void WayPointModel::clearPath() +{ + m_selectedPoints.clear(); + Q_EMIT selected(-1, false); // 清空所有结点 + Q_EMIT dataChanged(); + + m_showErrorStyle = false; +} + +void WayPointModel::setLocked(bool locked) +{ + if (m_locked != locked) { + m_locked = locked; + Q_EMIT inputLocked(m_locked); + } +} + +int WayPointModel::positionToIndex(int x, int y) +{ + if (x < 0 || y < 0) { + return -1; + } + + int index = static_cast(m_size) - 1; + while (index >= 0) { + int ix = index % 3; + int iy = index / 3; + if (ix == x && iy == y) { + return index; + } + + index--; + } + return -1; +} + +void WayPointModel::appendPath(int index) +{ + if (!m_selectedPoints.contains(index)) { + m_selectedPoints.push_back(index); + Q_EMIT selected(index, true); + Q_EMIT dataChanged(); + } +} diff --git a/plugins/login-gesture/src/waypointmodel.h b/plugins/login-gesture/src/waypointmodel.h new file mode 100644 index 00000000..9ab18c19 --- /dev/null +++ b/plugins/login-gesture/src/waypointmodel.h @@ -0,0 +1,146 @@ +// SPDX-FileCopyrightText: 2021 - 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef WAYPOINTMODEL_H +#define WAYPOINTMODEL_H + +#include +#include +#include + +namespace gestureLogin { + +enum GestureState { + NotSet, + SetAndRemoved, + Set +}; + +enum class Mode { + Enroll, + Auth +}; + +enum class ModelAppType { + LoginLock, + Reset, + Unknow +}; + +struct GestureColors { + QColor line; + QColor unSelecteBoarder; + QColor selectedBoarder; + QColor fill; + QColor internalFill; + QColor warningFill; + QColor warningLine; +}; + +class WayPointModel : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QString title MEMBER m_title WRITE setTitle NOTIFY titleChanged) + Q_PROPERTY(QString tip MEMBER m_tip WRITE setTip NOTIFY tipChanged) + +public: + static WayPointModel *instance(); + const QList ¤tPoints(); + + ModelAppType appType() const; + bool isLockApp(); + + // 响应userSwitch,重置校验过程与状态 + // 通过用户名查询user的dbuspath + QString userName() const; + void setUserName(const QString &); + + inline uint size() const { return m_size; } + + bool isValidPath() const; + QString getToken() const; + + Mode currentMode() const; + void setCurrentMode(Mode); + + void setTitle(const QString &); + void setTip(const QString &); + + inline QString title() const { return m_title; } + inline QString tip() const { return m_tip; } + inline bool locked() const { return m_locked; } + + void setGestureState(int); + void setGestureEnabled(bool); + + int getGestureState(); + bool isGestureEnabled(); + + inline bool showErrorStyle() { return m_showErrorStyle; } + const GestureColors& colorConfig(); + + QString errorTextStyle() const; + + void setLocaleName(const QString &); + inline QString localeName() const { return m_localeName; } + +public Q_SLOTS: + void onPathArrived(int); + void onUserInputDone(); + void clearPath(); + void setLocked(bool); + +Q_SIGNALS: + void inputLocked(bool); + + // 文案控制 + void titleChanged(const QString &); + void tipChanged(const QString &); + void widgetColorChanged(); + + void modeChanged(Mode); + // 录入 + void finished(); + + void pathError(); // 不满足条件 + void pathDone(); // 满足条件 + + // 校验的信号转发 + void authError(); // 验证失败 + void authDone(); // 验证成功 + + void dataChanged(); // 通知UI执行更新 + void selected(int, bool); // 单个点被选中或取消 + + void gestureStateChanged(int); + void localeChanged(const QString &); + +private: + explicit WayPointModel(QObject *parent = nullptr); + int positionToIndex(int, int); + void appendPath(int); + void removePath(int); + void initColorConfig(); + void setColorByTheme(int); + +private: + QList m_selectedPoints; + uint m_size; + Mode m_currentMode; + + QString m_userName; + QString m_title; + QString m_tip; + // 后端手势标志: 0 从未录入 1 已被重置 2 已录入 + int m_gestureStateFlag; + bool m_gestureEnable; + bool m_showErrorStyle; + GestureColors m_colorConfig; + bool m_locked; + QString m_localeName; +}; +} // namespace gestureLogin + +#endif // WAYPOINTMODEL_H diff --git a/plugins/login-gesture/src/waypointwidget.cpp b/plugins/login-gesture/src/waypointwidget.cpp new file mode 100644 index 00000000..7867b0d3 --- /dev/null +++ b/plugins/login-gesture/src/waypointwidget.cpp @@ -0,0 +1,93 @@ +// SPDX-FileCopyrightText: 2021 - 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "waypointwidget.h" +#include "waypointmodel.h" + +#include +#include +#include + +using namespace gestureLogin; +WayPointWidget::WayPointWidget(QWidget *parent) + : QWidget(parent) + , m_isSelected(false) +{ + setContentsMargins(0, 0, 0, 0); + + setWindowFlag(Qt::FramelessWindowHint); + setAttribute(Qt::WA_TranslucentBackground); + + connect(WayPointModel::instance(), &WayPointModel::widgetColorChanged, this, [this] { + update(); + }); +} + +void WayPointWidget::setId(int id) +{ + m_id = id; +} + +void WayPointWidget::onSelectedById(int id, bool selected) +{ + if (id == m_id || id == -1) { + m_isSelected = selected; + update(); + } +} + +void WayPointWidget::onPathStarted(const QPoint &startPoint) +{ + if (geometry().contains(startPoint)) { + Q_EMIT arrived(this->m_id); + } + + m_started = true; +} + +void WayPointWidget::onPathStopped() +{ + m_started = false; +} + +void WayPointWidget::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event) + + QPainterPath pathBound; + QPainterPath pathCenter; + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing); + + static auto model = WayPointModel::instance(); + auto colorConfig = model->colorConfig(); + + QColor unselectedBoarderColor = colorConfig.unSelecteBoarder; + QColor selectedBoarderColor = colorConfig.selectedBoarder; + QColor fillColor = colorConfig.fill; + QColor internalFillColor = colorConfig.internalFill; + + if (model->showErrorStyle()) { + selectedBoarderColor = colorConfig.warningLine; + fillColor = colorConfig.warningFill; + internalFillColor = selectedBoarderColor; + } + + pathBound.addEllipse(this->contentsRect().center(), 23, 23); + QPen pen; + pen.setColor(m_isSelected ? selectedBoarderColor : unselectedBoarderColor); + pen.setWidth(2); + painter.setPen(pen); + + if (m_isSelected) { + painter.setBrush(fillColor); + } + painter.drawPath(pathBound); + + if (m_isSelected) { + pathCenter.addEllipse(this->contentsRect().center(), 12, 12); + painter.setBrush(internalFillColor); + painter.drawPath(pathCenter); + } +} diff --git a/plugins/login-gesture/src/waypointwidget.h b/plugins/login-gesture/src/waypointwidget.h new file mode 100644 index 00000000..88d436f4 --- /dev/null +++ b/plugins/login-gesture/src/waypointwidget.h @@ -0,0 +1,41 @@ +// SPDX-FileCopyrightText: 2021 - 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef WAYPOINTWIDGET_H +#define WAYPOINTWIDGET_H + +#include + +namespace gestureLogin { + +class WayPointWidget : public QWidget +{ + Q_OBJECT +public: + explicit WayPointWidget(QWidget *parent = nullptr); + static QList getCrossedNode(int startId, int stopId); + + void setId(int); + inline int id() { return m_id; } + +public Q_SLOTS: + void onSelectedById(int m_id, bool arrived); // 选中、撤消图形变化 + void onPathStarted(const QPoint &); + void onPathStopped(); + +Q_SIGNALS: + void arrived(int m_id); // 通知模型处理,如果可选中,再调用onSelectedById + +protected: + virtual void paintEvent(QPaintEvent *event) override; + +private: + QString m_iconPath; + int m_id; + bool m_isSelected; + bool m_started; +}; +} // namespace gestureLogin + +#endif // WAYPOINTWIDGET_H diff --git a/plugins/login-gesture/translate_generation.sh b/plugins/login-gesture/translate_generation.sh new file mode 100755 index 00000000..c9080923 --- /dev/null +++ b/plugins/login-gesture/translate_generation.sh @@ -0,0 +1,11 @@ +#!/bin/bash +# this file is used to auto-generate .qm file from .ts file. +# author: shibowen at linuxdeepin.com + +ts_list=(`ls translations/*.ts`) + +for ts in "${ts_list[@]}" +do + printf "\nprocess ${ts}\n" + lrelease "${ts}" +done diff --git a/plugins/login-gesture/translations/login-gesture_en.ts b/plugins/login-gesture/translations/login-gesture_en.ts new file mode 100644 index 00000000..129d47b6 --- /dev/null +++ b/plugins/login-gesture/translations/login-gesture_en.ts @@ -0,0 +1,99 @@ + + + + + gestureLogin::TranslastionDoc + + Gesture password is enabled + + + + For first time use, please set the gesture password first + + + + Set gesture password + + + + Please draw a gesture password + + + + Please draw the gesture password again + + + + Setup completed Start login + + + + Forgot gesture password + + + + Sign in with gesture password + + + + Gesture password has been reset + + + + Please reset the gesture password + + + + Inconsistent with the last drawing, please redraw + + + + Device is locked, unlocked after %1 minutes + + + + Modify gesture password + + + + Please draw the current gesture password + + + + Modified successfully + + + + Drawing error, Contact the administrator to reset + + + + Cancel + + + + Ok + + + + Drawing error, %1 chances left. Contact the administrator to reset + + + + Setup completed Start unlock + + + + Unlock with gesture password + + + + Minimum 4 points, please redraw + + + + Contact the administrator to reset + + + + diff --git a/plugins/login-gesture/translations/login-gesture_zh_CN.ts b/plugins/login-gesture/translations/login-gesture_zh_CN.ts new file mode 100644 index 00000000..f3e07661 --- /dev/null +++ b/plugins/login-gesture/translations/login-gesture_zh_CN.ts @@ -0,0 +1,99 @@ + + + + + gestureLogin::TranslastionDoc + + Gesture password is enabled + 手势密码已开启 + + + For first time use, please set the gesture password first + 首次使用,请先设置手势密码 + + + Set gesture password + 设置手势密码 + + + Please draw a gesture password + 请绘制手势密码 + + + Please draw the gesture password again + 请再次绘制手势密码 + + + Setup completed Start login + 设置完成 开始登录 + + + Forgot gesture password + 忘记手势密码 + + + Sign in with gesture password + 使用手势密码登录 + + + Gesture password has been reset + 手势密码已重置 + + + Please reset the gesture password + 请重新设置手势密码 + + + Inconsistent with the last drawing, please redraw + 与上次绘制不一致,请重新绘制 + + + Drawing error, Contact the administrator to reset + 绘制错误,可联系管理员重置 + + + Device is locked, unlocked after %1 minutes + 设备已锁定,%1分钟后解锁 + + + Modify gesture password + 修改手势密码 + + + Please draw the current gesture password + 请绘制当前手势密码 + + + Modified successfully + 修改成功 + + + Cancel + 取消 + + + Ok + 确定 + + + Drawing error, %1 chances left. Contact the administrator to reset + 绘制错误,剩余%1次机会。可联系管理员重置 + + + Setup completed Start unlock + 设置完成开始解锁 + + + Unlock with gesture password + 使用手势密码解锁 + + + Minimum 4 points, please redraw + 最少连接4个点,请重新绘制 + + + Contact the administrator to reset + 可联系管理员重置手势密码 + + + diff --git a/plugins/login-gesture/translations/login-gesture_zh_HK.ts b/plugins/login-gesture/translations/login-gesture_zh_HK.ts new file mode 100644 index 00000000..6c9ac01d --- /dev/null +++ b/plugins/login-gesture/translations/login-gesture_zh_HK.ts @@ -0,0 +1,99 @@ + + + + + gestureLogin::TranslastionDoc + + Gesture password is enabled + 手勢密碼已開啟 + + + For first time use, please set the gesture password first + 首次使用,請先設定手勢密碼 + + + Set gesture password + 設定手勢密碼 + + + Please draw a gesture password + 請繪製手勢密碼 + + + Please draw the gesture password again + 請再次繪製手勢密碼 + + + Setup completed Start login + 設定完成開始登入 + + + Forgot gesture password + 忘記手勢密碼 + + + Sign in with gesture password + 使用手勢密碼登入 + + + Gesture password has been reset + 手勢密碼已重置 + + + Please reset the gesture password + 請重新設定手勢密碼 + + + Inconsistent with the last drawing, please redraw + 與上次繪製不一致,請重新繪製 + + + Drawing error, Contact the administrator to reset + 繪製錯誤,可聯系管理員重置 + + + Device is locked, unlocked after %1 minutes + 設備已鎖定,%1分鐘後解鎖 + + + Modify gesture password + 修改手勢密碼 + + + Please draw the current gesture password + 請繪製當前手勢密碼 + + + Modified successfully + 修改成功 + + + Cancel + 取消 + + + Ok + 確定 + + + Drawing error, %1 chances left. Contact the administrator to reset + 繪製錯誤,剩餘%1次機會。可聯絡管理員重置 + + + Setup completed Start unlock + 設定完成開始解鎖 + + + Unlock with gesture password + 使用手勢密碼解鎖 + + + Minimum 4 points, please redraw + 最少連接4個點,請重新繪製 + + + Contact the administrator to reset + 可聯絡管理員重置手勢密碼 + + + diff --git a/plugins/login-gesture/translations/login-gesture_zh_TW.ts b/plugins/login-gesture/translations/login-gesture_zh_TW.ts new file mode 100644 index 00000000..440bc4ef --- /dev/null +++ b/plugins/login-gesture/translations/login-gesture_zh_TW.ts @@ -0,0 +1,99 @@ + + + + + gestureLogin::TranslastionDoc + + Gesture password is enabled + 手勢密碼已開啟 + + + For first time use, please set the gesture password first + 首次使用,請先設定手勢密碼 + + + Set gesture password + 設定手勢密碼 + + + Please draw a gesture password + 請繪製手勢密碼 + + + Please draw the gesture password again + 請再次繪製手勢密碼 + + + Setup completed Start login + 設定完成開始登入 + + + Forgot gesture password + 忘記手勢密碼 + + + Sign in with gesture password + 使用手勢密碼登入 + + + Gesture password has been reset + 手勢密碼已重置 + + + Please reset the gesture password + 請重新設定手勢密碼 + + + Inconsistent with the last drawing, please redraw + 與上次繪製不一致,請重新繪製 + + + Drawing error, Contact the administrator to reset + 繪製錯誤,可聯系管理員重置 + + + Device is locked, unlocked after %1 minutes + 設備已鎖定,%1分鐘後解鎖 + + + Modify gesture password + 修改手勢密碼 + + + Please draw the current gesture password + 請繪製當前手勢密碼 + + + Modified successfully + 修改成功 + + + Cancel + 取消 + + + Ok + 確定 + + + Drawing error, %1 chances left. Contact the administrator to reset + 繪製錯誤,剩餘%1次機會。可聯絡管理員重設 + + + Setup completed Start unlock + 設定完成開始解鎖 + + + Unlock with gesture password + 使用手勢密碼解鎖 + + + Minimum 4 points, please redraw + 最少連接4個點,請重新繪製 + + + Contact the administrator to reset + 可聯絡管理員重設手勢密碼 + + + diff --git a/plugins/login-gesture/utils/tokenCrypt.h b/plugins/login-gesture/utils/tokenCrypt.h new file mode 100644 index 00000000..7c8f2ad1 --- /dev/null +++ b/plugins/login-gesture/utils/tokenCrypt.h @@ -0,0 +1,85 @@ +// SPDX-FileCopyrightText: 2011 - 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef TOKENCRYPT_H +#define TOKENCRYPT_H + +#include +#include + +#include +#include + +namespace gestureEncrypt { +/** + * @brief crypt函数是否支持SM3算法 + * crypt使用固定的key和salt进行加密,把加密结果与已知的正确结果进行比对,相等则支持,反之不支持 + * @return true 支持 + * @return false 不支持 + */ +inline bool supportSM3() +{ + char password[] = "Hello world!"; + char salt[] = "$8$saltstring"; + const QString cryptResult = "$8$saltstring$6RCuSuWbADZmLALkvsvtcYYzhrw3xxpuDcqwdPIWxTD"; + return crypt(password, salt) == cryptResult; +} + +inline const QByteArray getCryptSalt(const QByteArray &key) +{ + QByteArray retsult = ""; + int count = 0; + for (const auto value : key) { + if (value == '$') { + retsult += value; + count++; + if (count == 3) { + return retsult; + } + } else { + if (count > 0) { + retsult += value; + } + } + } + + return retsult; +} + +inline QString cryptUserPassword(const QString &password, const QByteArray &salt) +{ + return crypt(password.toUtf8().data(), salt); +} + +inline QString getSalt() +{ + /* + NOTE(kirigaya): Password is a combination of salt and crypt function. + slat is begin with $6$, 16 byte of random values, at the end of $. + crypt function will return encrypted values. + */ + const QString seedChars("./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); + char salt[] = "$6$................$"; + if (supportSM3()) { + salt[1] = '8'; + } + + std::random_device r; + std::default_random_engine e1(r()); + std::uniform_int_distribution uniform_dist(0, seedChars.size() - 1); // seedChars.size()是64,生成随机数的范围应该写成[0, 63]。 + + // Random access to a character in a restricted list + for (int i = 0; i != 16; i++) { + salt[3 + i] = seedChars.at(uniform_dist(e1)).toLatin1(); + } + + return salt; +} + +inline QString cryptUserPassword(const QString &password) +{ + return crypt(password.toUtf8().data(), getSalt().toLatin1()); +} +}; // namespace gestureEncrypt +#endif diff --git a/plugins/login-gesture/utils/translastiondoc.cpp b/plugins/login-gesture/utils/translastiondoc.cpp new file mode 100644 index 00000000..06c473d8 --- /dev/null +++ b/plugins/login-gesture/utils/translastiondoc.cpp @@ -0,0 +1,86 @@ +// SPDX-FileCopyrightText: 2021 - 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "translastiondoc.h" +#include "waypointmodel.h" + +#include +#include +#include +#include +#include + +const QString kTransFile = "%1/login-gesture_%2.qm"; + +using namespace gestureLogin; + +TranslastionDoc::TranslastionDoc(QObject *parent) + : QObject(parent) + , m_translator(new QTranslator(this)) +{ + setLocale(WayPointModel::instance()->localeName()); + connect(WayPointModel::instance(), &WayPointModel::localeChanged, this, &TranslastionDoc::setLocale); +} + +TranslastionDoc *TranslastionDoc::instance() +{ + static TranslastionDoc doc; + return &doc; +} + +QString TranslastionDoc::getDoc(DocIndex index) +{ + return m_doc.value(index, ""); +} + +void TranslastionDoc::setLocale(const QString &localName) +{ + auto name = localName; + if (name.isEmpty()) { + name = QLocale::system().name(); + } + + qApp->removeTranslator(m_translator); + QString transFile = kTransFile.arg(QM_FILES_DIR).arg(name); + if (QFile(transFile).exists()) { + m_translator->load(transFile); + qApp->installTranslator(m_translator); + initDoc(); + } else { + qWarning() << transFile << "not exists"; + } +} + +void TranslastionDoc::initDoc() +{ + m_doc.clear(); + m_doc[DocIndex::Enabled] = tr("Gesture password is enabled"); + m_doc[DocIndex::RequestFirstEnroll] = tr("For first time use, please set the gesture password first"); + m_doc[DocIndex::SetPasswd] = tr("Set gesture password"); + m_doc[DocIndex::RequestDrawing] = tr("Please draw a gesture password"); + m_doc[DocIndex::RequestDrawingAgain] = tr("Please draw the gesture password again"); + m_doc[DocIndex::ForgotPasswd] = tr("Forgot gesture password"); + m_doc[DocIndex::PasswdCleared] = tr("Gesture password has been reset"); + m_doc[DocIndex::NeedResetPasswd] = tr("Please reset the gesture password"); + m_doc[DocIndex::PathError] = tr("Minimum 4 points, please redraw"); + m_doc[DocIndex::Inconsistent] = tr("Inconsistent with the last drawing, please redraw"); + m_doc[DocIndex::ContactAdmin] = tr("Drawing error, Contact the administrator to reset"); + m_doc[DocIndex::DeviceLock] = tr("Device is locked, unlocked after %1 minutes"); + m_doc[DocIndex::ModifyPasswd] = tr("Modify gesture password"); + m_doc[DocIndex::DrawCurrentPasswd] = tr("Please draw the current gesture password"); + m_doc[DocIndex::EnrollDone] = tr("Modified successfully"); + m_doc[DocIndex::Cancel] = tr("Cancel"); + m_doc[DocIndex::Ok] = tr("Ok"); + m_doc[DocIndex::AuthErrorWithTimes] = tr("Drawing error, %1 chances left. Contact the administrator to reset"); + m_doc[DocIndex::DeviceLockHelp] = tr("Contact the administrator to reset"); + + // 登录器与锁屏的差异部分 + if (WayPointModel::instance()->isLockApp()) { + m_doc[DocIndex::LoginAfterEnroll] = tr("Setup completed Start unlock"); + m_doc[DocIndex::LoginStart] = tr("Unlock with gesture password"); + } else { + m_doc[DocIndex::LoginAfterEnroll] = tr("Setup completed Start login"); + m_doc[DocIndex::LoginStart] = tr("Sign in with gesture password"); + } +} diff --git a/plugins/login-gesture/utils/translastiondoc.h b/plugins/login-gesture/utils/translastiondoc.h new file mode 100644 index 00000000..85d76a5d --- /dev/null +++ b/plugins/login-gesture/utils/translastiondoc.h @@ -0,0 +1,62 @@ +// SPDX-FileCopyrightText: 2021 - 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef TRANSLASTIONDOC_H +#define TRANSLASTIONDOC_H + +#include +#include + +class QTranslator; + +namespace gestureLogin { + +enum class DocIndex { + Enabled, + RequestFirstEnroll, + SetPasswd, + RequestDrawing, + RequestDrawingAgain, + LoginAfterEnroll, + ForgotPasswd, + LoginStart, + GestureLockStart, + PasswdCleared, + NeedResetPasswd, + PathError, + Inconsistent, + GestureAuthError, + ContactAdmin, + DeviceLock, + ModifyPasswd, + DrawCurrentPasswd, + EnrollDone, + Cancel, + Ok, + AuthErrorWithTimes, + DeviceLockHelp +}; + +// 在这里加载翻译文件,提供翻译内容 +class TranslastionDoc : public QObject +{ + Q_OBJECT + +public: + static TranslastionDoc *instance(); + QString getDoc(DocIndex); + +public Q_SLOTS: + void setLocale(const QString &); + +private: + explicit TranslastionDoc(QObject *parent = nullptr); + void initDoc(); + +private: + QMap m_doc; + QTranslator *m_translator; +}; +} // namespace gestureLogin +#endif // TRANSLASTIONDOC_H diff --git a/plugins/login-gesture/utils/userservice.cpp b/plugins/login-gesture/utils/userservice.cpp new file mode 100644 index 00000000..8bb3ac50 --- /dev/null +++ b/plugins/login-gesture/utils/userservice.cpp @@ -0,0 +1,137 @@ +// SPDX-FileCopyrightText: 2021 - 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "userservice.h" +#include "waypointmodel.h" + +#include +#include + +const QString kAccountService = "com.deepin.daemon.Accounts"; +const QString kAccountPath = "/com/deepin/daemon/Accounts"; +const QString kUserInterface = "com.deepin.daemon.Accounts.User"; +const QString kPropertyInterface = "org.freedesktop.DBus.Properties"; +const QString kEnableFlag = "PatternEnabled"; +const QString kStateFlag = "PatternState"; + +using namespace gestureLogin; + +UserService::UserService(const QString &name, QObject *parent) + : QObject(parent) + , m_userInter(nullptr) + , m_currentName(name) +{ + init(); +} + +UserService *UserService::instance() +{ + static UserService s(""); + return &s; +} + +void UserService::setUserName(const QString &userName) +{ + // 不论设置用户名多少,都重新做初始化 + m_currentName = userName; + init(); +} + +bool UserService::gestureEnabled() +{ + auto rst = false; + if (m_userInter && m_userInter->isValid()) { + rst = m_userInter->property("PatternEnabled").toBool(); + } + + return rst; +} + +int UserService::gestureFlags() +{ + auto rst = -1; + if (m_userInter && m_userInter->isValid()) { + rst = m_userInter->property("PatternState").toInt(); + } + + return rst; +} + +QString UserService::localeName() +{ + QString rst = ""; + if (m_userInter && m_userInter->isValid()) { + rst = m_userInter->property("Locale").toString(); + } + + return rst; +} + +void UserService::enroll(const QString &data) +{ + if (data.isEmpty()) { + qDebug() << Q_FUNC_INFO << "enroll data empty"; + return; + } + + if (m_userInter && m_userInter->isValid()) { + m_userInter->call("SetPattern", data); + qDebug() << Q_FUNC_INFO << "enrolled" << m_userInter->path(); + } +} + +void UserService::onPropertiesChanged(const QDBusMessage& msg) +{ + QList arguments = msg.arguments(); + if (3 != arguments.count()) + return; + + QString interfaceName = msg.arguments().at(0).toString(); + + if (interfaceName != kUserInterface) + return; + + QVariantMap changedProps = qdbus_cast(arguments.at(1).value()); + QStringList keys = changedProps.keys(); + if (changedProps.keys().contains(kStateFlag)) { + WayPointModel::instance()->setGestureState(changedProps.value(kStateFlag).toInt()); + } +} + +void UserService::init() +{ + if (m_userInter) { + m_userInter->disconnect(); + delete m_userInter; + m_userInter = nullptr; + } + + if (!m_userInterpath.isEmpty()) { + QDBusConnection::systemBus().disconnect(kAccountService, m_userInterpath, kPropertyInterface, "PropertiesChanged", "sa{sv}as", this, SLOT(onPropertiesChanged(QDBusMessage))); + } + + static QDBusInterface accountInter(kAccountService, kAccountPath, kAccountService, QDBusConnection::systemBus(), this); + QDBusReply reply = accountInter.call("FindUserByName", m_currentName); + if (reply.isValid()) { + m_userInterpath = reply; + } + + if (m_userInterpath.isEmpty()) { + return; + } + + m_userInter = new QDBusInterface(kAccountService, m_userInterpath, kUserInterface, QDBusConnection::systemBus(), this); + if (m_userInter->isValid()) { + bool connected = QDBusConnection::systemBus().connect(kAccountService, m_userInterpath, kPropertyInterface, "PropertiesChanged", "sa{sv}as", this, SLOT(onPropertiesChanged(QDBusMessage))); + qDebug() << Q_FUNC_INFO << "property change handle" << connected; + + auto model = WayPointModel::instance(); + model->setGestureEnabled(gestureEnabled()); + model->setGestureState(gestureFlags()); + model->setLocaleName(localeName()); + } else { + qWarning() << Q_FUNC_INFO << "user interface invalid" << m_userInterpath; + } + +} diff --git a/plugins/login-gesture/utils/userservice.h b/plugins/login-gesture/utils/userservice.h new file mode 100644 index 00000000..2e25f30f --- /dev/null +++ b/plugins/login-gesture/utils/userservice.h @@ -0,0 +1,47 @@ +// SPDX-FileCopyrightText: 2021 - 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef DBUSSERVICE_H +#define DBUSSERVICE_H + +#include +#include + +namespace gestureLogin { + +// 提供da状态查询与监控 +class UserService : public QObject +{ + Q_OBJECT +public: + static UserService *instance(); + + void setUserName(const QString &); + inline bool isServiceValid() { return m_userInter != nullptr; } + // 查询当前用户手势是否开启 + bool gestureEnabled(); + // 查询当前用户手势状态标识 + int gestureFlags(); + QString localeName(); + + void enroll(const QString &); + +public Q_SLOTS: + void onPropertiesChanged(const QDBusMessage& msg); + +Q_SIGNALS: + void userPatternStateChanged(int); + +private: + explicit UserService(const QString &, QObject *parent = nullptr); + void init(); + +private: + QDBusInterface *m_userInter; + QString m_currentName; + QString m_userInterpath; +}; +} // namespace gestureLogin + +#endif // DBUSSERVICE_H diff --git a/plugins/one-key-login/login_module.cpp b/plugins/one-key-login/login_module.cpp index 15b3c553..daf5a078 100644 --- a/plugins/one-key-login/login_module.cpp +++ b/plugins/one-key-login/login_module.cpp @@ -118,7 +118,7 @@ LoginModule::LoginModule(QObject *parent) if (!m_isAcceptFingerprintSignal) { // 防止刚切换指纹认证stop还没结束。 QTimer::singleShot(30, this, [this] { - sendAuthTypeToSession(AuthType::AT_Fingerprint); + sendAuthTypeToSession(AuthType::AT_All); }); } }, Qt::DirectConnection); @@ -167,7 +167,7 @@ void LoginModule::startCallHuaweiFingerprint() auto failedHandler = [this] { m_isAcceptFingerprintSignal = false; // FIXME 此处不能调用回调,因为还没初始化,此处的逻辑应该在setCallBack函数完成后再进行。 - sendAuthTypeToSession(AuthType::AT_Fingerprint); + sendAuthTypeToSession(AuthType::AT_All); }; QDBusMessage m = QDBusMessage::createMethodCall(DSS_DBUS::authenticateService, DSS_DBUS::fingerprintAuthPath, DSS_DBUS::fingerprintAuthInterface, @@ -200,7 +200,7 @@ void LoginModule::init() if (m_appType == AppType::Lock && !m_acceptSleepSignal) { //此处延迟0.5s发送,是需要等待dde-session-shell认证方式创建完成 QTimer::singleShot(500, this, [this] { - sendAuthTypeToSession(AuthType::AT_Fingerprint); + sendAuthTypeToSession(AuthType::AT_All); }); } } @@ -331,7 +331,7 @@ QString LoginModule::message(const QString &message) sendAuthData(m_lastAuthResult); } if (m_needSendAuthType) { - sendAuthTypeToSession(AuthType::AT_Fingerprint); + sendAuthTypeToSession(AuthType::AT_All); } } } else if (cmdType == "AuthState") { @@ -399,7 +399,7 @@ void LoginModule::slotIdentifyStatus(const QString &name, const int errorCode, c if (m_appType == AppType::Lock && m_userName != name && name != "") { // 防止stop还未完成就开启了指纹认证 QTimer::singleShot(30, this, [this] { - sendAuthTypeToSession(AuthType::AT_Fingerprint); + sendAuthTypeToSession(AuthType::AT_All); }); return ; } @@ -413,7 +413,7 @@ void LoginModule::slotIdentifyStatus(const QString &name, const int errorCode, c // 发送一键登录失败的信息 qWarning() << "Identify Status recive failed, error: " << msg; QTimer::singleShot(30, this, [this] { - sendAuthTypeToSession(AuthType::AT_Fingerprint); + sendAuthTypeToSession(AuthType::AT_All); }); m_lastAuthResult.result = AuthResult::Failure; @@ -499,7 +499,7 @@ void LoginModule::slotPrepareForSleep(bool active) m_spinner->start(); } else { //fix: 多用户时,第一个用户直接锁屏,然后待机唤醒,在直接切换到另一个用户时,m_login1SessionSelf没有激活,见159949 - sendAuthTypeToSession(AuthType::AT_Fingerprint); + sendAuthTypeToSession(AuthType::AT_All); } } @@ -525,12 +525,6 @@ void LoginModule::sendAuthTypeToSession(AuthType type) return; } - // 登录界面密码锁定后只允许切换到密码认证,等待密码锁定解除后才能切换到其它认证类型 - if (m_isLocked && m_appType == AppType::Login) { - qInfo() << "Password is locked and current application is greeter, change authentication type to password"; - type = AuthType::AT_Password; - } - // 这里主要为了防止 在发送切换信号的时候,lightdm还为开启认证,导致切换类型失败 if (m_authStatus == AuthStatus::None && !m_isLocked && type != AuthType::AT_Custom && m_appType != AppType::Lock) { m_needSendAuthType = true; @@ -543,7 +537,7 @@ void LoginModule::sendAuthTypeToSession(AuthType type) QJsonObject message; message.insert("CmdType", "setAuthTypeInfo"); QJsonObject retDataObj; - retDataObj["AuthType"] = type; + retDataObj["AuthType"] = static_cast(type); message["Data"] = retDataObj; QJsonDocument doc; doc.setObject(message); diff --git a/src/dde-lock/lockframe.cpp b/src/dde-lock/lockframe.cpp index 079f5f1a..cf594bcb 100644 --- a/src/dde-lock/lockframe.cpp +++ b/src/dde-lock/lockframe.cpp @@ -95,7 +95,7 @@ LockFrame::LockFrame(SessionBaseModel *const model, QWidget *parent) if (DConfigHelper::instance()->getConfig("autoExit", false).toBool()) { m_autoExitTimer = new QTimer(this); - m_autoExitTimer->setInterval(1000*60); //1分钟 + m_autoExitTimer->setInterval(1000 * 60); //1分钟 m_autoExitTimer->setSingleShot(true); connect(m_autoExitTimer, &QTimer::timeout, qApp, &QApplication::quit); } @@ -115,7 +115,7 @@ LockFrame::~LockFrame() bool LockFrame::event(QEvent *event) { if (event->type() == QEvent::KeyRelease) { - QString keyValue = ""; + QString keyValue = ""; switch (static_cast(event)->key()) { case Qt::Key_PowerOff: { if (!handlePoweroffKey()) { @@ -165,8 +165,8 @@ bool LockFrame::event(QEvent *event) } // 锁屏时会独占键盘,需要单独处理F1待机事件 case Qt::Key_Sleep: { - m_model->setPowerAction(SessionBaseModel::RequireSuspend); - break; + m_model->setPowerAction(SessionBaseModel::RequireSuspend); + break; } } @@ -230,7 +230,9 @@ void LockFrame::keyPressEvent(QKeyEvent *e) { switch (e->key()) { #ifdef QT_DEBUG - case Qt::Key_Escape: qApp->quit(); break; + case Qt::Key_Escape: + qApp->quit(); + break; #endif } } @@ -255,7 +257,6 @@ void LockFrame::hideEvent(QHideEvent *event) return FullScreenBackground::hideEvent(event); } - void LockFrame::prepareForSleep(bool isSleep) { //不管是待机还是唤醒均不响应电源按键信号 diff --git a/src/dde-lock/lockworker.cpp b/src/dde-lock/lockworker.cpp index 75a59430..62d28358 100644 --- a/src/dde-lock/lockworker.cpp +++ b/src/dde-lock/lockworker.cpp @@ -14,6 +14,8 @@ #include "fullscreenbackground.h" #include "updateworker.h" #include "lockcontent.h" +#include "signal_bridge.h" +#include "mfasequencecontrol.h" #include @@ -160,8 +162,22 @@ void LockWorker::initConnections() destroyAuthentication(m_account); } }); + + // 在待机时启动定时器,如果定时器超时的时间离待机时间大于15秒,则认为已经唤醒,停止定时器,并设置黑屏模式为false + // prepareForSleep信号有时候会会延迟,所以需要定时器来确认是否已经唤醒,尽早显示出锁屏。 + static QDateTime doSuspendTime = QDateTime::currentDateTime(); + auto checkSystemWakeupTimer = new QTimer(this); + checkSystemWakeupTimer->setInterval(500); + connect(checkSystemWakeupTimer, &QTimer::timeout, this, [this, checkSystemWakeupTimer] { + const int secs = static_cast(doSuspendTime.secsTo(QDateTime::currentDateTime())); + if (secs >= 15) { // 小风险:如果15秒还没有待机下去,那就把锁屏显示出来了,不过15秒没有待机的才是大问题 + qCInfo(DDE_SHELL) << "System wakeup,hide black widget"; + checkSystemWakeupTimer->stop(); + m_model->setIsBlackMode(false); + } + }); /* org.freedesktop.login1.Manager */ - connect(m_login1Inter, &DBusLogin1Manager::PrepareForSleep, this, [this](bool isSleep) { + connect(m_login1Inter, &DBusLogin1Manager::PrepareForSleep, this, [this, checkSystemWakeupTimer](bool isSleep) { qCInfo(DDE_SHELL) << "DBus login1 manager prepare for sleep: " << isSleep; if (m_model->currentContentType() == SessionBaseModel::UpdateContent) { qCInfo(DDE_SHELL) << "Updating, do not answer `PrepareForSleep` signal"; @@ -177,6 +193,8 @@ void LockWorker::initConnections() if (isSleep) { endAuthentication(m_account, AT_All); destroyAuthentication(m_account); + checkSystemWakeupTimer->start(); + doSuspendTime = QDateTime::currentDateTime(); } else { // 非黑屏mode m_model->setIsBlackMode(isSleep); @@ -263,6 +281,7 @@ void LockWorker::initConnections() FullScreenBackground::setContent(LockContent::instance()); m_model->setCurrentContentType(SessionBaseModel::LockContent); }); + connect(&SignalBridge::ref(), &SignalBridge::requestSendExtraInfo, this, &LockWorker::sendExtraInfo); } void LockWorker::initData() @@ -363,7 +382,12 @@ void LockWorker::onAuthStateChanged(const int type, const int state, const QStri && m_model->currentModeState() != SessionBaseModel::ModeStatus::ConfirmPasswordMode) { m_model->setCurrentModeState(SessionBaseModel::ModeStatus::PasswordMode); } - m_resetSessionTimer->start(); + + // 处于序列化多因认证过程中时不要重置认证 + if (MFASequenceControl::instance().currentAuthType() == AuthType::AT_None) { + m_resetSessionTimer->start(); + } + m_model->updateAuthState(AuthType(type), AuthState(state), message); break; case AS_Failure: @@ -526,34 +550,38 @@ void LockWorker::doPowerAction(const SessionBaseModel::PowerAction action) } break; case SessionBaseModel::PowerAction::RequireRestart: - QProcess::startDetached("/usr/lib/deepin-daemon/dde-blackwidget", QStringList() << "nodbus"); - if (!isLocked() || m_model->currentModeState() == SessionBaseModel::ModeStatus::ShutDownMode || !isCheckPwdBeforeRebootOrShut()) { - m_sessionManagerInter->RequestReboot(); - } else { - createAuthentication(m_account); - m_model->setCurrentModeState(SessionBaseModel::ModeStatus::ConfirmPasswordMode); - } + m_model->setShutdownMode(true); + + QTimer::singleShot(350, this, [=] { + if (!isLocked() || m_model->currentModeState() == SessionBaseModel::ModeStatus::ShutDownMode || !isCheckPwdBeforeRebootOrShut()) { + m_sessionManagerInter->RequestReboot(); + } else { + createAuthentication(m_account); + m_model->setCurrentModeState(SessionBaseModel::ModeStatus::ConfirmPasswordMode); + } + + if (m_model->visibleShutdownWhenRebootOrShutdown()) { + return; + } - if (m_model->visibleShutdownWhenRebootOrShutdown()) { - return; - } - QTimer::singleShot(250, this, [=] { m_model->setVisible(false); }); return; case SessionBaseModel::PowerAction::RequireShutdown: - QProcess::startDetached("/usr/lib/deepin-daemon/dde-blackwidget", QStringList() << "nodbus"); - if (!isLocked() || m_model->currentModeState() == SessionBaseModel::ModeStatus::ShutDownMode || !isCheckPwdBeforeRebootOrShut()) { - m_sessionManagerInter->RequestShutdown(); - } else { - createAuthentication(m_account); - m_model->setCurrentModeState(SessionBaseModel::ModeStatus::ConfirmPasswordMode); - } + m_model->setShutdownMode(true); + + QTimer::singleShot(350, this, [=] { + if (!isLocked() || m_model->currentModeState() == SessionBaseModel::ModeStatus::ShutDownMode || !isCheckPwdBeforeRebootOrShut()) { + m_sessionManagerInter->RequestShutdown(); + } else { + createAuthentication(m_account); + m_model->setCurrentModeState(SessionBaseModel::ModeStatus::ConfirmPasswordMode); + } + + if (m_model->visibleShutdownWhenRebootOrShutdown()) { + return; + } - if (m_model->visibleShutdownWhenRebootOrShutdown()) { - return; - } - QTimer::singleShot(250, this, [=] { m_model->setVisible(false); }); return; @@ -628,6 +656,9 @@ void LockWorker::setCurrentUser(const std::shared_ptr user) void LockWorker::switchToUser(std::shared_ptr user) { + // 发生用户切换时锁住 + setLocked(true); + qCDebug(DDE_SHELL) << "LockWorker::switchToUser:" << m_account << user->name(); if (user->name() == m_account || *user == *m_model->currentUser()) { qCInfo(DDE_SHELL) << "Switch to current user:" << user->name() << user->isLogin(); @@ -992,3 +1023,14 @@ void LockWorker::onNoPasswordLoginChanged(const QString &account, bool noPasswor } } } + +void LockWorker::sendExtraInfo(const QString &account, AuthCommon::AuthType authType, const QString &info) +{ + switch (m_model->getAuthProperty().FrameworkState) { + case Available: + m_authFramework->sendExtraInfo(account, authType, info); + break; + default: + break; + } +} diff --git a/src/dde-lock/lockworker.h b/src/dde-lock/lockworker.h index 47d2d1d0..385e57a9 100644 --- a/src/dde-lock/lockworker.h +++ b/src/dde-lock/lockworker.h @@ -67,6 +67,7 @@ public slots: void authFinishedAction(); void onNoPasswordLoginChanged(const QString &account, bool noPassword); + void sendExtraInfo(const QString &account, AuthCommon::AuthType authType, const QString &info); private: void initConnections(); diff --git a/src/global_util/plugin_manager/login_plugin.cpp b/src/global_util/plugin_manager/login_plugin.cpp index 0dc642f0..8c504d2e 100644 --- a/src/global_util/plugin_manager/login_plugin.cpp +++ b/src/global_util/plugin_manager/login_plugin.cpp @@ -129,6 +129,20 @@ void LoginPlugin::notifyCurrentUserChanged(const QString &userName, uid_t uid) this->message(toJson(message)); } + +void LoginPlugin::authStateChanged(AuthCommon::AuthType type, AuthCommon::AuthState state, const QString &prompt) +{ + QJsonObject message; + message["CmdType"] = "AuthState"; + QJsonObject retDataObj; + retDataObj["AuthType"] = static_cast(type); + retDataObj["AuthState"] = state; + retDataObj["AuthPrompt"] = prompt; + message["Data"] = retDataObj; + + this->message(toJson(message)); +} + void LoginPlugin::updateConfig() { QJsonObject message; @@ -181,3 +195,21 @@ void LoginPlugin::notifyAuthFactorsChanged(int authFactors) this->message(toJson(message)); } + +/** + * @brief + * + * @return true 插件已经准备好做认证了(一般是用户已经输入了内容,比如验证码) + * @return false + */ +bool LoginPlugin::readyToAuth() +{ + QJsonObject message; + message["CmdType"] = "ReadyToAuth"; + const QString &result = this->message(toJson(message)); + const QJsonObject &dataObj = getDataObj(result); + if (dataObj.isEmpty()) + return true; + + return dataObj["ReadyToAuth"].toBool(true); +} diff --git a/src/global_util/plugin_manager/login_plugin.h b/src/global_util/plugin_manager/login_plugin.h index c628c050..7a595ad9 100644 --- a/src/global_util/plugin_manager/login_plugin.h +++ b/src/global_util/plugin_manager/login_plugin.h @@ -103,6 +103,10 @@ class LoginPlugin : public PluginBase inline PluginConfig pluginConfig() const { return m_pluginConfig; } + void authStateChanged(AuthCommon::AuthType type, AuthCommon::AuthState state, const QString &prompt); + + bool readyToAuth(); + private: PluginConfig m_pluginConfig; AuthType m_authType; diff --git a/src/global_util/plugin_manager/plugin_manager.cpp b/src/global_util/plugin_manager/plugin_manager.cpp index 4e5b1eb1..82c1b4e7 100644 --- a/src/global_util/plugin_manager/plugin_manager.cpp +++ b/src/global_util/plugin_manager/plugin_manager.cpp @@ -123,6 +123,23 @@ LoginPlugin* PluginManager::getFullManagedLoginPlugin() const return nullptr; } +LoginPlugin *PluginManager::getLoginPlugin(int authType) const +{ + for (const auto &plugin : m_plugins.values()) { + if (plugin && PluginBase::ModuleType::LoginType == plugin->type()) { + auto p = dynamic_cast(plugin); + p->updateConfig(); + if (p->defaultAuthType() == authType) { + return p; + } + } + } + + qDebug() << "plugin for type " << authType << "not found in " << m_plugins; + + return nullptr; +} + QList PluginManager::trayPlugins() const { QList list; @@ -139,7 +156,8 @@ PluginBase* PluginManager::createPlugin(dss::module::BaseModuleInterface* module { if (dss::module::BaseModuleInterface::LoginType == module->type() || dss::module::BaseModuleInterface::FullManagedLoginType == module->type() - || dss::module::BaseModuleInterface::IpcAssistLoginType == module->type()) { + || dss::module::BaseModuleInterface::IpcAssistLoginType == module->type() + || dss::module::BaseModuleInterface::PasswordExtendLoginType == module->type()) { return createLoginPlugin(module, version); } else if (dss::module::BaseModuleInterface::TrayType == module->type()) { TrayPlugin* plugin = createTrayPlugin(module, version); @@ -196,6 +214,17 @@ LoginPlugin* PluginManager::getAssistloginPlugin() const return nullptr; } +LoginPlugin *PluginManager::getFirstLoginPlugin(PluginBase::ModuleType type) const +{ + for (const auto& plugin : m_plugins.values()) { + if (plugin && type == plugin->type()) { + return dynamic_cast(plugin); + } + } + + return nullptr; +} + void PluginManager::setModel(SessionBaseModel *model) { if (!model) @@ -235,3 +264,57 @@ void PluginManager::broadcastAuthFactors(int authFactors) loginPlugin->notifyAuthFactorsChanged(authFactors); } } + +QPair PluginManager::parseMessage(const QString &message) +{ + QJsonParseError jsonParseError; + const QJsonDocument messageDoc = QJsonDocument::fromJson(message.toLatin1(), &jsonParseError); + QJsonObject retObj; + QJsonObject dataObj; + retObj["Code"] = 0; + retObj["Message"] = "Success"; + if (jsonParseError.error != QJsonParseError::NoError || messageDoc.isEmpty()) { + retObj["Code"] = -1; + retObj["Message"] = "Failed to analysis message info"; + qCWarning(DDE_SHELL) << "Failed to analysis message from plugin: " << message; + return qMakePair(toJson(retObj), messageDoc); + } + + return qMakePair(QStringLiteral(""), messageDoc); +} + +QString PluginManager::getProperties(const QJsonObject &messageObj) const +{ + QJsonArray properties; + properties = messageObj.value("Data").toArray(); + + QJsonObject retObj; + QJsonObject dataObj; + retObj["Code"] = 0; + retObj["Message"] = "Success"; + do + { + if (m_model.isNull()) { + retObj["Code"] = -1; + retObj["Message"] = "Session base model is nullptr"; + break; + } + if (properties.contains("AppType")) { + dataObj["AppType"] = m_model->appType(); + } + if (properties.contains("CurrentUser")) { + if (m_model->currentUser()) { + QJsonObject user; + user["Name"] = m_model->currentUser()->name(); + user["Uid"] = static_cast(m_model->currentUser()->uid()); + dataObj["CurrentUser"] = user; + } else { + retObj["Code"] = -1; + retObj["Message"] = "Current user is null!"; + } + } + } while(0); + retObj["Data"] = dataObj; + + return toJson(retObj); +} diff --git a/src/global_util/plugin_manager/plugin_manager.h b/src/global_util/plugin_manager/plugin_manager.h index be18cc2c..76d88fe4 100644 --- a/src/global_util/plugin_manager/plugin_manager.h +++ b/src/global_util/plugin_manager/plugin_manager.h @@ -27,10 +27,15 @@ class PluginManager : public QObject QList getLoginPlugins(int level = 1) const; LoginPlugin *getFullManagedLoginPlugin() const; LoginPlugin *getAssistloginPlugin() const; + LoginPlugin *getFirstLoginPlugin(PluginBase::ModuleType type) const; + LoginPlugin *getLoginPlugin(int authType) const; QList trayPlugins() const; bool contains(const QString &key) const; PluginBase *findPlugin(const QString &key) const; void broadcastAuthFactors(int authFactors); + QString getProperties(const QJsonObject &messageObj) const; + + static QPair parseMessage(const QString &message); signals: void trayPluginAdded(TrayPlugin *); diff --git a/src/global_util/public_func.cpp b/src/global_util/public_func.cpp index 95cda7d2..20940968 100644 --- a/src/global_util/public_func.cpp +++ b/src/global_util/public_func.cpp @@ -219,21 +219,6 @@ bool isDeepinAuth() return true; } -uint timeFromString(QString time) -{ -#ifndef ENABLE_DSS_SNIPE - if (time.isEmpty()) { - return QDateTime::currentDateTime().toTime_t(); - } - return QDateTime::fromString(time, Qt::ISODateWithMs).toLocalTime().toTime_t(); -#else - if (time.isEmpty()) { - return QDateTime::currentDateTime().toSecsSinceEpoch(); - } - return QDateTime::fromString(time, Qt::ISODateWithMs).toLocalTime().toSecsSinceEpoch(); -#endif -} - void setAppType(int type) { appType = type; diff --git a/src/global_util/public_func.h b/src/global_util/public_func.h index 69c02b38..38e010bc 100644 --- a/src/global_util/public_func.h +++ b/src/global_util/public_func.h @@ -56,11 +56,6 @@ void setPointer(); */ bool isDeepinAuth(); -/** - * @brief 把字符串解析成时间,然后转换为Unix时间戳 - */ -uint timeFromString(QString time); - /** * @brief 设置app类型,让程序知道应该获取哪个配置文件 */ diff --git a/src/global_util/qt-compat-helper.cpp b/src/global_util/qt-compat-helper.cpp new file mode 100644 index 00000000..e0f7b9f2 --- /dev/null +++ b/src/global_util/qt-compat-helper.cpp @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: 2021 - 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "qt-compat-helper.h" + +#include +#include + +uint QtCompatHelper::toSecsSinceEpoch(const QDateTime &dateTime) +{ +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + return static_cast(dateTime.toSecsSinceEpoch()); +#else + return static_cast(dateTime.toTime_t()); +#endif +} diff --git a/src/global_util/qt-compat-helper.h b/src/global_util/qt-compat-helper.h new file mode 100644 index 00000000..9d7bc397 --- /dev/null +++ b/src/global_util/qt-compat-helper.h @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: 2021 - 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef QT_COMPAT_HELPER_H +#define QT_COMPAT_HELPER_H + +#include + +class QString; + +/** + * @brief Qt版本兼容性帮助类 + * + * 用于统一处理Qt5和Qt6之间的API差异,避免在代码中到处使用版本检查宏。 + * 主要解决QDateTime相关方法在不同Qt版本中的差异。 + */ +class QtCompatHelper +{ +public: + /** + * @brief 获取QDateTime对象对应的秒级时间戳 + * + * 在Qt5中使用toTime_t()方法,在Qt6中使用toSecsSinceEpoch()方法 + * + * @param dateTime QDateTime对象 + * @return uint 秒级时间戳 + */ + static uint toSecsSinceEpoch(const QDateTime &dateTime); +}; + +#endif // QT_COMPAT_HELPER_H diff --git a/src/global_util/signal_bridge.h b/src/global_util/signal_bridge.h new file mode 100644 index 00000000..0d9f33d0 --- /dev/null +++ b/src/global_util/signal_bridge.h @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: 2016 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef SIGNALBRIDGE_H +#define SIGNALBRIDGE_H + +#include "constants.h" +#include "authcommon.h" + +#include + +#include + +class SignalBridge : public QObject, public Dtk::Core::DSingleton +{ + Q_OBJECT + friend class Dtk::Core::DSingleton; +private: + explicit SignalBridge(QObject *parent = nullptr) : QObject(parent) {} + +signals: + /** + * @brief 发送 extra info 给deepin-authentication + */ + void requestSendExtraInfo(const QString &account, const AuthCommon::AuthType authType, const QString &token); +}; + +#endif // SIGNALBRIDGE_H diff --git a/src/libdde-auth/authcommon.h b/src/libdde-auth/authcommon.h index 69932702..a4e5a47e 100644 --- a/src/libdde-auth/authcommon.h +++ b/src/libdde-auth/authcommon.h @@ -60,6 +60,7 @@ enum AuthType { AT_FingerVein = 1 << 5, // 指静脉 AT_Iris = 1 << 6, // 虹膜 AT_Passkey = 1 << 7, // Passkey + AT_Pattern = 1 << 8, // 手势 AT_PAM = 1 << 29, // PAM AT_Custom = 1 << 30, // 自定义 AT_All = -1 // all @@ -86,7 +87,9 @@ enum AuthState { AS_Ended, // 认证已结束,调用 End 之后,每种成功关闭的都会发送此信号,当某种认证类型被锁定时,也会触发此信号 AS_Locked, // 认证已锁定,当认证类型锁定时,触发此信号。该信号不会给出锁定等待时间信息 AS_Recover, // 设备恢复,需要调用 Start 重新开启认证,对应 AS_Exception - AS_Unlocked // 认证解锁,对应 AS_Locked + AS_Unlocked, // 认证解锁,对应 AS_Locked + AS_Unknown, // 未知状态 + AS_VerifyCode, // 需要验证码 }; Q_ENUM_NS(AuthState) diff --git a/src/libdde-auth/deepinauthframework.cpp b/src/libdde-auth/deepinauthframework.cpp index 0c92db5e..f1e6959b 100644 --- a/src/libdde-auth/deepinauthframework.cpp +++ b/src/libdde-auth/deepinauthframework.cpp @@ -60,6 +60,12 @@ DeepinAuthFramework::DeepinAuthFramework(QObject *parent) watcher->deleteLater(); }); } + + connect(&m_authReminder, &QTimer::timeout, this, []{ + qCWarning(DDE_SHELL) << "No auth result recived after token send"; + }); + + m_authReminder.setSingleShot(false); } void DeepinAuthFramework::onDeviceChanged(const int authType, const int state) @@ -268,7 +274,7 @@ void DeepinAuthFramework::SendToken(const QString &token) if (!m_waitToken) { return; } - qCInfo(DDE_SHELL) << "Send token to PAM, token: " << token; + qCInfo(DDE_SHELL) << "Token arrived, is it empty ? " << token.isEmpty(); m_token = token; m_waitToken = false; } @@ -330,6 +336,11 @@ void DeepinAuthFramework::CreateAuthController(const QString &account, const Aut connect(authControllerInter, &AuthControllerInter::PINLenChanged, this, &DeepinAuthFramework::PINLenChanged); connect(authControllerInter, &AuthControllerInter::PromptChanged, this, &DeepinAuthFramework::PromptChanged); connect(authControllerInter, &AuthControllerInter::Status, this, [this](int flag, int state, const QString &msg) { + if (m_authReminder.isActive()) { + m_authReminder.stop(); + qCWarning(DDE_SHELL) << "reminder stopped for status changed"; + } + const AuthType type = AUTH_TYPE_CAST(flag); const AuthState authState = AUTH_STATE_CAST(state); emit AuthStateChanged(type, authState, msg); @@ -434,17 +445,15 @@ void DeepinAuthFramework::EndAuthentication(const QString &account, const AuthFl */ void DeepinAuthFramework::SendTokenToAuth(const QString &account, const AuthType authType, const QString &token) { + qCInfo(DDE_SHELL) << "Send token to auth, account: " << account << ", auth type:" << authType; if (!m_authenticateControllers->contains(account)) { qCWarning(DDE_SHELL) << "account not included"; - // 无论出于什么原因到这个逻辑,必须更新前端当前认证状态 - // 否则将导致登录停在这里 - Q_EMIT TokenAccountMismatch(m_lastAuthUser); return; } - qCInfo(DDE_SHELL) << "Send token to auth, account: " << account << ", auth type:" << authType; QByteArray ba = EncryptHelper::ref().getEncryptedToken(token); m_authenticateControllers->value(account)->SetToken(authType, ba); + m_authReminder.start(3000); } /** @@ -646,3 +655,19 @@ bool DeepinAuthFramework::isDeepinAuthValid() const return QDBusConnection::systemBus().interface()->isServiceRegistered(DSS_DBUS::authenticateService) && (Available == GetFrameworkState()); } + +void DeepinAuthFramework::sendExtraInfo(const QString &account, AuthType authType, const QString &info) +{ + if (!m_authenticateControllers->contains(account)) { + qCWarning(DDE_SHELL) << "Do not contain account"; + return; + } + qInfo() << "Send extra info to authentication:" << account << authType; + AuthControllerInter *inter = m_authenticateControllers->value(account); + QDBusMessage msg = QDBusMessage::createMethodCall(inter->service(), inter->path(), inter->interface(), "SetExtraInfo"); + msg << authType << QString("{\"type\":%1,\"data\":\"%2\"}").arg(authType).arg(info); + QDBusPendingCall reply = inter->connection().asyncCall(msg, 2000); + if (reply.isError()) { + qCWarning(DDE_SHELL) << "Send extra info error:" << reply.error(); + } +} diff --git a/src/libdde-auth/deepinauthframework.h b/src/libdde-auth/deepinauthframework.h index 394eab2f..31455998 100644 --- a/src/libdde-auth/deepinauthframework.h +++ b/src/libdde-auth/deepinauthframework.h @@ -64,7 +64,7 @@ class DeepinAuthFramework : public QObject bool authSessionExist(const QString &account) const; bool isDeepinAuthValid() const; bool isDAStartupCompleted() const { return m_isDAStartupCompleted;} - + void sendExtraInfo(const QString &account, AuthCommon::AuthType authType, const QString &info); signals: void startupCompleted(); diff --git a/src/lightdm-deepin-greeter/greeterworker.cpp b/src/lightdm-deepin-greeter/greeterworker.cpp index 9461eb08..21cf5af3 100644 --- a/src/lightdm-deepin-greeter/greeterworker.cpp +++ b/src/lightdm-deepin-greeter/greeterworker.cpp @@ -9,6 +9,8 @@ #include "userinfo.h" #include "dconfig_helper.h" #include "login_plugin_util.h" +#include "mfasequencecontrol.h" +#include "signal_bridge.h" #include @@ -324,6 +326,8 @@ void GreeterWorker::initConnections() // 监听terminal锁定状态 QDBusConnection::systemBus().connect(DSS_DBUS::accountsService, DSS_DBUS::accountsPath, "org.freedesktop.DBus.Properties", "PropertiesChanged", this, SLOT(terminalLockedChanged(QDBusMessage))); + + connect(&SignalBridge::ref(), &SignalBridge::requestSendExtraInfo, this, &GreeterWorker::sendExtraInfo); } void GreeterWorker::initData() @@ -908,7 +912,12 @@ void GreeterWorker::onAuthStateChanged(const int type, const int state, const QS case AS_Success: if (m_model->currentModeState() != SessionBaseModel::ResetPasswdMode) m_model->setCurrentModeState(SessionBaseModel::ModeStatus::PasswordMode); - m_resetSessionTimer->start(); + + // 处于序列化多因认证过程中时不要重置认证 + if (MFASequenceControl::instance().currentAuthType() == AuthType::AT_None) { + m_resetSessionTimer->start(); + } + m_model->updateAuthState(AuthType(type), AuthState(state), message); break; case AS_Failure: @@ -1217,3 +1226,14 @@ void GreeterWorker::prepareShutdownSound() //soundPlayerInter.call("PrepareShutdownSound", static_cast(m_model->currentUser()->uid())); } #endif + +void GreeterWorker::sendExtraInfo(const QString &account, AuthCommon::AuthType authType, const QString &info) +{ + switch (m_model->getAuthProperty().FrameworkState) { + case Available: + m_authFramework->sendExtraInfo(account, authType, info); + break; + default: + break; + } +} \ No newline at end of file diff --git a/src/lightdm-deepin-greeter/greeterworker.h b/src/lightdm-deepin-greeter/greeterworker.h index dbc5a6d5..ab38a854 100644 --- a/src/lightdm-deepin-greeter/greeterworker.h +++ b/src/lightdm-deepin-greeter/greeterworker.h @@ -65,6 +65,7 @@ private slots: void onSessionCreated(); void terminalLockedChanged(const QDBusMessage &msg); void resetAuth(const QString &); + void sendExtraInfo(const QString &account, AuthCommon::AuthType authType, const QString &info); private: void initConnections(); diff --git a/src/session-widgets/assist_login_widget.cpp b/src/session-widgets/assist_login_widget.cpp index 52d1b478..bb0a5f7c 100644 --- a/src/session-widgets/assist_login_widget.cpp +++ b/src/session-widgets/assist_login_widget.cpp @@ -3,6 +3,9 @@ // SPDX-License-Identifier: GPL-3.0-or-later #include "assist_login_widget.h" +#include "plugin_manager.h" + +using DSS_PLUGIN_TYPE = dss::module::BaseModuleInterface::ModuleType; QList AssistLoginWidget::AssistLoginWidgetObjs = {}; @@ -30,6 +33,7 @@ void AssistLoginWidget::setModule(LoginPlugin *module) m_module = module; setCallback(); + updateVisible(); } void AssistLoginWidget::initUI() @@ -43,9 +47,18 @@ void AssistLoginWidget::initUI() qCInfo(DDE_SHELL) << Q_FUNC_INFO << "m_module->init()"; m_module->init(); m_mainLayout->addWidget(m_module->content()); + setFocusProxy(m_module->content()); } } +dss::module::BaseModuleInterface::ModuleType AssistLoginWidget::pluginType() const +{ + if (m_module) + return m_module->type(); + + return dss::module::BaseModuleInterface::ModuleType::LoginType; +} + void AssistLoginWidget::setCallback() { if (!m_module) { @@ -68,7 +81,7 @@ void AssistLoginWidget::authCallback(const LoginPlugin::AuthCallbackData *callba QString AssistLoginWidget::messageCallback(const QString &message, void *app_data) { - qCDebug(DDE_SHELL) << Q_FUNC_INFO << "Received message: " << message; + qCInfo(DDE_SHELL) << "Received message: " << message; QJsonParseError jsonParseError; const QJsonDocument messageDoc = QJsonDocument::fromJson(message.toLatin1(), &jsonParseError); @@ -94,7 +107,13 @@ QString AssistLoginWidget::messageCallback(const QString &message, void *app_dat QString cmdType = messageObj.value("CmdType").toString(); qCInfo(DDE_SHELL) << "Cmd type: " << cmdType; if (cmdType == "GetProperties") { - } else if (cmdType == "setAuthTypeInfo") { + return PluginManager::instance()->getProperties(messageObj); + } + + if (cmdType == "ReadyToAuthChanged") { + Q_EMIT assistLoginWidget->readyToAuthChanged(messageObj["Data"].toBool()); + } else if (cmdType == "PluginEnabledChanged") { + assistLoginWidget->setVisible(messageObj["Data"].toBool()); } retObj["Data"] = dataObj; @@ -104,13 +123,22 @@ QString AssistLoginWidget::messageCallback(const QString &message, void *app_dat void AssistLoginWidget::setAuthData(const LoginPlugin::AuthCallbackData &callbackData) { m_currentAuthData = callbackData; + qCDebug(DDE_SHELL) << Q_FUNC_INFO << m_currentAuthData.result; + const QString &account = callbackData.account; + const QString &token = callbackData.token; + if (DSS_PLUGIN_TYPE::PasswordExtendLoginType == pluginType()) { + qCInfo(DDE_SHELL) << "Request send extra info, result: " << callbackData.result; + m_extraInfo = token; + if (dss::module::AuthResult::Success == callbackData.result) + Q_EMIT requestSendExtraInfo(token); + return; + } + if (dss::module::AuthResult::Success != callbackData.result) { qCWarning(DDE_SHELL) << "Custom auth result is not success"; return; } - qCDebug(DDE_SHELL) << Q_FUNC_INFO << m_currentAuthData.result; - const QString &account = callbackData.account; - const QString &token = callbackData.token; + if (!account.isEmpty()) { qCInfo(DDE_SHELL) << Q_FUNC_INFO << "requestSendToken: " << account << token; emit requestSendToken(account, token); @@ -176,3 +204,24 @@ void AssistLoginWidget::setPluginConfig(const LoginPlugin::PluginConfig &pluginC Q_EMIT requestPluginConfigChanged(m_pluginConfig); } } + +void AssistLoginWidget::setAuthState(AuthCommon::AuthState state, const QString &result) +{ + if (m_module) { + m_module->authStateChanged(AuthCommon::AT_Password, state, result); + } +} + +void AssistLoginWidget::updateVisible() +{ + if (m_module) + setVisible(m_module->isPluginEnabled()); +} + +bool AssistLoginWidget::readyToAuth() const +{ + if (m_module) + return m_module->readyToAuth(); + + return true; +} diff --git a/src/session-widgets/assist_login_widget.h b/src/session-widgets/assist_login_widget.h index 11dd2585..c1af183b 100644 --- a/src/session-widgets/assist_login_widget.h +++ b/src/session-widgets/assist_login_widget.h @@ -27,15 +27,22 @@ class AssistLoginWidget: public AuthModule void startAuth(); void resetAuth(); void updateConfig(); + void updateVisible(); const LoginPlugin::PluginConfig &getPluginConfig() const; void setPluginConfig(const LoginPlugin::PluginConfig &PluginConfig); + void setAuthState(AuthCommon::AuthState state, const QString &result); + dss::module::BaseModuleInterface::ModuleType pluginType() const; + bool readyToAuth() const; + QString extraInfo() const { return m_extraInfo; } Q_SIGNALS: void requestCheckAccount(const QString &account); void requestSendToken(const QString &account, const QString &token); void requestPluginConfigChanged(const LoginPlugin::PluginConfig &PluginConfig); void requestHidePlugin(); + void readyToAuthChanged(bool ready); + void requestSendExtraInfo(const QString &info); private: void setCallback(); @@ -49,6 +56,7 @@ class AssistLoginWidget: public AuthModule LoginPlugin *m_module; static QList AssistLoginWidgetObjs; LoginPlugin::PluginConfig m_pluginConfig; + QString m_extraInfo; }; diff --git a/src/session-widgets/auth_custom.cpp b/src/session-widgets/auth_custom.cpp index d6fd560a..6f3592f8 100644 --- a/src/session-widgets/auth_custom.cpp +++ b/src/session-widgets/auth_custom.cpp @@ -13,8 +13,8 @@ using namespace dss::module; QList AuthCustom::AuthCustomObjs = {}; -AuthCustom::AuthCustom(QWidget *parent) - : AuthModule(AuthCommon::AT_Custom, parent) +AuthCustom::AuthCustom(QWidget *parent, AuthCommon::AuthType type) + : AuthModule(type, parent) , m_mainLayout(new QVBoxLayout(this)) , m_plugin(nullptr) , m_model(nullptr) @@ -26,6 +26,9 @@ AuthCustom::AuthCustom(QWidget *parent) m_mainLayout->setSpacing(0); AuthCustomObjs.append(this); + + // 需要父类的定时解锁 + AuthModule::initConnections(); } AuthCustom::~AuthCustom() @@ -45,7 +48,7 @@ void AuthCustom::detachPlugin() m_plugin = nullptr; } -void AuthCustom::setModule(LoginPlugin* module) +void AuthCustom::setModule(LoginPlugin *module) { if (m_plugin) { return; @@ -114,12 +117,17 @@ void AuthCustom::setAuthState(const AuthCommon::AuthState state, const QString & case AuthCommon::AS_Started: // greeter需要等lightdm开启验证后再发送认证信息 if (m_model->appType() == AuthCommon::AppType::Lock) { + // FIXME: 并不是所有插件都会有此场景下发token的需求,因此注意清空authdata sendAuthToken(); } break; case AuthCommon::AS_Success: emit authFinished(state); break; + case AuthCommon::AS_Unlocked: + // 切换用户时,会有解锁的动作,因此需要响应unlock + emit activeAuth(m_type); + break; default: break; } @@ -133,12 +141,15 @@ void AuthCustom::setAuthData(const LoginPlugin::AuthCallbackData &callbackData) return; } - const QString &account = callbackData.account; + const QString &account = m_currentAuthData.account; if (!account.isEmpty()) { qCInfo(DDE_SHELL) << "Request check account: " << account; emit requestCheckAccount(account, m_plugin->pluginConfig().switchUserWhenCheckAccount); } else { - emit requestSendToken(""); + // 如果插件的token信息带上了用户名,则通过checkAccount信号通知外面去做用户切换或发送token + // 隐式地要求插件仅在主动切换用户时需要填充token的account,注意需要在代码中增加信号响应,参见sfawidget + Q_EMIT requestSendToken(m_currentAuthData.token); + resetAuth(); } } @@ -275,6 +286,18 @@ bool AuthCustom::event(QEvent *e) return AuthModule::event(e); } +// 响应解锁 +void AuthCustom::updateUnlockPrompt() +{ + // DA不会主动刷新limit信息,以当前解锁时间判断 + if (m_integerMinutes == 0) { + QTimer::singleShot(1000, this, [this] { + emit activeAuth(m_type); + }); + qCInfo(DDE_SHELL) << "Waiting authentication service..."; + } +} + void AuthCustom::updateConfig() { if (!m_plugin) @@ -375,6 +398,27 @@ void AuthCustom::setLimitsInfo(const QMap &limitsInfo) m_plugin->message(toJson(message)); } +void AuthCustom::setLimitsInfo(const LimitsInfo &limitsInfo) +{ + if (!m_plugin) + return; + + QJsonObject message; + message["CmdType"] = "LimitsInfo"; + + // FIXME: 注意limitsinfo这个结构体没有必要存在两个定义 + QJsonObject obj; + obj["Locked"] = limitsInfo.locked; + obj["MaxTries"] = static_cast(limitsInfo.maxTries); + obj["NumFailures"] = static_cast(limitsInfo.numFailures); + obj["UnlockSecs"] = static_cast(limitsInfo.unlockSecs);; + obj["UnlockTime"] = limitsInfo.unlockTime; + message["Data"] = obj; + m_plugin->message(toJson(message)); + + AuthModule::setLimitsInfo(limitsInfo); +} + QJsonObject AuthCustom::getRootObj(const QString &jsonStr) { QJsonParseError jsonParseError; diff --git a/src/session-widgets/auth_custom.h b/src/session-widgets/auth_custom.h index 4d055f54..741f2220 100644 --- a/src/session-widgets/auth_custom.h +++ b/src/session-widgets/auth_custom.h @@ -11,6 +11,7 @@ #include "login_plugin.h" #include +#include class SessionBaseModel; @@ -23,11 +24,9 @@ class AuthCustom : public AuthModule DeepinAuthenticate // deepin authentication framework - 深度认证框架 }; - public: - - explicit AuthCustom(QWidget *parent = nullptr); - ~AuthCustom(); + explicit AuthCustom(QWidget *parent = nullptr, AuthCommon::AuthType type = AuthCommon::AT_Custom); + virtual ~AuthCustom() override; void setModule(LoginPlugin *module); LoginPlugin* getLoginPlugin() const; @@ -47,7 +46,7 @@ class AuthCustom : public AuthModule void lightdmAuthStarted(); void notifyAuthState(AuthCommon::AuthFlags authType, AuthCommon::AuthState state); - using AuthModule::setLimitsInfo; // 避免警告:XXX hides overloaded virtual function + void setLimitsInfo(const LimitsInfo &limitsInfo) override; void setLimitsInfo(const QMap &limitsInfo); void setCustomAuthIndex(int index) { m_customAuthIndex = index; } @@ -59,6 +58,7 @@ class AuthCustom : public AuthModule bool event(QEvent *e) override; private: + void updateUnlockPrompt() override; void setCallback(); static void authCallback(const LoginPlugin::AuthCallbackData *callbackData, void *app_data); static QString messageCallback(const QString &message, void *app_data); diff --git a/src/session-widgets/auth_face.cpp b/src/session-widgets/auth_face.cpp index e3ff5d39..11b8841d 100644 --- a/src/session-widgets/auth_face.cpp +++ b/src/session-widgets/auth_face.cpp @@ -12,6 +12,7 @@ AuthFace::AuthFace(QWidget *parent) : AuthModule(AuthCommon::AT_Face, parent) , m_aniIndex(-1) , m_textLabel(new DLabel(this)) + , m_filterTimer(new QTimer(this)) { setObjectName(QStringLiteral("AuthFace")); setAccessibleName(QStringLiteral("AuthFace")); @@ -37,6 +38,10 @@ void AuthFace::initUI() m_authStateLabel->installEventFilter(this); setAuthStateStyle(LOGIN_WAIT); mainLayout->addWidget(m_authStateLabel, 0, Qt::AlignRight | Qt::AlignVCenter); + + // 多数摄像头模组前2s内会有由暗变亮的过程,特别是重新上电的第一次,例如待机/休眠/重启等场景,此时获取的人脸图像会报错,此处增加一个定时器,过滤前2s的错误 + m_filterTimer->setInterval(2000); + m_filterTimer->setSingleShot(true); } /** @@ -68,6 +73,7 @@ void AuthFace::reset() */ void AuthFace::setAuthState(const AuthCommon::AuthState state, const QString &result) { + static bool startFilterTimer = false; m_state = state; switch (state) { case AuthCommon::AS_Success: @@ -79,6 +85,7 @@ void AuthFace::setAuthState(const AuthCommon::AuthState state, const QString &re m_showPrompt = true; emit authFinished(state); emit retryButtonVisibleChanged(false); + startFilterTimer = false; break; case AuthCommon::AS_Failure: { setAnimationState(false); @@ -92,6 +99,7 @@ void AuthFace::setAuthState(const AuthCommon::AuthState state, const QString &re } emit retryButtonVisibleChanged(true); emit authFinished(state); + startFilterTimer = false; break; } case AuthCommon::AS_Cancel: @@ -106,9 +114,15 @@ void AuthFace::setAuthState(const AuthCommon::AuthState state, const QString &re m_showPrompt = true; break; case AuthCommon::AS_Verify: + if (!startFilterTimer) { + m_filterTimer->start(); + startFilterTimer = true; + } setAnimationState(false); setAuthStateStyle(isMFA() ? LOGIN_SPINNER : AUTH_LOCK); - m_textLabel->setText(result); + if (!m_filterTimer->isActive()) { + m_textLabel->setText(result); + } break; case AuthCommon::AS_Exception: setAnimationState(false); @@ -121,6 +135,7 @@ void AuthFace::setAuthState(const AuthCommon::AuthState state, const QString &re setAuthStateStyle(isMFA() ? LOGIN_WAIT : AUTH_LOCK); break; case AuthCommon::AS_Started: + startFilterTimer = false; m_textLabel->setText(tr("Verify your Face ID")); break; case AuthCommon::AS_Ended: diff --git a/src/session-widgets/auth_face.h b/src/session-widgets/auth_face.h index 00647b82..e7400a5d 100644 --- a/src/session-widgets/auth_face.h +++ b/src/session-widgets/auth_face.h @@ -38,6 +38,7 @@ public slots: private: int m_aniIndex; DLabel *m_textLabel; + QTimer *m_filterTimer; }; #endif // AUTHFACE_H diff --git a/src/session-widgets/auth_module.cpp b/src/session-widgets/auth_module.cpp index f5e6d792..f3ddf618 100644 --- a/src/session-widgets/auth_module.cpp +++ b/src/session-widgets/auth_module.cpp @@ -153,13 +153,7 @@ void AuthModule::updateUnlockTime() void AuthModule::updateIntegerMinutes() { if (QDateTime::fromString(m_limitsInfo->unlockTime, Qt::ISODateWithMs) > QDateTime::currentDateTime()) { -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - qreal intervalSeconds = QDateTime::fromString(m_limitsInfo->unlockTime, Qt::ISODateWithMs).toLocalTime().toSecsSinceEpoch() - - QDateTime::currentDateTimeUtc().toSecsSinceEpoch(); -#else - qreal intervalSeconds = QDateTime::fromString(m_limitsInfo->unlockTime, Qt::ISODateWithMs).toLocalTime().toTime_t() - - QDateTime::currentDateTimeUtc().toTime_t(); -#endif + qreal intervalSeconds = QDateTime::fromString(m_limitsInfo->unlockTime, Qt::ISODateWithMs).toLocalTime().toSecsSinceEpoch() - QDateTime::currentDateTimeUtc().toSecsSinceEpoch(); m_integerMinutes = static_cast(qCeil(intervalSeconds / 60)); } else { m_integerMinutes = 0; diff --git a/src/session-widgets/auth_password.cpp b/src/session-widgets/auth_password.cpp index 444ec6c7..f719042d 100644 --- a/src/session-widgets/auth_password.cpp +++ b/src/session-widgets/auth_password.cpp @@ -38,6 +38,7 @@ const QString PASSWORD_SHOWN = QStringLiteral(":/misc/images/password-shown.svg" const QString DConfig_LongPressDisplayPassword = "longPressDisplayPassword"; using namespace AuthCommon; +using DSS_PLUGIN_TYPE = dss::module::BaseModuleInterface::ModuleType; AuthPassword::AuthPassword(QWidget *parent) : AuthModule(AT_Password, parent) @@ -78,7 +79,7 @@ void AuthPassword::initUI() { QVBoxLayout *mainLayout = new QVBoxLayout(this); mainLayout->setContentsMargins(0, 0, 0, 0); - mainLayout->setSpacing(5); + mainLayout->setSpacing(10); m_lineEdit->setClearButtonEnabled(false); m_lineEdit->setEchoMode(QLineEdit::Password); @@ -105,9 +106,15 @@ void AuthPassword::initUI() /* 缩放因子 */ passwordLayout->addStretch(1); + /* 认证状态 */ + m_authStateLabel = new DLabel(this); + m_authStateLabel->setVisible(false); + setAuthStateStyle(LOGIN_WAIT); + passwordLayout->addWidget(m_authStateLabel, 0, Qt::AlignRight | Qt::AlignVCenter); + /*显示密码*/ m_passwordShowBtn->setAccessibleName(QStringLiteral("PasswordShow")); - m_passwordShowBtn->setContentsMargins(0,0,0,0); + m_passwordShowBtn->setContentsMargins(0, 0, 0, 0); m_passwordShowBtn->setFocusPolicy(Qt::NoFocus); m_passwordShowBtn->setCursor(Qt::ArrowCursor); m_passwordShowBtn->setFlat(true); @@ -116,11 +123,6 @@ void AuthPassword::initUI() m_passwordShowBtn->setVisible(true); passwordLayout->addWidget(m_passwordShowBtn, 0, Qt::AlignRight | Qt::AlignVCenter); - /* 认证状态 */ - m_authStateLabel = new DLabel(this); - m_authStateLabel->setVisible(false); - setAuthStateStyle(LOGIN_WAIT); - passwordLayout->addWidget(m_authStateLabel, 0, Qt::AlignRight | Qt::AlignVCenter); /* 密码提示 */ m_passwordHintBtn->setAccessibleName(QStringLiteral("PasswordHint")); m_passwordHintBtn->setContentsMargins(0, 0, 0, 0); @@ -132,12 +134,11 @@ void AuthPassword::initUI() m_passwordHintBtn->setVisible(false); passwordLayout->addWidget(m_passwordHintBtn, 0, Qt::AlignRight | Qt::AlignVCenter); mainLayout->addWidget(m_lineEdit); - auto plugin = PluginManager::instance()->getAssistloginPlugin(); + auto plugin = PluginManager::instance()->getAssistloginPlugin(); if (plugin) { m_isPasswdAuthWidgetReplaced = true; m_assistLoginWidget = new AssistLoginWidget(this); - m_assistLoginWidget->setModule(plugin); m_assistLoginWidget->initUI(); mainLayout->addWidget(m_assistLoginWidget); @@ -146,6 +147,16 @@ void AuthPassword::initUI() m_lineEdit->show(); } + // 密码框下面增加一个认证界面 + auto extendPlugin = PluginManager::instance()->getFirstLoginPlugin(dss::module::BaseModuleInterface::PasswordExtendLoginType); + if (extendPlugin) { + m_assistLoginWidget = new AssistLoginWidget(this); + m_assistLoginWidget->setModule(extendPlugin); + m_assistLoginWidget->initUI(); + mainLayout->addWidget(m_assistLoginWidget); + } else { + qCDebug(DDE_SHELL) << "There's no password extend plugin"; + } updatePasswordTextMargins(); m_passwordTipsWidget->hide(); } @@ -156,20 +167,27 @@ void AuthPassword::initUI() void AuthPassword::initConnections() { AuthModule::initConnections(); + + auto blockEditSig = [this]() -> bool { + return m_assistLoginWidget + && m_assistLoginWidget->isVisible() + && DSS_PLUGIN_TYPE::PasswordExtendLoginType == m_assistLoginWidget->pluginType() + && !m_assistLoginWidget->readyToAuth(); + }; /* 密码提示 */ connect(m_passwordHintBtn, &DIconButton::clicked, this, &AuthPassword::showPasswordHint); /* 密码输入框 */ - connect(m_lineEdit, &DLineEditEx::focusChanged, this, [this](const bool focus) { + connect(m_lineEdit, &DLineEditEx::focusChanged, this, [this, blockEditSig](const bool focus) { if (!focus) m_lineEdit->setAlert(false); m_authStateLabel->setVisible(!focus && m_showAuthState); updatePasswordTextMargins(); emit focusChanged(focus); - if (focus) { + if (focus && !blockEditSig()) { emit lineEditTextChanged(m_lineEdit->text()); } }); - connect(m_lineEdit, &DLineEditEx::textChanged, this, [this](const QString &text) { + connect(m_lineEdit, &DLineEditEx::textChanged, this, [this, blockEditSig](const QString &text) { m_lineEdit->hideAlertMessage(); hidePasswordHintWidget(); m_lineEdit->setAlert(false); @@ -181,11 +199,18 @@ void AuthPassword::initConnections() m_passwordTipsWidget->adjustSize(); emit passwordErrorTipsClearChanged(true); } - emit lineEditTextChanged(text); + if (!blockEditSig()) + emit lineEditTextChanged(text); }); - connect(m_lineEdit, &DLineEditEx::returnPressed, this, [this] { - if (!m_lineEdit->lineEdit()->isReadOnly()) // 避免用户在验证的时候反复点击 - emit requestAuthenticate(); + connect(m_lineEdit, &DLineEditEx::returnPressed, this, [this, blockEditSig] { + if (!m_lineEdit->lineEdit()->isReadOnly()) { // 避免用户在验证的时候反复点击 + if (!blockEditSig()) { + emit requestAuthenticate(); + } else { + setFocusProxy(m_assistLoginWidget); + m_assistLoginWidget->setFocus(); + } + } }); if (DConfigHelper::instance()->getConfig(DConfig_LongPressDisplayPassword, true).toBool()) { @@ -217,14 +242,28 @@ void AuthPassword::initConnections() } if (m_assistLoginWidget && m_isPasswdAuthWidgetReplaced) { - connect(m_assistLoginWidget, &AssistLoginWidget::requestPluginConfigChanged, this, [this] (const LoginPlugin::PluginConfig pluginConfig) { + connect(m_assistLoginWidget, &AssistLoginWidget::requestPluginConfigChanged, this, [this](const LoginPlugin::PluginConfig pluginConfig) { Q_EMIT requestPluginConfigChanged(pluginConfig); }); - connect(m_assistLoginWidget, &AssistLoginWidget::requestHidePlugin, this, [ = ] { + connect(m_assistLoginWidget, &AssistLoginWidget::requestHidePlugin, this, [=] { hidePlugin(); }); connect(m_assistLoginWidget, &AssistLoginWidget::requestSendToken, this, &AuthPassword::requestPluginAuthToken); } + + if (m_assistLoginWidget) { + connect(m_assistLoginWidget, &AssistLoginWidget::requestSendExtraInfo, this, [this](const QString &info) { + if (!m_lineEdit->text().isEmpty()) { + Q_EMIT requestAuthenticate(); + return; + } + + // 焦点切换到密码输入框 + m_lineEdit->setFocus(); + setFocusProxy(m_lineEdit); + }); + connect(m_assistLoginWidget, &AssistLoginWidget::readyToAuthChanged, this, &AuthPassword::onReadyToAuthChanged); + } } /** @@ -235,6 +274,7 @@ void AuthPassword::reset() m_lineEdit->clear(); m_lineEdit->setAlert(false); m_lineEdit->hideAlertMessage(); + setFocusProxy(m_lineEdit); hidePasswordHintWidget(); setLineEditEnabled(true); setLineEditInfo(tr("Password"), PlaceHolderText); @@ -361,6 +401,11 @@ void AuthPassword::setAuthState(const AuthState state, const QString &result) setLineEditEnabled(true); m_showPrompt = true; break; + case AS_VerifyCode: + setAnimationState(false); + setAuthStateStyle(LOGIN_WAIT); + setLineEditEnabled(true); + break; default: setAnimationState(false); setAuthStateStyle(LOGIN_WAIT); @@ -369,6 +414,9 @@ void AuthPassword::setAuthState(const AuthState state, const QString &result) qCWarning(DDE_SHELL) << "Error! The state of Password Auth is wrong, state: " << state << ", result: " << result; break; } + if (m_assistLoginWidget) { + m_assistLoginWidget->setAuthState(state, result); + } update(); } @@ -576,8 +624,8 @@ void AuthPassword::setPasswordHintBtnVisible(const bool isVisible) void AuthPassword::setResetPasswordMessageVisible(const bool isVisible, bool fromResetDialog) { qCDebug(DDE_SHELL) << "Set reset password message visible, incoming visible:" << isVisible - << " current visible:" << m_resetPasswordMessageVisible - << " fromResetDialog " << fromResetDialog; + << " current visible:" << m_resetPasswordMessageVisible + << " fromResetDialog " << fromResetDialog; if (isVisible && fromResetDialog) { m_resetDialogShow = false; } @@ -629,7 +677,7 @@ void AuthPassword::showResetPasswordMessage() // DFloatingMessage中有两个按钮一个是DIconButton,另一个是继承于DIconButton的DDialogCloseButton,需要区分 QList btnList = m_resetPasswordFloatingMessage->findChildren(); foreach (const auto iconButton, btnList) { - DDialogCloseButton * closeButton = qobject_cast(iconButton); + DDialogCloseButton *closeButton = qobject_cast(iconButton); if (closeButton) { continue; } @@ -656,7 +704,7 @@ void AuthPassword::showResetPasswordMessage() emit m_resetPasswordFloatingMessage->closeButtonClicked(); }); - connect(m_resetPasswordFloatingMessage, &DFloatingMessage::closeButtonClicked, this, [this](){ + connect(m_resetPasswordFloatingMessage, &DFloatingMessage::closeButtonClicked, this, [this]() { if (m_resetPasswordFloatingMessage) { m_resetPasswordFloatingMessage->deleteLater(); m_resetPasswordFloatingMessage = nullptr; @@ -712,7 +760,7 @@ bool AuthPassword::isUserAccountBinded() } QString uuid = retUUID.toString(); - QDBusReply retLocalBindCheck= syncHelperInter.call("LocalBindCheck", uosid, uuid); + QDBusReply retLocalBindCheck = syncHelperInter.call("LocalBindCheck", uosid, uuid); if (!syncHelperInter.isValid()) { return false; } @@ -730,7 +778,7 @@ bool AuthPassword::isUserAccountBinded() m_bindCheckTimer = new QTimer(this); connect(m_bindCheckTimer, &QTimer::timeout, this, [this] { qCWarning(DDE_SHELL) << "BindCheck retry!"; - if(isUserAccountBinded()) { + if (isUserAccountBinded()) { setResetPasswordMessageVisible(true); updateResetPasswordUI(); } @@ -865,11 +913,9 @@ void AuthPassword::updatePasswordTextMargins() { QMargins textMargins = m_lineEdit->lineEdit()->textMargins(); // 右边控件宽度+控件间距 - const int rightWidth = (m_passwordShowBtn->isVisible() ? m_passwordShowBtn->width() + 5 : 0) + - (m_authStateLabel->isVisible() ? m_authStateLabel->width() + 5 : 0) + - (m_passwordHintBtn->isVisible() ? m_passwordHintBtn->width() + 5: 0); + const int rightWidth = (m_passwordShowBtn->isVisible() ? m_passwordShowBtn->width() + 5 : 0) + (m_authStateLabel->isVisible() ? m_authStateLabel->width() + 5 : 0) + (m_passwordHintBtn->isVisible() ? m_passwordHintBtn->width() + 5 : 0); // 左侧控件宽度 - const int leftWidth = (m_capsLock->isVisible() ? m_capsLock->width() + 5: 0); + const int leftWidth = (m_capsLock->isVisible() ? m_capsLock->width() + 5 : 0); textMargins.setRight(rightWidth); #if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0) const int displayTextWidth = m_lineEdit->lineEdit()->fontMetrics().horizontalAdvance(m_lineEdit->lineEdit()->displayText()); @@ -978,3 +1024,8 @@ void AuthPassword::moveEvent(QMoveEvent *event) } QWidget::moveEvent(event); } + +void AuthPassword::onReadyToAuthChanged(bool ready) +{ + ready ? emit lineEditTextChanged(m_lineEdit->text()) : emit lineEditTextChanged(""); +} diff --git a/src/session-widgets/auth_password.h b/src/session-widgets/auth_password.h index e8e50000..cdfe01ef 100644 --- a/src/session-widgets/auth_password.h +++ b/src/session-widgets/auth_password.h @@ -66,6 +66,8 @@ class AuthPassword : public AuthModule void clearPasswrodErrorTip(bool isClear); void updatePasswrodErrorTipUi(); + AssistLoginWidget * assistLoginWidget() const { return m_assistLoginWidget; } + signals: void focusChanged(const bool); void lineEditTextChanged(const QString &); // 数据同步 @@ -78,10 +80,12 @@ class AuthPassword : public AuthModule void requestPluginAuthToken(const QString accout, const QString token); void requestUpdateBlurEffectGeometry(); void passwordErrorTipsClearChanged(const bool isClear); + void requestSendExtraInfo(const QString &info); public slots: void setResetPasswordMessageVisible(const bool isVisible, bool fromResetDialog = false); void updateResetPasswordUI(); + void onReadyToAuthChanged(bool ready); protected: bool eventFilter(QObject *watched, QEvent *event) override; diff --git a/src/session-widgets/auth_widget.cpp b/src/session-widgets/auth_widget.cpp index 0a8a336b..b2679f53 100644 --- a/src/session-widgets/auth_widget.cpp +++ b/src/session-widgets/auth_widget.cpp @@ -41,6 +41,7 @@ AuthWidget::AuthWidget(QWidget *parent) , m_faceAuth(nullptr) , m_irisAuth(nullptr) , m_passkeyAuth(nullptr) + , m_gestureAuth(nullptr) , m_customAuth(nullptr) , m_refreshTimer(new QTimer(this)) , m_authState(AuthCommon::AS_None) @@ -79,6 +80,7 @@ void AuthWidget::initUI() m_accountEdit->lineEdit()->setAlignment(Qt::AlignCenter); m_accountEdit->setClearButtonEnabled(false); m_accountEdit->setPlaceholderText(tr("Account")); + m_accountEdit->setEnableTableKeyEvent(true); // 账户名有效字符使用dsg配置 const QString accountExpression = DConfigHelper::instance()->getConfig("accountExpression", "[a-zA-Z0-9-_@]+$").toString(); @@ -294,6 +296,11 @@ void AuthWidget::setLimitsInfo(const QMap *limitsInfo) case AT_ActiveDirectory: case AT_Custom: break; + case AT_Pattern: + if (m_gestureAuth) { + m_gestureAuth->setLimitsInfo(limitsInfoTmp); + } + break; default: qCWarning(DDE_SHELL) << "Authentication type is wrong." << i.key(); break; @@ -546,6 +553,11 @@ int AuthWidget::getTopSpacing() const return qMax(15, deltaY); } +QWidget *AuthWidget::getAuthWidget() +{ + return this; +} + int AuthWidget::calcCurrentHeight(const int height) const { const int h = static_cast(((double) height / (double) BASE_SCREEN_HEIGHT) * topLevelWidget()->geometry().height()); diff --git a/src/session-widgets/auth_widget.h b/src/session-widgets/auth_widget.h index d2435e3e..47e543f3 100644 --- a/src/session-widgets/auth_widget.h +++ b/src/session-widgets/auth_widget.h @@ -130,6 +130,7 @@ class AuthWidget : public QWidget virtual void setAuthType(const AuthCommon::AuthFlags type); virtual void setAuthState(const AuthCommon::AuthType type, const AuthCommon::AuthState state, const QString &message); virtual int getTopSpacing() const; + virtual QWidget* getAuthWidget(); void setAccountErrorMsg(const QString &message); void syncPasswordResetPasswordVisibleChanged(const QVariant &value); @@ -193,6 +194,7 @@ protected Q_SLOTS: QPointer m_faceAuth; // 面容 QPointer m_irisAuth; // 虹膜 QPointer m_passkeyAuth; // 安全密钥 + QPointer m_gestureAuth; // 手势 QPointer m_customAuth; // 自定义认证 QString m_passwordHint; // 密码提示 diff --git a/src/session-widgets/lockcontent.cpp b/src/session-widgets/lockcontent.cpp index cd3b687e..c30a5557 100644 --- a/src/session-widgets/lockcontent.cpp +++ b/src/session-widgets/lockcontent.cpp @@ -18,6 +18,7 @@ #include "keyboardmonitor.h" #include "dconfig_helper.h" #include "constants.h" +#include "mfasequencecontrol.h" #include #include @@ -41,9 +42,9 @@ LockContent::LockContent(QWidget *parent) } -LockContent* LockContent::instance() +LockContent *LockContent::instance() { - static LockContent* lockContent = nullptr; + static LockContent *lockContent = nullptr; if (!lockContent) { lockContent = new LockContent(); } @@ -140,7 +141,9 @@ void LockContent::initUI() initUserListWidget(); - initFMAWidget(); + if (PluginManager::instance()->getFullManagedLoginPlugin() != nullptr) { + initFMAWidget(); + } } void LockContent::initConnections() @@ -171,7 +174,8 @@ void LockContent::initConnections() connect(&VirtualKBInstance::Instance(), &VirtualKBInstance::initFinished, this, [&] { m_virtualKB = VirtualKBInstance::Instance().virtualKBWidget(); m_controlWidget->setVirtualKBVisible(true); - }, Qt::QueuedConnection); + }, + Qt::QueuedConnection); VirtualKBInstance::Instance().init(); } else { VirtualKBInstance::Instance().stopVirtualKBProcess(); @@ -192,11 +196,17 @@ void LockContent::initConnections() isMFA ? initMFAWidget() : initSFAWidget(); // 当前中间窗口为空或者中间窗口就是验证窗口的时候显示验证窗口 if (!m_centerWidget || m_centerWidget == m_authWidget) - setCenterContent(m_authWidget, 0, Qt::AlignTop, calcTopSpacing(m_authWidget->getTopSpacing())); + setCenterContent(m_authWidget->getAuthWidget(), 0, Qt::AlignTop, calcTopSpacing(m_authWidget->getTopSpacing())); }); connect(m_wmInter, &com::deepin::wm::WorkspaceSwitched, this, &LockContent::currentWorkspaceChanged); connect(m_localServer, &QLocalServer::newConnection, this, &LockContent::onNewConnection); + connect(m_controlWidget, &ControlWidget::notifyKeyboardLayoutHidden, this, [this] { + if (!m_model->isUseWayland() && isVisible() && window()->windowHandle()) { + qCDebug(DDE_SHELL) << "Grab keyboard after keyboard layout hidden"; + window()->windowHandle()->setKeyboardGrabEnabled(true); + } + }); connect(m_model, &SessionBaseModel::showUserList, this, &LockContent::showUserList); connect(m_model, &SessionBaseModel::showLockScreen, this, &LockContent::showLockScreen); @@ -232,6 +242,16 @@ void LockContent::initMFAWidget() connect(m_mfaWidget, &MFAWidget::requestEndAuthentication, this, &LockContent::requestEndAuthentication); connect(m_mfaWidget, &MFAWidget::requestCheckAccount, this, &LockContent::requestCheckAccount); connect(m_mfaWidget, &MFAWidget::requestCheckSameNameAccount, this, &LockContent::requestCheckSameNameAccount); + + connect(&MFASequenceControl::instance(), &MFASequenceControl::currentUIAuthTypeChanged, this, [&](int authType) { + Q_UNUSED(authType) + // 仅在当前处于认证界面上刷新 + auto isMFA = m_model->getAuthProperty().MFAFlag; + qCInfo(DDE_SHELL) << "model mfa flag" << isMFA; + if (m_model->currentModeState() == SessionBaseModel::ModeStatus::PasswordMode && isMFA) { + pushPasswordFrame(); + } + }); } /** @@ -307,7 +327,8 @@ void LockContent::initUserListWidget() void LockContent::onCurrentUserChanged(std::shared_ptr user) { - if (user.get() == nullptr) return; // if dbus is async + if (user.get() == nullptr) + return; // if dbus is async //如果是锁屏就用系统语言,如果是登陆界面就用用户语言 auto locale = qApp->applicationName() == "dde-lock" ? QLocale::system().name() : user->locale(); @@ -340,11 +361,11 @@ void LockContent::pushPasswordFrame() } } - setCenterContent(m_authWidget, 0, Qt::AlignTop, calcTopSpacing(m_authWidget->getTopSpacing())); + setCenterContent(m_authWidget->getAuthWidget(), 0, Qt::AlignTop, calcTopSpacing(m_authWidget->getTopSpacing())); m_authWidget->syncResetPasswordUI(); - if (!m_fmaWidget->isPluginLoaded()) { + if (m_fmaWidget && !m_fmaWidget->isPluginLoaded()) { showDefaultFrame(); return; } @@ -367,7 +388,7 @@ void LockContent::pushUserFrame() return; } - if(m_model->isServerModel()) + if (m_model->isServerModel()) m_controlWidget->setUserSwitchEnable(false); m_userListWidget->updateLayout(width()); @@ -380,17 +401,17 @@ void LockContent::pushUserFrame() m_controlWidget->setUserSwitchEnable(m_isUserSwitchVisible); } - showDefaultFrame(); + showDefaultFrame(); - // fix: 解决用户界面多账户区域无焦点问题 - // showDefaultFrame() -> hideStackedWidgets() -> 会将焦点置为空 - // 导致默认用户无选中状态,多账户区域无键盘事件 - setFocus(); + // fix: 解决用户界面多账户区域无焦点问题 + // showDefaultFrame() -> hideStackedWidgets() -> 会将焦点置为空 + // 导致默认用户无选中状态,多账户区域无键盘事件 + setFocus(); } void LockContent::pushConfirmFrame() { - setCenterContent(m_authWidget, 0, Qt::AlignTop, calcTopSpacing(m_authWidget->getTopSpacing())); + setCenterContent(m_authWidget->getAuthWidget(), 0, Qt::AlignTop, calcTopSpacing(m_authWidget->getTopSpacing())); showDefaultFrame(); } @@ -462,7 +483,7 @@ void LockContent::onNewConnection() << "screenshot-ocr" << "screenshot-scroll" << "deepin-screen-recorder", - false, false); + false, false); } m_hasResetPasswordDialog = true; } @@ -475,9 +496,13 @@ void LockContent::onDisConnect() } // 这种情况下不必强制要求可以抓取到键盘,因为可能是网络弹窗抓取了键盘 tryGrabKeyboard(false); - enableSystemShortcut(QStringList() << "screenshot" << "screenshot-window" << "screenshot-delayed" - << "screenshot-ocr" << "screenshot-scroll" << "deepin-screen-recorder", true, false); - m_hasResetPasswordDialog = false; + enableSystemShortcut(QStringList() << "screenshot" + << "screenshot-window" + << "screenshot-delayed" + << "screenshot-ocr" + << "screenshot-scroll" + << "deepin-screen-recorder", + true, false); } void LockContent::onStatusChanged(SessionBaseModel::ModeStatus status) @@ -492,7 +517,7 @@ void LockContent::onStatusChanged(SessionBaseModel::ModeStatus status) PluginConfigMap::instance().requestRemoveConfig(PluginConfigMap::ConfigIndex::FullManagePlugin); } - if(m_model->isServerModel()) + if (m_model->isServerModel()) onUserListChanged(m_model->loginedUserList()); if (!m_isPANGUCpu && m_currentModeStatus == status) @@ -659,7 +684,7 @@ void LockContent::toggleVirtualKB() void LockContent::showModule(const QString &name, const bool callShowForce) { - PluginBase * plugin = PluginManager::instance()->findPlugin(name); + PluginBase *plugin = PluginManager::instance()->findPlugin(name); if (!plugin) { return; } @@ -689,12 +714,12 @@ bool LockContent::eventFilter(QObject *watched, QEvent *e) { // 点击插件弹窗以外区域,隐藏插件弹窗 if (m_popWin && m_currentTray && m_popWin->isVisible()) { - QWidget * w = qobject_cast(watched); + QWidget *w = qobject_cast(watched); if (!w) return false; - const bool isChild = m_currentTray->findChildren().contains(w); + const bool isChild = m_currentTray->findChildren().contains(w); if ((watched == m_currentTray || isChild) && e->type() == QEvent::Enter) { - Q_EMIT qobject_cast(m_currentTray)->requestHideTips(); + Q_EMIT qobject_cast(m_currentTray)->requestHideTips(); } else if (watched != m_currentTray && !isChild && e->type() == QEvent::MouseButtonRelease) { if (!m_popWin->geometry().contains(this->mapFromGlobal(QCursor::pos()))) { m_popWin->hide(); @@ -721,7 +746,7 @@ void LockContent::updateVirtualKBPosition() m_virtualKB->move(point); } -void LockContent::onUserListChanged(QList > list) +void LockContent::onUserListChanged(QList> list) { const bool allowShowUserSwitchButton = m_model->allowShowUserSwitchButton(); const bool alwaysShowUserSwitchButton = m_model->alwaysShowUserSwitchButton(); @@ -762,7 +787,7 @@ void LockContent::tryGrabKeyboard(bool exitIfFailed) } if (m_model->isUseWayland()) { - static QDBusInterface *kwinInter = new QDBusInterface("org.kde.KWin","/KWin","org.kde.KWin", QDBusConnection::sessionBus()); + static QDBusInterface *kwinInter = new QDBusInterface("org.kde.KWin", "/KWin", "org.kde.KWin", QDBusConnection::sessionBus()); if (!kwinInter || !kwinInter->isValid()) { qCWarning(DDE_SHELL) << "Kwin interface is invalid"; m_failures = 0; @@ -900,7 +925,7 @@ void LockContent::showTrayPopup(QWidget *trayWidget, QWidget *contentWidget, con window()->windowHandle()->setKeyboardGrabEnabled(true); } }); - + connect(this, &LockContent::parentChanged, this, [this] { if (m_popWin && m_popWin->isVisible()) { const QPoint &point = mapFromGlobal(m_currentTray->mapToGlobal(QPoint(m_currentTray->size().width() / 2, 0))); @@ -917,7 +942,7 @@ void LockContent::showTrayPopup(QWidget *trayWidget, QWidget *contentWidget, con } m_currentTray = trayWidget; - + // 隐藏后需要removeEventFilter,后期优化 for (auto child : this->findChildren()) { child->removeEventFilter(this); @@ -963,7 +988,7 @@ void LockContent::showUserList() { if (m_model->userlistVisible()) { m_model->setCurrentModeState(SessionBaseModel::ModeStatus::UserMode); - QTimer::singleShot(10, this, [ = ] { + QTimer::singleShot(10, this, [=] { m_model->setVisible(true); }); } else { diff --git a/src/session-widgets/mfa_widget.cpp b/src/session-widgets/mfa_widget.cpp index 8f204367..e99818d2 100644 --- a/src/session-widgets/mfa_widget.cpp +++ b/src/session-widgets/mfa_widget.cpp @@ -13,6 +13,10 @@ #include "keyboardmonitor.h" #include "sessionbasemodel.h" #include "useravatar.h" +#include "mfasequencecontrol.h" +#include "plugin_manager.h" +#include "auth_custom.h" +#include "signal_bridge.h" MFAWidget::MFAWidget(QWidget *parent) : AuthWidget(parent) @@ -54,6 +58,10 @@ void MFAWidget::initConnections() AuthWidget::initConnections(); connect(m_model, &SessionBaseModel::authTypeChanged, this, &MFAWidget::setAuthType); connect(m_model, &SessionBaseModel::authStateChanged, this, &MFAWidget::setAuthState); + // 关闭--已开启但无UI的认证(针对手势这种开启功能,但可能因配置错误、未安装插件,导致无法完成登录/解锁的场景) + connect(&MFASequenceControl::instance(), &MFASequenceControl::requestEndUnsupportedAuth, this, [&](int authType) { + Q_EMIT requestEndAuthentication(m_model->currentUser()->name(), AUTH_FLAGS_CAST(authType)); + }); } void MFAWidget::setModel(const SessionBaseModel *model) @@ -110,6 +118,14 @@ void MFAWidget::setAuthType(const AuthFlags type) m_passwordAuth->deleteLater(); m_passwordAuth = nullptr; } + /* 手势 */ + if (type & AT_Pattern) { + initGestureAuth(); + } else if (m_gestureAuth) { + m_gestureAuth->deleteLater(); + m_gestureAuth = nullptr; + } + /* 账户 */ if (type == AT_None) { if (m_model->currentUser()->isNoPasswordLogin()) { @@ -158,6 +174,8 @@ void MFAWidget::setAuthType(const AuthFlags type) } } setFocus(); + + MFASequenceControl::instance().setAuthType(type); } /** @@ -194,12 +212,21 @@ void MFAWidget::setAuthState(const AuthCommon::AuthType type, const AuthCommon:: m_irisAuth->setAuthState(state, message); } break; + case AT_Pattern: + if (m_gestureAuth) { + m_gestureAuth->setAuthState(state, message); + // 插件需要认证结果 + m_gestureAuth->notifyAuthState(type, state); + } + break; case AT_All: checkAuthResult(type, state); break; default: break; } + + MFASequenceControl::instance().onAuthStatusChanged(type, state, message); } void MFAWidget::autoUnlock() @@ -236,6 +263,13 @@ void MFAWidget::initPasswdAuth() if (text.isEmpty()) { return; } + if (m_passwordAuth->assistLoginWidget()) { + if (m_user) { + Q_EMIT SignalBridge::ref().requestSendExtraInfo(m_user->name(), AT_Password, m_passwordAuth->assistLoginWidget()->extraInfo()); + } else { + qCWarning(DDE_SHELL) << "Current user is null, can not send extra info"; + } + } m_passwordAuth->setAuthStateStyle(LOGIN_SPINNER); m_passwordAuth->setAnimationState(true); m_passwordAuth->setLineEditEnabled(false); @@ -356,6 +390,41 @@ void MFAWidget::initIrisAuth() }); } +/** + * @brief 由插件加载,无法加载时,结束该认证 + */ +void MFAWidget::initGestureAuth() +{ + if (m_gestureAuth) { + return; + } + + auto plugin = PluginManager::instance()->getLoginPlugin(AuthType::AT_Pattern); + if (!plugin) { + // 如果插件不存在或被析构,次序控制将结束已开启的认证 + MFASequenceControl::instance().insertIsolateAuthWidget(AT_Pattern, nullptr); + qCWarning(DDE_SHELL) << "Pattern plug not found, should stop pattern auth"; + return; + } + + m_gestureAuth = new AuthCustom(this, AuthType::AT_Pattern); + m_gestureAuth->setModule(plugin); + m_gestureAuth->setModel(m_model); + m_gestureAuth->initUi(); + m_gestureAuth->reset(); + m_gestureAuth->hide(); + + connect(m_gestureAuth, &AuthCustom::requestSendToken, this, [this](const QString &token) { + Q_EMIT sendTokenToAuth(m_model->currentUser()->name(), AuthType::AT_Pattern, token); + }); + + connect(m_gestureAuth, &AuthCustom::activeAuth, this, [this] { + Q_EMIT requestStartAuthentication(m_model->currentUser()->name(), AuthType::AT_Pattern); + }); + + MFASequenceControl::instance().insertIsolateAuthWidget(AT_Pattern, m_gestureAuth->getLoginPlugin()->content()); +} + /** * @brief 检查多因子认证结果 * @@ -417,8 +486,9 @@ void MFAWidget::updateFocusPosition() int MFAWidget::getTopSpacing() const { const int calcTopHeight = static_cast(topLevelWidget()->geometry().height() * AUTH_WIDGET_TOP_SPACING_PERCENT); - // 验证类型数量*高度 - const int authWidgetHeight = m_index * 47 + MIN_AUTH_WIDGET_HEIGHT; + auto sequenceAuthWid = MFASequenceControl::instance().currentAuthWidget(); + // 验证类型数量*高度,或者是独立ui的高度 + const int authWidgetHeight = sequenceAuthWid == nullptr ? m_index * 47 + MIN_AUTH_WIDGET_HEIGHT : sequenceAuthWid->height(); // 当分辨率过低时,如果仍然保持用户头像到屏幕顶端的距离为屏幕高度的35%,那么验证窗口整体时偏下的。 // 计算当验证窗口居中时距离屏幕顶端的高度,与屏幕高度*0.35对比取较小值。 @@ -431,6 +501,15 @@ int MFAWidget::getTopSpacing() const return qMax(15, deltaY); } +/** + * @brief 根据认证序列,提供不同来源的UI + */ +QWidget *MFAWidget::getAuthWidget() +{ + auto wid = MFASequenceControl::instance().currentAuthWidget(); + return wid == nullptr ? this : wid; +} + void MFAWidget::resizeEvent(QResizeEvent *event) { QTimer::singleShot(0, this, &MFAWidget::updateBlurEffectGeometry); diff --git a/src/session-widgets/mfa_widget.h b/src/session-widgets/mfa_widget.h index 91cca7d0..a216231d 100644 --- a/src/session-widgets/mfa_widget.h +++ b/src/session-widgets/mfa_widget.h @@ -22,6 +22,8 @@ class MFAWidget : public AuthWidget void autoUnlock(); int getTopSpacing() const override; + QWidget *getAuthWidget() override; + protected: void resizeEvent(QResizeEvent *event) override; @@ -34,6 +36,7 @@ class MFAWidget : public AuthWidget void initUKeyAuth(); void initFaceAuth(); void initIrisAuth(); + void initGestureAuth(); void checkAuthResult(const AuthCommon::AuthType type, const AuthCommon::AuthState state) override; diff --git a/src/session-widgets/mfasequencecontrol.cpp b/src/session-widgets/mfasequencecontrol.cpp new file mode 100644 index 00000000..24658430 --- /dev/null +++ b/src/session-widgets/mfasequencecontrol.cpp @@ -0,0 +1,248 @@ +#include "mfasequencecontrol.h" +#include "dconfig_helper.h" +#include "authcommon.h" + +#include + +#include +#include +#include +#include +#include + +const QString kConfigAttrName = "mfaSequence"; +const QString kConfigUserKey = "userType"; +const QString kConfigAuthTypeKey = "authSequence"; +const QString kDefaultUser = "default"; +const QString kADDomainUser = "adDomain"; +const QString kNativeUser = "native"; +const QString kAllUser = "all"; + +// 不处理某个类型的认证过程,只处理UI切换 +MFASequenceControl::MFASequenceControl(QObject *parent) + : QObject(parent) + , m_userType("") + , m_authType(-1) + , m_configUserType("") + , m_currentType(-1) + , m_validConfig(false) +{ + initConfig(); + DConfigHelper::instance()->bind(this, kConfigAttrName, &MFASequenceControl::onPropertyChanged); +} + +MFASequenceControl &MFASequenceControl::instance() +{ + static MFASequenceControl control; + return control; +} + +// 外部设置当前认证类型 +// 工行在此判断是否多因和是否满足配置场景 +void MFASequenceControl::setAuthType(int type) +{ + qCDebug(DDE_SHELL) << "set mfa auth type" << type << this; + + // 先重置到默认界面上 + m_currentType = AuthCommon::AT_None; + Q_EMIT currentUIAuthTypeChanged(m_currentType); + + if (type == AuthCommon::AT_None) { + return; + } + + m_authType = type; + updateAuthSequence(); +} + +// 注意,同类型不代表同用户,所以设置后也需要更新 +void MFASequenceControl::setUserType(User::UserType type) +{ + qCDebug(DDE_SHELL) << "set mfa user type" << type << this; + + static QMap userTypes = { + {User::UserType::Default, kDefaultUser}, + {User::UserType::Native, kNativeUser}, + {User::UserType::ADDomain, kADDomainUser}}; + + m_userType = userTypes.value(type, kDefaultUser); +} + +int MFASequenceControl::currentAuthType() +{ + return m_currentType; +} + +void MFASequenceControl::insertIsolateAuthWidget(int type, QWidget *authWidget) +{ + m_isolateWidgets[type] = authWidget; +} + +void MFASequenceControl::removeAuthWidget(int type) +{ + m_isolateWidgets.remove(type); +} + +// 设计如下:这个类仅被动响应由配置指定的认证序列 +// 比如配置为[1,256], 刚以MFAWidget开始认证,当被通知认证1成功时,推出256认证的UI +// 因此这个函数只会当开始指定认证时返回非空值 +QWidget *MFASequenceControl::currentAuthWidget() +{ + return m_isolateWidgets.value(m_currentType, nullptr); +} + +void MFASequenceControl::doNextAuth(int finishedAuth) +{ + if (!m_configAuthList.size()) { + return; + } + + auto currentIndex = m_configAuthList.indexOf(finishedAuth); + if (currentIndex == -1) { + // 无法处理,认证类型有异常 + return; + } + + if (currentIndex == m_configAuthList.size() - 1) { + qCDebug(DDE_SHELL) << "mfa auth sequence done, continue to other auth"; + + m_currentType = AuthCommon::AT_None; + + // 是否所有认证已结束 + auto allFinishedType = 0; + foreach (auto type, m_configAuthList) { + allFinishedType |= type; + } + + // 存在配置外的认证,切回默认认证UI + if (allFinishedType != m_authType) { + Q_EMIT currentUIAuthTypeChanged(AuthCommon::AT_None); + } + + return; + } + + // 通知UI显示下一个类型的认证界面 + m_currentType = m_configAuthList.value(currentIndex + 1); + qCDebug(DDE_SHELL) << "next auth type" << m_currentType; + + Q_EMIT currentUIAuthTypeChanged(m_currentType); +} + +void MFASequenceControl::initConfig() +{ + m_validConfig = false; + m_configAuthList.clear(); + m_configUserType = ""; + + auto obj = DConfigHelper::instance()->getConfig(kConfigAttrName, "").toJsonObject(); + toLocalConfig(obj); +} + +// json转内部配置 +void MFASequenceControl::toLocalConfig(const QJsonObject &obj) +{ + if (!obj.contains(kConfigUserKey) || !obj.contains(kConfigAuthTypeKey)) { + return; + } + + // 处理用户类型 + auto nameType = obj[kConfigUserKey].toString(); + if (nameType.isEmpty()) { + return; + } + + m_configUserType = nameType; + // 处理认证顺序 + auto authTypeList = obj[kConfigAuthTypeKey].toArray().toVariantList(); + // 多因必然类型比2多 + if (authTypeList.size() < 2) { + return; + } + + foreach (auto type, authTypeList) { + m_configAuthList.push_back(type.toInt()); + } + + // 注意仅在完成配置后才设置true值 + m_validConfig = true; +} + +// 如果配置类型在传入的类型中 +// 先完成配置认证,再执行其它认证 +void MFASequenceControl::updateAuthSequence() +{ + if (!m_validConfig) { + qCDebug(DDE_SHELL) << "config not valid"; + return; + } + + // FIXME 工行上不关注用户类型,DA仅给域用户配置 + if (!m_authType) { + qCDebug(DDE_SHELL) << "not expect value"; + return; + } + + if (m_configUserType != m_userType && m_configUserType != kAllUser) { + return; + } + + auto isolateAuthList = m_isolateWidgets.keys(); + bool isolateActive = false; + foreach (auto isolate, isolateAuthList) { + if (isolate & m_authType) { + if (m_isolateWidgets.value(isolate, nullptr) != nullptr) { + isolateActive = true; + } + } + } + + // 没有参与认证的独立界面,由于认证未提供,将在认证开始后关闭 + if (!isolateActive) { + return; + } + + // 先完成配置的认证顺序 + m_currentType = m_configAuthList.value(0); + + qCDebug(DDE_SHELL) << "start from type" << m_currentType << m_configAuthList; + Q_EMIT currentUIAuthTypeChanged(m_currentType); +} + +void MFASequenceControl::onAuthStatusChanged(int type, int status, const QString &message) +{ + //仅用于参数对齐 + Q_UNUSED(message) + + if (!m_configAuthList.size()) { + return; + } + + switch (status) { + case AuthCommon::AS_Success: + if (type == AuthCommon::AT_All) { + return; + } else { + doNextAuth(type); + } + break; + case AuthCommon::AS_Started: + // 多因认证开启了手势,但是并没有安装插件 + if (m_isolateWidgets.contains(type) && m_isolateWidgets.value(type, nullptr) == nullptr) { + Q_EMIT requestEndUnsupportedAuth(type); + } + } +} + +// 在下一次认证过程中生效 +void MFASequenceControl::onPropertyChanged(const QString &key, const QVariant &value, QObject *objPtr) +{ + auto obj = qobject_cast(objPtr); + if (!obj) + return; + + qCInfo(DDE_SHELL) << "DConfig property changed, key: " << key << ", value: " << value; + if (key == kConfigAttrName) { + obj->initConfig(); + } +} diff --git a/src/session-widgets/mfasequencecontrol.h b/src/session-widgets/mfasequencecontrol.h new file mode 100644 index 00000000..b3c85902 --- /dev/null +++ b/src/session-widgets/mfasequencecontrol.h @@ -0,0 +1,67 @@ +#ifndef MFASEQUENCECONTROL_H +#define MFASEQUENCECONTROL_H + +#include "userinfo.h" + +#include +#include + +class MFASequenceControl : public QObject +{ + Q_OBJECT +public: + static MFASequenceControl &instance(); + void setAuthType(int); + void setUserType(User::UserType); + + // 外部传入widget,如果为空也代表认证无效 + // 内置认证不参与 + void insertIsolateAuthWidget(int, QWidget*); + + int currentAuthType(); + QWidget *currentAuthWidget(); + + void doNextAuth(int); + +public Q_SLOTS: + void initConfig(); + // 响应认证状态变化 + void onAuthStatusChanged(int, int, const QString &); + // 响应配置变化 + static void onPropertyChanged(const QString &key, const QVariant &value, QObject *objPtr); + +Q_SIGNALS: + // 当某个认证成功时,通知UI转到下一个认证 + void currentUIAuthTypeChanged(int); + + // 某个认证的UI不存在,通知结束该认证 + void requestEndUnsupportedAuth(int); + +private: + explicit MFASequenceControl(QObject *parent = nullptr); + + // 从dconf初始化,greeter与lock各持一份,区别配置 + void toLocalConfig(const QJsonObject &); + void updateAuthSequence(); + void removeAuthWidget(int); + +private: + // 外部设置当前用户与开启的认证类型 + QString m_userType; + int m_authType; + + // 实际配置值 + QString m_configUserType; + QList m_configAuthList; + + // 当前处理中的认证 + int m_currentType; + + // 配置可靠性 + bool m_validConfig; + + // 外部已注册的提供UI的认证 + QMap> m_isolateWidgets; +}; + +#endif // MFASEQUENCECONTROL_H diff --git a/src/session-widgets/sessionbasemodel.cpp b/src/session-widgets/sessionbasemodel.cpp index 1dd53212..bf35fc82 100644 --- a/src/session-widgets/sessionbasemodel.cpp +++ b/src/session-widgets/sessionbasemodel.cpp @@ -5,6 +5,7 @@ #include "sessionbasemodel.h" #include "dconfig_helper.h" #include "dbus/dbusdisplaymanager.h" +#include "mfasequencecontrol.h" #include #include @@ -39,6 +40,7 @@ SessionBaseModel::SessionBaseModel(QObject *parent) , m_lightdmPamStarted(false) , m_authResult{AuthType::AT_None, AuthState::AS_None, ""} , m_enableShellBlackMode(DConfigHelper::instance()->getConfig("enableShellBlack", true).toBool()) + , m_enableShutdownBlackWidget(DConfigHelper::instance()->getConfig("enableShutdownBlackWidget", true).toBool()) , m_visibleShutdownWhenRebootOrShutdown(DConfigHelper::instance()->getConfig("visibleShutdownWhenRebootOrShutdown", true).toBool()) { #ifndef ENABLE_DSS_SNIPE @@ -120,6 +122,9 @@ void SessionBaseModel::setPowerAction(const PowerAction &powerAction) m_powerAction = powerAction; + if (m_enableShutdownBlackWidget && (powerAction == SessionBaseModel::PowerAction::RequireRestart || powerAction == SessionBaseModel::PowerAction::RequireShutdown)) + Q_EMIT shutdownkModeChanged(true); + emit onPowerActionChanged(powerAction); } @@ -240,6 +245,14 @@ void SessionBaseModel::setIsBlackMode(bool is_black) emit blackModeChanged(is_black); } +void SessionBaseModel::setShutdownMode(bool is_black) +{ + if (!m_enableShutdownBlackWidget) { + return; + } + Q_EMIT shutdownkModeChanged(is_black); +} + void SessionBaseModel::setIsHibernateModel(bool is_Hibernate) { if (m_isHibernateMode == is_Hibernate) @@ -272,6 +285,7 @@ void SessionBaseModel::setAuthType(const AuthFlags type) if (type == m_authProperty.AuthType && type != AT_None) { return; } + if (m_currentUser->type() == User::Default) { m_authProperty.AuthType = type; emit authTypeChanged(AT_None); @@ -660,6 +674,7 @@ void SessionBaseModel::updateAuthState(const AuthType type, const AuthState stat m_authResult.authState = state; m_authResult.authType = type; m_authResult.authMessage = message; + switch (m_authProperty.FrameworkState) { case Available: emit authStateChanged(type, state, message); @@ -710,4 +725,4 @@ void SessionBaseModel::setUserlistVisible(bool visible) void SessionBaseModel::setQuickLoginProcess(bool val) { m_isQuickLoginProcess = val; -} \ No newline at end of file +} diff --git a/src/session-widgets/sessionbasemodel.h b/src/session-widgets/sessionbasemodel.h index 31d26e48..9032e6ba 100644 --- a/src/session-widgets/sessionbasemodel.h +++ b/src/session-widgets/sessionbasemodel.h @@ -152,6 +152,8 @@ class SessionBaseModel : public QObject inline bool isBlackMode() const { return m_isBlackMode; } void setIsBlackMode(bool is_black); + void setShutdownMode(bool is_black); + inline bool isHibernateMode() const { return m_isHibernateMode; } void setIsHibernateModel(bool is_Hibernate); @@ -252,6 +254,7 @@ public slots: void userListLoginedChanged(QList> list); void activeAuthChanged(bool active); void blackModeChanged(bool is_black); + void shutdownkModeChanged(bool is_black); void HibernateModeChanged(bool is_hibernate); //休眠信号改变 void prepareForSleep(bool is_Sleep); //待机信号改变 void shutdownInhibit(const SessionBaseModel::PowerAction action, bool needConfirm); @@ -311,6 +314,7 @@ public slots: bool m_lightdmPamStarted; // 标志lightdmpam是否已经开启,主要用于greeter,lock不涉及lightdm AuthResult m_authResult; // 记录认证结果 bool m_enableShellBlackMode; + bool m_enableShutdownBlackWidget; bool m_visibleShutdownWhenRebootOrShutdown; bool m_isQuickLoginProcess=false;//标志当前界面展示是否为快速登录流程 }; diff --git a/src/session-widgets/sfa_widget.cpp b/src/session-widgets/sfa_widget.cpp index e2f8fdef..8af47b08 100644 --- a/src/session-widgets/sfa_widget.cpp +++ b/src/session-widgets/sfa_widget.cpp @@ -19,6 +19,7 @@ #include "useravatar.h" #include "plugin_manager.h" #include "login_plugin_util.h" +#include "signal_bridge.h" #include @@ -192,8 +193,17 @@ void SFAWidget::initCustomFactors(const AuthFlags type) auto plugins = filtrateAuthPlugins(PluginManager::instance()->getLoginPlugins()); qCInfo(DDE_SHELL) << "Login plugin size:" << plugins.size(); if (!m_model->terminalLocked() && !plugins.isEmpty() && (type & AT_Custom)) { - for (const auto plugin : plugins) - initCustomAuth(plugin); + for (const auto plugin : plugins) { + if (plugin) { + plugin->updateConfig(); + // 手势认证当前还未适配单因认证 + if (plugin->defaultAuthType() & AT_Pattern) { + continue; + } + + initCustomAuth(plugin); + } + } // 析构不使用的插件 for (const auto &auth : m_customAuths.values()) { @@ -463,10 +473,19 @@ void SFAWidget::initPasswdAuth() if (text.isEmpty()) { return; } + if (m_passwordAuth->assistLoginWidget()) { + if (m_user) { + Q_EMIT SignalBridge::ref().requestSendExtraInfo(m_user->name(), AT_Password, m_passwordAuth->assistLoginWidget()->extraInfo()); + } else { + qCWarning(DDE_SHELL) << "Current user is null, can not send extra info"; + } + } m_passwordAuth->setAuthStateStyle(LOGIN_SPINNER); m_passwordAuth->setAnimationState(true); m_passwordAuth->setLineEditEnabled(false); m_lockButton->setEnabled(false); + // OPTIMIZING: DA没有发AS_Verify,这里自己发一个,后续DA优化 + m_passwordAuth->setAuthState(AuthCommon::AS_Verify, QStringLiteral("Verifying...")); if (m_model->currentUser()->isLdapUser() && LPUtil::updateLoginType() == LPUtil::Type::CLT_MFA) @@ -1256,28 +1275,42 @@ void SFAWidget::initAccount() }); } -void SFAWidget::onRequestChangeAuth(const AuthType authType) +void SFAWidget::onRequestChangeAuth(AuthType authType) { qCInfo(DDE_SHELL) << "Request change auth, auth type: " << authType << ", choose auth button box is enabled: " << m_chooseAuthButtonBox->isEnabled() << ", current authentication type: " << m_currentAuthType; + + int authTypeTmp = authType; const auto customAuth = qobject_cast(sender()); if (customAuth && customAuth->customAuthType() != m_currentAuthType) { - qCInfo(DDE_SHELL) << "Custom auth dismath with current auth type"; - return; + // 输出一下日志表明是哪个插件请求切换的,目前遇到的场景:锁屏时开启了密码认证,云桌面需要切换单点登录插件认证,自动解锁。 + qCInfo(DDE_SHELL) << "Custom auth mismatch with current auth type, custom auth type: " << customAuth->customAuthType(); } - if (authType == m_currentAuthType) { + if (authTypeTmp == m_currentAuthType) { qCInfo(DDE_SHELL) << "Current auth type is same with request auth type"; return; } - if (authType != AuthCommon::AT_Password && !m_chooseAuthButtonBox->isEnabled()) { - qCWarning(DDE_SHELL) << "Authentication button box is disabled and authentication type is not password."; - return; + if (authTypeTmp != AuthCommon::AT_Password && !m_chooseAuthButtonBox->isEnabled()) { + qCInfo(DDE_SHELL) << "Authentication button box is disabled and authentication type is not password, automatically switch to password authentication"; + authTypeTmp = AT_Password; + } + + // 如果请求切换到AT_All 表明请求方无所谓切换到某种特定的类型,交由登录器来处理。 + // 登录器会切换到上次认证成功的类型。 + if (authTypeTmp == AuthCommon::AT_All) { + qCInfo(DDE_SHELL) << "Request authentication type is AT_All, let the session-shell to handle it"; + if (m_user) { + authTypeTmp = m_user->lastAuthType(); + } else { + qCWarning(DDE_SHELL) << "No user, use first auth type"; + authTypeTmp = m_authButtons.firstKey(); + } } - if (!m_authButtons.contains(authType)) { + if (!m_authButtons.contains(authTypeTmp)) { qCWarning(DDE_SHELL) << "Authentication buttons do not contain the type"; // 登录选项默认显示上一次认证方式,当不存在上一次认证,默认显示第一种认证方式 @@ -1285,7 +1318,7 @@ void SFAWidget::onRequestChangeAuth(const AuthType authType) const auto lastAuthType = m_user->lastAuthType(); qCInfo(DDE_SHELL) << "Last authtication type:" << lastAuthType; if (lastAuthType == AuthCommon::AT_Custom) { - int authType = m_authButtons.firstKey(); + authTypeTmp = m_authButtons.firstKey(); // 获取上次自定义认证的类型 const auto &lastCustomAuth = m_user->lastCustomAuth(); qCInfo(DDE_SHELL) << "Last custom auth:" << lastCustomAuth; @@ -1294,10 +1327,10 @@ void SFAWidget::onRequestChangeAuth(const AuthType authType) if (it != m_customAuths.end() && it.value() != nullptr) { // 排除重复使用了当前自定义认证类型的情况 if (!customAuth || customAuth->customAuthType() != it.value()->customAuthType()) - authType = it.value()->customAuthType(); + authTypeTmp = it.value()->customAuthType(); } } - button = m_chooseAuthButtonBox->button(authType); + button = m_chooseAuthButtonBox->button(authTypeTmp); } else if (m_authButtons.contains(lastAuthType)) { button = m_chooseAuthButtonBox->button(lastAuthType); } else { @@ -1313,7 +1346,7 @@ void SFAWidget::onRequestChangeAuth(const AuthType authType) return ; } - QAbstractButton *btn = m_chooseAuthButtonBox->button(authType); + QAbstractButton *btn = m_chooseAuthButtonBox->button(authTypeTmp); if (!btn) { qCWarning(DDE_SHELL) << "The button of authentication is null"; return; diff --git a/src/session-widgets/sfa_widget.h b/src/session-widgets/sfa_widget.h index 46cee644..1de2d6e6 100644 --- a/src/session-widgets/sfa_widget.h +++ b/src/session-widgets/sfa_widget.h @@ -43,7 +43,7 @@ class SFAWidget : public AuthWidget public slots: void onRetryButtonVisibleChanged(bool visible); - void onRequestChangeAuth(const AuthCommon::AuthType authType); + void onRequestChangeAuth(AuthCommon::AuthType authType); void onLightdmPamStartChanged(); protected: diff --git a/src/session-widgets/userpanel.cpp b/src/session-widgets/userpanel.cpp index 121b025a..a50b6426 100644 --- a/src/session-widgets/userpanel.cpp +++ b/src/session-widgets/userpanel.cpp @@ -9,16 +9,14 @@ #include -DWIDGET_USE_NAMESPACE - UserPanel::UserPanel(QWidget *parent) : ActionWidget(parent) , m_isSelected(false) , m_uid(UINT_MAX) , m_mainLayout(new QHBoxLayout(this)) , m_avatar(new UserAvatar(this)) - , m_displayNameLabel(new QLabel(this)) - , m_typeLabel(new QLabel(this)) + , m_displayNameLabel(new DLabel(this)) + , m_typeLabel(new DLabel(this)) { setObjectName(QStringLiteral("UserPanel")); setAccessibleName(QStringLiteral("UserPanel")); @@ -33,8 +31,12 @@ void UserPanel::initUI() setRadius(18); m_displayNameLabel->setAlignment(Qt::AlignLeft); DFontSizeManager::instance()->bind(m_displayNameLabel, DFontSizeManager::T5); + m_displayNameLabel->setElideMode(Qt::ElideRight); + m_typeLabel->setAlignment(Qt::AlignLeft); DFontSizeManager::instance()->bind(m_typeLabel, DFontSizeManager::T6); + m_typeLabel->setElideMode(Qt::ElideRight); + m_avatar->setAvatarSize(UserAvatarSize); m_mainLayout->setSpacing(12); m_mainLayout->setContentsMargins(10, 10, 10, 10); @@ -103,4 +105,4 @@ void UserPanel::setType(const QString &type) { m_typeLabel->setText(type); update(); -} \ No newline at end of file +} diff --git a/src/session-widgets/userpanel.h b/src/session-widgets/userpanel.h index 06db0689..964a5d16 100644 --- a/src/session-widgets/userpanel.h +++ b/src/session-widgets/userpanel.h @@ -7,6 +7,8 @@ #include "actionwidget.h" +#include + #include const int UserPanelWidth = 330; @@ -16,6 +18,7 @@ const int UserAvatarSize = 56; class QHBoxLayout; class UserAvatar; +DWIDGET_USE_NAMESPACE class UserPanel : public ActionWidget { Q_OBJECT @@ -46,8 +49,8 @@ class UserPanel : public ActionWidget QString m_fullName; QString m_name; QString m_type; - QLabel *m_displayNameLabel; - QLabel *m_typeLabel; + DLabel *m_displayNameLabel; + DLabel *m_typeLabel; }; #endif // USERPANEL_H diff --git a/src/session-widgets/userswiththesamename.cpp b/src/session-widgets/userswiththesamename.cpp index 403cf846..81e2620c 100644 --- a/src/session-widgets/userswiththesamename.cpp +++ b/src/session-widgets/userswiththesamename.cpp @@ -34,9 +34,9 @@ UsersWithTheSameName::UsersWithTheSameName(SessionBaseModel *model, QWidget *par void UsersWithTheSameName::initUI() { m_backButton->setText(tr("Return")); - const QPixmap &normalBackPixmap = DHiDPIHelper::loadNxPixmap(":/img/back-normal.svg").scaled(m_backButton->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation); + const QPixmap &normalBackPixmap = DHiDPIHelper::loadNxPixmap(":/img/back-normal.svg"); m_backButton->setNormalPixmap(normalBackPixmap); - const QPixmap &hoverBackPixmap = DHiDPIHelper::loadNxPixmap(":/img/back-hover.svg").scaled(m_backButton->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation); + const QPixmap &hoverBackPixmap = DHiDPIHelper::loadNxPixmap(":/img/back-hover.svg"); m_backButton->setHoverPixmap(hoverBackPixmap); m_backButton->setFixedSize(200, 64); DFontSizeManager::instance()->bind(m_backButton, DFontSizeManager::T4); @@ -114,7 +114,13 @@ bool UsersWithTheSameName::findUsers(const QString nativeUserName, const QString return false; } const QJsonObject &jsonObject = jsonDoc.object(); - const QString &fullName = jsonObject.value("gecos").toString(); + QString fullName; + // 检查fullname字段是否存在,如果存在则使用,否则使用name字段的值 + if (jsonObject.contains("fullname")) { + fullName = jsonObject.value("fullname").toString(); + } else { + fullName = jsonObject.value("name").toString(); + } const QString &name = jsonObject.value("name").toString(); const uint uid = jsonObject.value("uid").toInt(); diff --git a/src/widgets/dlineeditex.cpp b/src/widgets/dlineeditex.cpp index 58c83ec7..ec011265 100644 --- a/src/widgets/dlineeditex.cpp +++ b/src/widgets/dlineeditex.cpp @@ -10,6 +10,7 @@ #include #include #include +#include LoadSlider::LoadSlider(QWidget *parent) : QWidget(parent) @@ -37,6 +38,7 @@ DLineEditEx::DLineEditEx(QWidget *parent) : DLineEdit(parent) , m_loadSlider(new LoadSlider(this)) , m_animation(new QPropertyAnimation(m_loadSlider, "pos", m_loadSlider)) + , m_enableTableKeyEvent(false) { setObjectName(QStringLiteral("DLineEditEx")); setAccessibleName(QStringLiteral("DLineEditEx")); @@ -118,6 +120,14 @@ void DLineEditEx::stopAnimation() m_animation->stop(); } +void DLineEditEx::setEnableTableKeyEvent(bool enable) +{ + if (m_enableTableKeyEvent == enable) + return; + + m_enableTableKeyEvent = enable; +} + /** * @brief 重写 QLineEdit paintEvent 函数,实现当文本设置居中后,holderText 仍然显示的需求 * @@ -147,6 +157,16 @@ bool DLineEditEx::eventFilter(QObject *watched, QEvent *event) if ((watched == this || watched == this->lineEdit()) && event->type() == QEvent::InputMethodQuery) { return true; + } else if ((watched == this || watched == this->lineEdit()) + && event->type() == QEvent::KeyPress + && m_enableTableKeyEvent) { + QKeyEvent *keyEvent = static_cast(event); + if (keyEvent->key() == Qt::Key_Tab + && keyEvent->modifiers() == Qt::NoModifier + && !this->text().isEmpty()) { + emit this->returnPressed(); + return true; + } } return DLineEdit::eventFilter(watched, event); diff --git a/src/widgets/dlineeditex.h b/src/widgets/dlineeditex.h index 86ee2956..26c97c75 100644 --- a/src/widgets/dlineeditex.h +++ b/src/widgets/dlineeditex.h @@ -36,6 +36,7 @@ class DLineEditEx : public DLineEdit public slots: void startAnimation(); void stopAnimation(); + void setEnableTableKeyEvent(bool enable); protected: void paintEvent(QPaintEvent *event) override; @@ -48,6 +49,7 @@ public slots: private: LoadSlider *m_loadSlider; QPropertyAnimation *m_animation; + bool m_enableTableKeyEvent; }; #endif // DLINEEDITEX_H diff --git a/src/widgets/fullscreenbackground.cpp b/src/widgets/fullscreenbackground.cpp index fe0ac3e7..6fc81674 100644 --- a/src/widgets/fullscreenbackground.cpp +++ b/src/widgets/fullscreenbackground.cpp @@ -11,6 +11,7 @@ #include "dconfig_helper.h" #include +#include #include #include @@ -43,6 +44,7 @@ FullScreenBackground::FullScreenBackground(SessionBaseModel *model, QWidget *par , m_useSolidBackground(false) , m_blackWidget(new BlackWidget(this)) , m_resetGeometryTimer(new QTimer(this)) + , m_shutdownBlackWidget(nullptr) { #ifndef QT_DEBUG if (!m_model->isUseWayland()) { @@ -53,6 +55,8 @@ FullScreenBackground::FullScreenBackground(SessionBaseModel *model, QWidget *par setAttribute(Qt::WA_NativeWindow); // 创建窗口 handle // onScreenDisplay 低于override,高于tooltip,希望显示在锁屏上方的界面,均需要调整层级为onScreenDisplay或者override windowHandle()->setProperty("_d_dwayland_window-type", "onScreenDisplay"); + DPlatformWindowHandle handle(this, this); + handle.setWindowRadius(-1); } #endif frameList.append(this); @@ -71,6 +75,15 @@ FullScreenBackground::FullScreenBackground(SessionBaseModel *model, QWidget *par qCDebug(DDE_SHELL) << " setGeometry : " << m_geometryRect; setGeometry(m_geometryRect); }); + + connect(m_model, &SessionBaseModel::shutdownkModeChanged, this, [this] (bool value){ + if (!m_shutdownBlackWidget) { + m_shutdownBlackWidget = new ShutdownBlackWidget(this); + } + qCInfo(DDE_SHELL) << "FullScreenBackground size : " << size(); + m_shutdownBlackWidget->setFixedSize(this->size()); + m_shutdownBlackWidget->setBlackMode(value); + }); } FullScreenBackground::~FullScreenBackground() @@ -435,6 +448,8 @@ void FullScreenBackground::updateGeometry() } if (!m_screen.isNull()) { + if(m_model->isUseWayland()) + windowHandle()->setScreen(m_screen); setddeGeometry(m_screen->geometry()); qCInfo(DDE_SHELL) << "Update geometry, screen:" << m_screen diff --git a/src/widgets/fullscreenbackground.h b/src/widgets/fullscreenbackground.h index 8e1f4d1e..8b89989e 100644 --- a/src/widgets/fullscreenbackground.h +++ b/src/widgets/fullscreenbackground.h @@ -13,6 +13,7 @@ #include #include "abstractfullbackgroundinterface.h" +#include "shutdown_black_widget.h" Q_DECLARE_LOGGING_CATEGORY(DDE_SS) @@ -96,6 +97,7 @@ public slots: BlackWidget *m_blackWidget; QTimer *m_resetGeometryTimer; QRect m_geometryRect; + ShutdownBlackWidget *m_shutdownBlackWidget; }; #endif // FULLSCREENBACKGROUND_H diff --git a/src/widgets/shutdown_black_widget.cpp b/src/widgets/shutdown_black_widget.cpp new file mode 100644 index 00000000..b2d08d77 --- /dev/null +++ b/src/widgets/shutdown_black_widget.cpp @@ -0,0 +1,123 @@ +// SPDX-FileCopyrightText: 2020 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "shutdown_black_widget.h" +#include + +#include +#include +#include +#include +#include +#include + +bool onPreparingForShutdown() { + QDBusInterface iface( + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + QDBusConnection::systemBus() + ); + QVariant preparingForShutdown = iface.property("PreparingForShutdown"); + + if (preparingForShutdown.isValid()) { + bool isPreparing = preparingForShutdown.toBool(); + qDebug() << "Preparing for shutdown property value:" << isPreparing; + return isPreparing; + } else { + qWarning() << "Failed to retrieve preparing for shutdown property, the property is invalid"; + } + return false; +} + +void handleSIGTERM(int signal) { + qInfo() << "handleSIGTERM: " << signal; + + bool bShutdown = onPreparingForShutdown(); + qInfo() << "Whether preparing for shutdown: " << bShutdown; + if (bShutdown) { + QTimer::singleShot(2500, qApp, SLOT(quit())); + } else { + QTimer time; + time.start(1000); + QObject::connect(&time, &QTimer::timeout, [&] { + bool bShutdown = onPreparingForShutdown(); + qInfo() << "Whether preparing for shutdown: " << bShutdown; + if (bShutdown) { + time.stop(); + QTimer::singleShot(2000, qApp, SLOT(quit())); + return; + } else { + qInfo() << " Get org.freedesktop.login1.Manager PreparingForShutdown again."; + } + }); + } +} + +ShutdownBlackWidget::ShutdownBlackWidget(QWidget *parent) + : QWidget(parent) + , m_cursor(cursor()) +{ + signal(SIGTERM, handleSIGTERM); + setObjectName(QStringLiteral("ShutdownBlackWidget")); + setAccessibleName(QStringLiteral("ShutdownBlackWidget")); + setVisible(false); + setWindowFlags(Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint); + + QCursor cursor(Qt::BlankCursor); + this->setCursor(cursor); + + installEventFilter(this); +} + +void ShutdownBlackWidget::setBlackMode(const bool isBlackMode) +{ + qInfo() << "ShutdownBlackWidget setBlackMode : " << isBlackMode; + setVisible(isBlackMode); + if (isBlackMode) { + show(); + } +#ifndef QT_DEBUG + setCursor(isBlackMode ? Qt::BlankCursor : m_cursor); +#endif +} + +void ShutdownBlackWidget::setShutdownMode(const bool isBlackMode) +{ + setVisible(isBlackMode); +} + +void ShutdownBlackWidget::paintEvent(QPaintEvent *event) +{ + QPainter painter(this); + QRect rect(QPoint(0, 0), QSize(size() * devicePixelRatioF())); + painter.fillRect(rect, Qt::black); + + QWidget::paintEvent(event); +} + +void ShutdownBlackWidget::showEvent(QShowEvent *event) +{ + raise(); + grabMouse(); + grabKeyboard(); + QWidget::showEvent(event); +} + +bool ShutdownBlackWidget::eventFilter(QObject *watched, QEvent *event) +{ + if (event->type() == QEvent::MouseButtonPress || + event->type() == QEvent::MouseButtonRelease || + event->type() == QEvent::MouseButtonDblClick || + event->type() == QEvent::MouseMove || + event->type() == QEvent::KeyPress || + event->type() == QEvent::KeyRelease) { + qDebug() << "### All Mouse and Keyboard Event blocked!"; + + // 返回 true 表示事件已被处理,不再继续传递 + return true; + } + + return QWidget::eventFilter(watched, event); +} diff --git a/src/widgets/shutdown_black_widget.h b/src/widgets/shutdown_black_widget.h new file mode 100644 index 00000000..a200f386 --- /dev/null +++ b/src/widgets/shutdown_black_widget.h @@ -0,0 +1,27 @@ +#pragma once + +#include + +class ShutdownBlackWidget : public QWidget +{ + Q_OBJECT +public: + explicit ShutdownBlackWidget(QWidget *parent = nullptr); + + void setBlackMode(const bool isBlackMode); + +public Q_SLOTS: + void setShutdownMode(const bool isBlackMode = false); + +protected: + void paintEvent(QPaintEvent *event) override; + void showEvent(QShowEvent *event) override; + bool eventFilter(QObject *watched, QEvent *event) override; + +private: + QCursor m_cursor; +}; + +// SPDX-FileCopyrightText: 2020 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/widgets/shutdownwidget.cpp b/src/widgets/shutdownwidget.cpp index e25b41fd..873a3afe 100644 --- a/src/widgets/shutdownwidget.cpp +++ b/src/widgets/shutdownwidget.cpp @@ -615,7 +615,8 @@ void ShutdownWidget::recoveryLayout() void ShutdownWidget::onRequirePowerAction(SessionBaseModel::PowerAction powerAction, bool needConfirm) { - qCInfo(DDE_SHELL) << "Require power action: " << powerAction << ", need confirm: " << needConfirm; + qInfo() << "Require power action: " << powerAction << ", need confirm: " << needConfirm << ", m_model->appType(): " << m_model->appType(); + //锁屏或关机模式时,需要确认是否关机或检查是否有阻止关机 if (m_model->appType() == Lock) { switch (powerAction) { diff --git a/tests/dde-lock/CMakeLists.txt b/tests/dde-lock/CMakeLists.txt index b19f31c5..83dc4173 100644 --- a/tests/dde-lock/CMakeLists.txt +++ b/tests/dde-lock/CMakeLists.txt @@ -21,6 +21,7 @@ set(LOCK_TEST_SRCS ${PROJECT_SOURCE_DIR}/src/dde-lock/dbus/dbuslockfrontservice.cpp ${PROJECT_SOURCE_DIR}/src/dde-lock/dbus/dbusshutdownagent.cpp ${PROJECT_SOURCE_DIR}/src/dde-lock/dbus/dbusshutdownfrontservice.cpp + ${PROJECT_SOURCE_DIR}/src/global_util/signal_bridge.h ) add_executable(${BIN_NAME} diff --git a/tests/lightdm-deepin-greeter/CMakeLists.txt b/tests/lightdm-deepin-greeter/CMakeLists.txt index e4717cef..e5c2dd3b 100644 --- a/tests/lightdm-deepin-greeter/CMakeLists.txt +++ b/tests/lightdm-deepin-greeter/CMakeLists.txt @@ -20,6 +20,7 @@ set(GREETER_TEST_SRCS ${PROJECT_SOURCE_DIR}/src/lightdm-deepin-greeter/logincontent.cpp ${PROJECT_SOURCE_DIR}/src/lightdm-deepin-greeter/logintipswindow.cpp ${PROJECT_SOURCE_DIR}/src/lightdm-deepin-greeter/sessionwidget.cpp + ${PROJECT_SOURCE_DIR}/src/global_util/signal_bridge.h ) add_executable(${BIN_NAME}