From 6dcde637ffdac711c89da3480ebadcc3c698796b Mon Sep 17 00:00:00 2001 From: Calvin Lee Date: Thu, 6 May 2021 18:16:57 +0800 Subject: [PATCH 01/11] Add some Simplified Chinese translation --- .../assets/locale/locale.constant-zh_CN.json | 182 ++++++++++++++---- 1 file changed, 143 insertions(+), 39 deletions(-) diff --git a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json index b7a3078c3b..d436551d65 100644 --- a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json +++ b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json @@ -76,6 +76,7 @@ "general": "基本设置", "general-policy": "基本策略", "general-settings": "基本设置", + "home-settings": "首页设置", "mail-from": "邮件来自", "mail-from-required": "邮件发件人必填。", "max-failed-login-attempts": "登录失败之前的最大登录尝试次数", @@ -115,7 +116,7 @@ "customer-name-pattern": "客户名称模式", "default-dashboard-name": "默认仪表板名称", "delete-domain": "删除域", - "delete-domain-text": "注意,确认后域和所有 provider data 将不可用。", + "delete-domain-text": "请注意:确认后域和所有 provider data 将不可用。", "delete-domain-title": "确实要删除域 '{{domainName}}' 的设置吗?", "delete-provider": "删除 Provider", "delete-registration-text": "请注意:确认后 provider data 将不可用。", @@ -413,7 +414,7 @@ "assignedToCustomer": "分配客户", "copyId": "复制资产ID", "delete": "删除资产", - "delete-asset-text": "小心!确认后资产及其所有相关数据将不可恢复。", + "delete-asset-text": "请注意:确认后资产及其所有相关数据将不可恢复。", "delete-asset-title": "确定要删除资产 '{{assetName}}'?", "delete-assets": "删除资产", "delete-assets-action-title": "删除 { count, plural, 1 {# 个资产} other {# 个资产} }", @@ -589,10 +590,10 @@ "default-customer": "默认客户", "default-customer-required": "为了调试租户级别上的仪表板,需要默认客户。", "delete": "删除此客户", - "delete-customer-text": "小心!确认后,客户及其所有相关数据将不可恢复。", + "delete-customer-text": "请注意:确认后,客户及其所有相关数据将不可恢复。", "delete-customer-title": "您确定要删除客户'{{customerTitle}}'吗?", "delete-customers-action-title": "删除 { count, plural, 1 {# 个客户} other {# 个客户} }", - "delete-customers-text": "小心!确认后,所有选定的客户将被删除,所有相关数据将不可恢复。", + "delete-customers-text": "请注意:确认后,所有选定的客户将被删除,所有相关数据将不可恢复。", "delete-customers-title": "您确定要删除 { count, plural, 1 {# 个客户} other {# 个客户} }吗?", "description": "说明", "details": "详情", @@ -647,6 +648,7 @@ "background-color": "背景颜色", "background-image": "背景图片", "background-size-mode": "背景大小模式", + "cannot-upload-file": "无法上传文件", "close-toolbar": "关闭工具栏", "columns-count": "列数", "columns-count-required": "需要列数。", @@ -659,14 +661,15 @@ "dashboard-details": "仪表板详情", "dashboard-file": "仪表板文件", "dashboard-import-missing-aliases-title": "配置导入仪表板使用的别名", + "dashboard-logo-image": "仪表板 Logo 图片", "dashboard-required": "仪表板必填。", "dashboards": "仪表板库", "delete": "删除仪表板", - "delete-dashboard-text": "小心!确认后仪表板及其所有相关数据将不可恢复。", + "delete-dashboard-text": "请注意:确认后仪表板及其所有相关数据将不可恢复。", "delete-dashboard-title": "您确定要删除仪表板 '{{dashboardTitle}}'吗?", "delete-dashboards": "删除仪表板", "delete-dashboards-action-title": "删除 { count, plural, 1 {# 个仪表板} other {# 个仪表板} }", - "delete-dashboards-text": "小心!确认后所有选定的仪表板将被删除,所有相关数据将不可恢复。", + "delete-dashboards-text": "请注意:确认后所有选定的仪表板将被删除,所有相关数据将不可恢复。", "delete-dashboards-title": "确定要删除 { count, plural, 1 {# 个仪表板} other {# 个仪表板} }吗?", "delete-state": "删除仪表板状态", "delete-state-text": "确定要删除仪表板状态 '{{stateName}}' 吗?", @@ -674,6 +677,7 @@ "description": "说明", "details": "详情", "display-dashboard-export": "显示导出", + "display-dashboard-logo": "在仪表板全屏模式下显示 Logo", "display-dashboard-timewindow": "显示时间窗口", "display-dashboards-selection": "显示仪表板选项", "display-entities-selection": "显示实体选项", @@ -684,6 +688,8 @@ "export": "导出仪表板", "export-failed-error": "无法导出仪表板: {{error}}", "hide-details": "隐藏详情", + "home-dashboard": "首页仪表板", + "home-dashboard-hide-toolbar": "隐藏首页仪表板工具栏", "horizontal-margin": "水平边距", "horizontal-margin-required": "需要水平边距值。", "import": "导入仪表板", @@ -736,6 +742,7 @@ "select-state": "选择目标状态", "select-widget-subtitle": "可用的部件类型列表", "select-widget-title": "选择部件", + "select-widget-value": "{{title}}: 选择部件", "selected-dashboards": "已选择 { count, plural, 1 {# 个仪表盘} other {# 个仪表盘} }", "selected-states": "已选择 { count, plural, 1 {# 个仪表板状态} other {# 个仪表板状态} }", "set-background": "设置背景", @@ -803,6 +810,7 @@ }, "datasource": { "add-datasource-prompt": "请添加数据源", + "label": "标签", "name": "名称", "type": "数据源类型" }, @@ -843,6 +851,11 @@ "attributes-topic-filter": "Attributes topic filter", "attributes-topic-filter-required": "Attributes topic 筛选器必填。", "clear-alarm-rule": "清除报警规则", + "coap-device-payload-type": "CoAP 设备消息 Payload", + "coap-device-type": "CoAP 设备类型", + "coap-device-type-default": "默认", + "coap-device-type-efento": "Efento NB-IoT", + "coap-device-type-required": "CoAP 设备类型必填。", "condition": "条件", "condition-duration": "条件持续时间", "condition-duration-time-unit": "时间单位", @@ -867,17 +880,19 @@ "copyId": "复制设备配置 ID", "create-alarm-pattern": "创建 {{alarmType}} 报警", "create-alarm-rules": "创建报警规则", + "create-device-profile": "创建设备配置", "create-new-device-profile": "创建一个新的!", "default": "默认", "default-rule-chain": "默认规则链", "delete": "删除设备配置", - "delete-device-profile-text": "注意,确认后设备配置和所有相关数据将不可恢复。", + "delete-device-profile-text": "请注意:确认后设备配置和所有相关数据将不可恢复。", "delete-device-profile-title": "是否确实要删除设备配置 '{{deviceProfileName}}'?", "delete-device-profiles-text": "请注意:确认后,所有选定的设备配置将被删除,所有相关数据将不可恢复。", "delete-device-profiles-title": "确定要删除 { count, plural, 1 {# 个设备配置} other {# 个设备配置} }?", "description": "说明", "device-profile": "设备配置", "device-profile-details": "设备配置详情", + "device-profile-file": "设备配置文件", "device-profile-required": "设备配置必填", "device-profiles": "设备配置", "device-provisioning": "设备预配置", @@ -886,7 +901,11 @@ "edit-alarm-rule-condition": "编辑报警规则条件", "edit-schedule": "编辑报警日程表", "enter-alarm-rule-condition-prompt": "请添加报警规则条件", + "export": "导出设备配置", + "export-failed-error": "无法导出设备配置文件: {{error}}", "idCopiedMessage": "设备配置 ID 已复制到剪贴板", + "import": "导入设备配置", + "invalid-device-profile-file-error": "无法导入设备配置文件:无效的设备配置文件数据结构。", "mqtt-device-payload-type": "MQTT 设备 Payload", "mqtt-device-payload-type-json": "JSON", "mqtt-device-payload-type-proto": "Protobuf", @@ -920,6 +939,11 @@ "provision-strategy-created-new": "允许创建新设备", "provision-strategy-disabled": "禁用", "provision-strategy-required": "预配置策略必填。", + "rpc-request-proto-schema": "RPC 请求 proto schema", + "rpc-request-proto-schema-hint": "RPC 请求消息应始终包含字段:string method = 1; int32 requestId = 2; 和params = 3的任何数据类型。", + "rpc-request-proto-schema-required": "RPC 请求 proto schema 必填。", + "rpc-response-proto-schema": "RPC 响应 proto schema", + "rpc-response-proto-schema-required": "RPC 响应 proto schema 必填。", "rpc-response-topic-filter": "RPC响应 Topic 筛选器", "rpc-response-topic-filter-required": "RPC响应 Topic 筛选器必填。", "schedule": "启用规则:", @@ -957,6 +981,7 @@ "telemetry-topic-filter-required": "遥测数据 topic 筛选器必填。", "transport-configuration": "传输配置", "transport-type": "传输方式", + "transport-type-coap-hint": "启用高级 CoAP 传输设置", "transport-type-default": "默认", "transport-type-default-hint": "支持基本MQTT、HTTP和CoAP传输", "transport-type-lwm2m": "LWM2M", @@ -964,6 +989,7 @@ "transport-type-mqtt": "MQTT", "transport-type-mqtt-hint": "启用高级MQTT传输设置", "transport-type-required": "传输方式必填。", + "transport-type-snmp-hint": "指定 SNMP 传输配置", "type": "配置类型", "type-default": "默认", "type-required": "配置类型必填。" @@ -1000,11 +1026,11 @@ "credentials": "凭据", "credentials-type": "凭据类型", "delete": "删除设备", - "delete-device-text": "小心!确认后设备及其所有相关数据将不可恢复。", + "delete-device-text": "请注意:确认后设备及其所有相关数据将不可恢复。", "delete-device-title": "您确定要删除设备的{{deviceName}}吗?", "delete-devices": "删除设备", "delete-devices-action-title": "删除 {count,plural,1 {# 个设备} other {# 个设备} }", - "delete-devices-text": "小心!确认后所有选定的设备将被删除,所有相关数据将不可恢复。", + "delete-devices-text": "请注意:确认后所有选定的设备将被删除,所有相关数据将不可恢复。", "delete-devices-title": "确定要删除{count,plural,1 {# 个设备} other {# 个设备} } 吗?", "description": "说明", "details": "详情", @@ -1051,6 +1077,7 @@ "no-devices-text": "找不到设备", "no-key-matching": "'{{key}}' 没有找到。", "no-keys-found": "找不到密钥。", + "overwrite-activity-time": "覆盖已连接设备的活动时间", "password": "密码", "public": "公开", "remove-alias": "删除设备别名", @@ -1139,7 +1166,7 @@ "created-time": "创建时间", "date-limits": "日期限制", "delete": "删除实体视图", - "delete-entity-view-text": "小心!确认后实体视图及其所有相关数据将不可恢复。", + "delete-entity-view-text": "请注意:确认后实体视图及其所有相关数据将不可恢复。", "delete-entity-view-title": "确定要删除实体视图 '{{entityViewName}}'?", "delete-entity-views": "删除实体视图", "delete-entity-views-action-title": "删除 { count, plural, 1 {# 个实体视图} other {# 个实体视图} }", @@ -1235,6 +1262,7 @@ "duplicate-alias-error": "别名 '{{alias}}' 重复。
同一仪表板别名必须唯一。", "enter-entity-type": "输入实体类型", "entities": "实体", + "entities-count": "实体数量", "entity": "实体", "entity-alias": "实体别名", "entity-label": "实体标签", @@ -1334,12 +1362,15 @@ "body": "整体", "data": "数据", "data-type": "数据类型", + "entity-id": "实体ID", "error": "错误", "errors-occurred": "错误发生", "event": "事件", "event-time": "事件时间", "event-type": "事件类型", + "events-filter": "事件筛选器", "failed": "失败", + "has-error": "有错误", "message-id": "消息ID", "message-type": "消息类型", "messages-processed": "消息处理", @@ -1552,6 +1583,7 @@ "key-name-required": "键名必填。", "key-type": { "attribute": "属性", + "constant": "常量", "entity-field": "实体", "key-type": "键类型", "timeseries": "Timeseries" @@ -1603,6 +1635,51 @@ "value-type": "值类型" } }, + "firmware": { + "add": "添加固件", + "checksum": "校验和", + "checksum-algorithm": "校验和算法", + "checksum-copied-message": "固件校验和已复制到剪贴板", + "checksum-required": "校验和为必填项。", + "content-type": "Content type", + "copy-checksum": "复制校验和", + "copyId": "复制固件ID", + "delete": "删除固件", + "delete-firmware-text": "请注意:确认后固件将无法恢复。", + "delete-firmware-title": "确定要删除固件'{{firmwareTitle}}'?", + "delete-firmwares-action-title": "Delete { count, plural, 1 {# 个固件} other {# 个固件} }", + "delete-firmwares-text": "请注意:确认后所有选定的资源都将被删除。", + "delete-firmwares-title": "确定要删除固件 { count, plural, 1 {# 个固件} other {# 个固件} }?", + "description": "描述", + "download": "下载固件", + "drop-file": "拖拽固件文件或单击以选择要上传的文件。", + "empty": "固件为空", + "file-name": "文件名", + "file-size": "文件大小", + "file-size-bytes": "文件大小(字节)", + "firmware": "固件", + "firmware-details": "固件详情", + "firmware-required": "固件为必选项。", + "idCopiedMessage": "固件ID已复制到剪贴板", + "no-firmware-matching": "找不到与 '{{entity}}' 匹配的固件。", + "no-firmware-text": "没有找到固件", + "no-software-matching": "找不到与 '{{entity}}' 匹配的软件。", + "no-software-text": "没有找到软件", + "search": "查找固件", + "selected-firmware": "已选择{ count, plural, 1 {# 个固件} other {# 个固件}", + "software": "软件", + "software-required": "软件为必选项。", + "title": "标题", + "title-required": "标题为必填项。", + "type": "固件类型", + "types": { + "firmware": "固件", + "software": "软件" + }, + "version": "版本号", + "version-required": "版本号为必填项。", + "warning-after-save-no-edit": "保存固件后,将无法更改标题和版本号。" +}, "fullscreen": { "exit": "退出全屏", "expand": "展开到全屏", @@ -1907,6 +1984,30 @@ "to-relations": "向内的关联", "type": "类型" }, + "resource": { + "add": "添加资源", + "delete": "删除资源", + "delete-resource-text": "请注意:确认后资源将不可恢复。", + "delete-resource-title": "确定要删除资源 '{{resourceTitle}}' 吗?", + "delete-resources-action-title": "删除 { count, plural, 1 {# 个资源} other {# 个资源} }", + "delete-resources-text": "请注意:确认后所有选定的资源都将被删除。", + "delete-resources-title": "确定要删除 { count, plural, 1 {# 个资源} other {# 个资源} }?", + "drop-file": "拖拽资源文件或单击以选择要上传的文件。", + "empty": "资源为空", + "export": "导出资源", + "no-resource-matching": "找不到与 '{{widgetsBundle}}' 匹配的资源。", + "no-resource-text": "找不到资源", + "open-widgets-bundle": "打开部件库", + "resource": "资源", + "resource-library-details": "资源库详情", + "resource-type": "资源类型", + "resources-library": "资源库", + "search": "查找资源", + "selected-resources": "已选择{ count, plural, 1 {# 个资源} other {# 个资源} }", + "system": "系统", + "title": "标题", + "title-required": "标题是必填项。" +}, "rulechain": { "add": "添加规则链", "add-rulechain-text": "添加新的规则链", @@ -2098,10 +2199,10 @@ "admins": "管理员", "copyId": "复制租户ID", "delete": "删除租户", - "delete-tenant-text": "小心!确认后,租户和所有相关数据将不可恢复。", + "delete-tenant-text": "请注意:确认后,租户和所有相关数据将不可恢复。", "delete-tenant-title": "您确定要删除租户'{{tenantTitle}}'吗?", "delete-tenants-action-title": "删除 { count, plural, 1 {# 个租户} other {# 个租户} }", - "delete-tenants-text": "小心!确认后,所有选定的租户将被删除,所有相关数据将不可恢复。", + "delete-tenants-text": "请注意:确认后,所有选定的租户将被删除,所有相关数据将不可恢复。", "delete-tenants-title": "确定要删除 {count,plural,1 {# 个租户} other {# 个租户} } 吗?", "description": "说明", "details": "详情", @@ -2178,10 +2279,10 @@ "customer-users": "客户用户", "default-dashboard": "默认面板", "delete": "删除用户", - "delete-user-text": "小心!确认后,用户和所有相关数据将不可恢复。", + "delete-user-text": "请注意:确认后,用户和所有相关数据将不可恢复。", "delete-user-title": "您确定要删除用户 '{{userEmail}}' 吗?", "delete-users-action-title": "删除 { count, plural, 1 {# 个用户} other {# 个用户} }", - "delete-users-text": "小心!确认后,所有选定的用户将被删除,所有相关数据将不可恢复。", + "delete-users-text": "请注意:确认后,所有选定的用户将被删除,所有相关数据将不可恢复。", "delete-users-title": "确定要删除 { count, plural, 1 {# 个用户} other {# 个用户} } 吗?", "description": "说明", "details": "详情", @@ -2343,11 +2444,11 @@ "run": "运行部件", "save": "保存部件", "save-widget-type-as": "部件类型另存为", - "save-widget-type-as-text": "请输入新的部件标题或选择目标部件组", + "save-widget-type-as-text": "请输入新的部件标题或选择目标部件包", "saveAs": "部件另存为", "search-data": "查找数据", "select-widget-type": "选择窗口部件类型", - "select-widgets-bundle": "选择部件组", + "select-widgets-bundle": "选择部件包", "settings-schema": "设置模式", "static": "静态部件", "tidy": "Tidy", @@ -2358,7 +2459,7 @@ "type": "部件类型", "unable-to-save-widget-error": "无法保存部件!控件有错误!", "undo": "撤消部件更改", - "widget-bundle": "部件组", + "widget-bundle": "部件包", "widget-library": "部件库", "widget-saved": "部件已保存", "widget-template-load-failed-error": "无法加载部件模板!", @@ -2367,33 +2468,36 @@ "widget-type-not-found": "加载部件配置出错。
可能关联的部件已经删除了。" }, "widgets-bundle": { - "add": "添加部件组", - "add-widgets-bundle-text": "添加新的部件组", - "create-new-widgets-bundle": "创建新的部件组", + "add": "添加部件包", + "add-widgets-bundle-text": "添加新的部件包", + "create-new-widgets-bundle": "创建新的部件包", "current": "当前组", - "delete": "删除部件组", - "delete-widgets-bundle-text": "小心!确认后,部件组和所有相关数据将不可恢复。", - "delete-widgets-bundle-title": "您确定要删除部件组 '{{widgetsBundleTitle}}'吗?", - "delete-widgets-bundles-action-title": "删除 { count, plural, 1 {# 个部件组} other {# 个部件组} }", - "delete-widgets-bundles-text": "小心!确认后,所有选定的部件组将被删除,所有相关数据将不可恢复。", - "delete-widgets-bundles-title": "确定要删除 { count, plural, 1 {# 个部件组} other {# 个部件组} } 吗?", + "delete": "删除部件包", + "delete-widgets-bundle-text": "请注意:确认后,部件包和所有相关数据将不可恢复。", + "delete-widgets-bundle-title": "您确定要删除部件包 '{{widgetsBundleTitle}}'吗?", + "delete-widgets-bundles-action-title": "删除 { count, plural, 1 {# 个部件包} other {# 个部件包} }", + "delete-widgets-bundles-text": "请注意:确认后,所有选定的部件包将被删除,所有相关数据将不可恢复。", + "delete-widgets-bundles-title": "确定要删除 { count, plural, 1 {# 个部件包} other {# 个部件包} } 吗?", + "description": "描述", "details": "详情", - "empty": "部件组是空的", - "export": "导出部件组", - "export-failed-error": "无法导出部件组: {{error}}", - "import": "导入部件组", - "invalid-widgets-bundle-file-error": "无法导入部件组:无效的部件组数据结构。", - "no-widgets-bundles-matching": "没有找到与 '{{widgetsBundle}}' 匹配的部件组。", - "no-widgets-bundles-text": "找不到部件组", - "open-widgets-bundle": "打开部件组", - "search": "查找部件组", - "selected-widgets-bundles": "已选择 { count, plural, 1 {# 个部件组} other {# 个部件组} }", + "empty": "部件包是空的", + "export": "导出部件包", + "export-failed-error": "无法导出部件包: {{error}}", + "image-preview": "图片预览", + "import": "导入部件包", + "invalid-widgets-bundle-file-error": "无法导入部件包:无效的部件包数据结构。", + "loading-widgets-bundles": "加载部件包...", + "no-widgets-bundles-matching": "没有找到与 '{{widgetsBundle}}' 匹配的部件包。", + "no-widgets-bundles-text": "找不到部件包", + "open-widgets-bundle": "打开部件包", + "search": "查找部件包", + "selected-widgets-bundles": "已选择 { count, plural, 1 {# 个部件包} other {# 个部件包} }", "system": "系统", "title": "标题", "title-required": "标题必填。", - "widgets-bundle-details": "部件组详细信息", - "widgets-bundle-file": "部件组文件", - "widgets-bundle-required": "部件组必填。", + "widgets-bundle-details": "部件包详细信息", + "widgets-bundle-file": "部件包文件", + "widgets-bundle-required": "部件包必填。", "widgets-bundles": "部件包" }, "widgets": { From 33c65aaf1befa975a957f4f2eeef31b52e4a18c3 Mon Sep 17 00:00:00 2001 From: Jonathan Gross Date: Tue, 18 May 2021 10:45:08 +0200 Subject: [PATCH 02/11] fix: do not "uppercase" unit title of digital gauges MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit unit titles are automatically changed to uppercase. That is an issue when the unit is a greek letter. e.g. for the measure of a conductivity, the unit is µS/cm. Capitalizing it makes it MS/CM, which does not have the same meaning. Additionally, I don't understand the benefit of capitalizing this by default. --- .../modules/home/components/widget/lib/canvas-digital-gauge.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/canvas-digital-gauge.ts b/ui-ngx/src/app/modules/home/components/widget/lib/canvas-digital-gauge.ts index 00687e9b7c..898ceb3b81 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/canvas-digital-gauge.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/canvas-digital-gauge.ts @@ -772,7 +772,7 @@ function drawDigitalLabel(context: DigitalGaugeCanvasRenderingContext2D, options context.textAlign = 'center'; context.font = Drawings.font(options, 'Label', fontSizeFactor); context.lineWidth = 0; - drawText(context, options, 'Label', options.label.toUpperCase(), textX, textY); + drawText(context, options, 'Label', options.label, textX, textY); } function drawDigitalMinMax(context: DigitalGaugeCanvasRenderingContext2D, options: CanvasDigitalGaugeOptions) { From f6957f836c0bba4b5ec8da299f7ccd3e693a49c9 Mon Sep 17 00:00:00 2001 From: Chantsova Ekaterina Date: Tue, 8 Jun 2021 14:34:55 +0300 Subject: [PATCH 03/11] Fix entities count datasource label overwriting, configured label was never displayed --- ui-ngx/src/app/shared/models/query/query.models.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/ui-ngx/src/app/shared/models/query/query.models.ts b/ui-ngx/src/app/shared/models/query/query.models.ts index fd7fa68b5e..dbe114e18e 100644 --- a/ui-ngx/src/app/shared/models/query/query.models.ts +++ b/ui-ngx/src/app/shared/models/query/query.models.ts @@ -819,12 +819,14 @@ export function updateDatasourceFromEntityInfo(datasource: Datasource, entity: E datasource.entityId = entity.id; datasource.entityType = entity.entityType; if (datasource.type === DatasourceType.entity || datasource.type === DatasourceType.entityCount) { - datasource.entityName = entity.name; - datasource.entityLabel = entity.label; - datasource.name = entity.name; - datasource.entityDescription = entity.entityDescription; - datasource.entity.label = entity.label; - datasource.entity.name = entity.name; + if (datasource.type === DatasourceType.entity) { + datasource.entityName = entity.name; + datasource.entityLabel = entity.label; + datasource.name = entity.name; + datasource.entityDescription = entity.entityDescription; + datasource.entity.label = entity.label; + datasource.entity.name = entity.name; + } if (createFilter) { datasource.entityFilter = { type: AliasFilterType.singleEntity, From 8e4f8594f74a60889ec394556a452217476a6980 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 10 Jun 2021 11:50:11 +0300 Subject: [PATCH 04/11] Refactor OAuth2 settings. Add mobile application parameters: package and callback URL scheme --- .../main/data/upgrade/3.2.2/schema_update.sql | 61 +++ ...tomOAuth2AuthorizationRequestResolver.java | 30 +- .../server/controller/OAuth2Controller.java | 18 +- .../update/DefaultDataUpdateService.java | 26 +- .../oauth2/AbstractOAuth2ClientMapper.java | 12 +- .../auth/oauth2/BasicOAuth2ClientMapper.java | 8 +- .../auth/oauth2/CustomOAuth2ClientMapper.java | 8 +- .../auth/oauth2/GithubOAuth2ClientMapper.java | 8 +- .../auth/oauth2/OAuth2ClientMapper.java | 5 +- .../Oauth2AuthenticationFailureHandler.java | 16 +- .../Oauth2AuthenticationSuccessHandler.java | 27 +- .../auth/oauth2/TbOAuth2ParameterNames.java | 22 + .../server/dao/oauth2/OAuth2Service.java | 20 +- .../server/common/data/id/OAuth2DomainId.java | 33 ++ .../server/common/data/id/OAuth2MobileId.java | 33 ++ .../server/common/data/id/OAuth2ParamsId.java | 33 ++ .../common/data/id/OAuth2RegistrationId.java | 33 ++ .../OAuth2ClientRegistrationId.java | 4 +- .../OAuth2ClientRegistrationInfoId.java | 4 +- .../OAuth2ClientRegistrationTemplate.java | 2 - .../common/data/oauth2/OAuth2Domain.java | 42 ++ .../common/data/oauth2/OAuth2DomainInfo.java | 34 ++ .../server/common/data/oauth2/OAuth2Info.java | 31 ++ .../common/data/oauth2/OAuth2Mobile.java | 42 ++ .../common/data/oauth2/OAuth2MobileInfo.java | 34 ++ .../common/data/oauth2/OAuth2Params.java | 40 ++ .../common/data/oauth2/OAuth2ParamsInfo.java | 39 ++ .../data/oauth2/OAuth2Registration.java | 77 +++ ...onDto.java => OAuth2RegistrationInfo.java} | 3 +- .../deprecated/ClientRegistrationDto.java | 45 ++ .../oauth2/{ => deprecated}/DomainInfo.java | 4 +- .../ExtendedOAuth2ClientRegistrationInfo.java | 5 +- .../OAuth2ClientRegistration.java | 8 +- .../OAuth2ClientRegistrationInfo.java | 6 +- .../OAuth2ClientsDomainParams.java | 4 +- .../{ => deprecated}/OAuth2ClientsParams.java | 4 +- .../server/dao/model/ModelConstants.java | 15 +- .../dao/model/sql/OAuth2DomainEntity.java | 76 +++ .../dao/model/sql/OAuth2MobileEntity.java | 72 +++ .../dao/model/sql/OAuth2ParamsEntity.java | 65 +++ .../model/sql/OAuth2RegistrationEntity.java | 212 ++++++++ ...actOAuth2ClientRegistrationInfoEntity.java | 7 +- ...dedOAuth2ClientRegistrationInfoEntity.java | 5 +- .../OAuth2ClientRegistrationEntity.java | 9 +- .../OAuth2ClientRegistrationInfoEntity.java | 5 +- .../HybridClientRegistrationRepository.java | 32 +- .../server/dao/oauth2/OAuth2DomainDao.java | 28 + .../server/dao/oauth2/OAuth2MobileDao.java | 28 + .../server/dao/oauth2/OAuth2ParamsDao.java | 23 + .../dao/oauth2/OAuth2RegistrationDao.java | 33 ++ .../server/dao/oauth2/OAuth2ServiceImpl.java | 221 +++++++- .../server/dao/oauth2/OAuth2Utils.java | 144 +++++- .../OAuth2ClientRegistrationDao.java | 5 +- .../OAuth2ClientRegistrationInfoDao.java | 8 +- .../dao/sql/oauth2/JpaOAuth2DomainDao.java | 52 ++ .../dao/sql/oauth2/JpaOAuth2MobileDao.java | 52 ++ .../dao/sql/oauth2/JpaOAuth2ParamsDao.java | 47 ++ .../sql/oauth2/JpaOAuth2RegistrationDao.java | 62 +++ .../sql/oauth2/OAuth2DomainRepository.java | 29 ++ .../sql/oauth2/OAuth2MobileRepository.java | 28 + .../sql/oauth2/OAuth2ParamsRepository.java | 24 + .../oauth2/OAuth2RegistrationRepository.java | 52 ++ .../JpaOAuth2ClientRegistrationDao.java | 10 +- .../JpaOAuth2ClientRegistrationInfoDao.java | 11 +- ...Auth2ClientRegistrationInfoRepository.java | 9 +- .../OAuth2ClientRegistrationRepository.java | 5 +- .../resources/sql/schema-entities-hsql.sql | 75 ++- .../main/resources/sql/schema-entities.sql | 75 ++- .../dao/service/BaseOAuth2ServiceTest.java | 486 ++++++++++-------- .../resources/sql/hsql/drop-all-tables.sql | 6 +- .../resources/sql/psql/drop-all-tables.sql | 6 +- .../thingsboard/rest/client/RestClient.java | 24 +- ui-ngx/src/app/core/auth/auth.service.ts | 10 +- ui-ngx/src/app/core/http/oauth2.service.ts | 10 +- .../admin/oauth2-settings.component.html | 196 ++++--- .../admin/oauth2-settings.component.scss | 2 +- .../pages/admin/oauth2-settings.component.ts | 233 +++++---- ui-ngx/src/app/shared/models/oauth2.models.ts | 22 +- .../assets/locale/locale.constant-en_US.json | 11 +- 79 files changed, 2801 insertions(+), 545 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/TbOAuth2ParameterNames.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2DomainId.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2MobileId.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ParamsId.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2RegistrationId.java rename common/data/src/main/java/org/thingsboard/server/common/data/id/{ => deprecated}/OAuth2ClientRegistrationId.java (89%) rename common/data/src/main/java/org/thingsboard/server/common/data/id/{ => deprecated}/OAuth2ClientRegistrationInfoId.java (89%) create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Domain.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2DomainInfo.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Info.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Mobile.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2MobileInfo.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Params.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ParamsInfo.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Registration.java rename common/data/src/main/java/org/thingsboard/server/common/data/oauth2/{ClientRegistrationDto.java => OAuth2RegistrationInfo.java} (92%) create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/ClientRegistrationDto.java rename common/data/src/main/java/org/thingsboard/server/common/data/oauth2/{ => deprecated}/DomainInfo.java (85%) rename common/data/src/main/java/org/thingsboard/server/common/data/oauth2/{ => deprecated}/ExtendedOAuth2ClientRegistrationInfo.java (85%) rename common/data/src/main/java/org/thingsboard/server/common/data/oauth2/{ => deprecated}/OAuth2ClientRegistration.java (81%) rename common/data/src/main/java/org/thingsboard/server/common/data/oauth2/{ => deprecated}/OAuth2ClientRegistrationInfo.java (92%) rename common/data/src/main/java/org/thingsboard/server/common/data/oauth2/{ => deprecated}/OAuth2ClientsDomainParams.java (92%) rename common/data/src/main/java/org/thingsboard/server/common/data/oauth2/{ => deprecated}/OAuth2ClientsParams.java (92%) create mode 100644 dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2DomainEntity.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2MobileEntity.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ParamsEntity.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2RegistrationEntity.java rename dao/src/main/java/org/thingsboard/server/dao/model/sql/{ => deprecated}/AbstractOAuth2ClientRegistrationInfoEntity.java (97%) rename dao/src/main/java/org/thingsboard/server/dao/model/sql/{ => deprecated}/ExtendedOAuth2ClientRegistrationInfoEntity.java (91%) rename dao/src/main/java/org/thingsboard/server/dao/model/sql/{ => deprecated}/OAuth2ClientRegistrationEntity.java (89%) rename dao/src/main/java/org/thingsboard/server/dao/model/sql/{ => deprecated}/OAuth2ClientRegistrationInfoEntity.java (91%) create mode 100644 dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2DomainDao.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2MobileDao.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ParamsDao.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2RegistrationDao.java rename dao/src/main/java/org/thingsboard/server/dao/oauth2/{ => deprecated}/OAuth2ClientRegistrationDao.java (83%) rename dao/src/main/java/org/thingsboard/server/dao/oauth2/{ => deprecated}/OAuth2ClientRegistrationInfoDao.java (81%) create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2DomainDao.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2MobileDao.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ParamsDao.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2RegistrationDao.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2DomainRepository.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2MobileRepository.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ParamsRepository.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2RegistrationRepository.java rename dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/{ => deprecated}/JpaOAuth2ClientRegistrationDao.java (82%) rename dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/{ => deprecated}/JpaOAuth2ClientRegistrationInfoDao.java (85%) rename dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/{ => deprecated}/OAuth2ClientRegistrationInfoRepository.java (81%) rename dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/{ => deprecated}/OAuth2ClientRegistrationRepository.java (83%) diff --git a/application/src/main/data/upgrade/3.2.2/schema_update.sql b/application/src/main/data/upgrade/3.2.2/schema_update.sql index 5647c301cf..9e94caf1af 100644 --- a/application/src/main/data/upgrade/3.2.2/schema_update.sql +++ b/application/src/main/data/upgrade/3.2.2/schema_update.sql @@ -79,6 +79,67 @@ CREATE TABLE IF NOT EXISTS ota_package ( CONSTRAINT ota_package_tenant_title_version_unq_key UNIQUE (tenant_id, title, version) ); +CREATE TABLE IF NOT EXISTS oauth2_params ( + id uuid NOT NULL CONSTRAINT oauth2_params_pkey PRIMARY KEY, + enabled boolean, + tenant_id uuid, + created_time bigint NOT NULL +); + +CREATE TABLE IF NOT EXISTS oauth2_registration ( + id uuid NOT NULL CONSTRAINT oauth2_registration_pkey PRIMARY KEY, + oauth2_params_id uuid NOT NULL, + created_time bigint NOT NULL, + additional_info varchar, + client_id varchar(255), + client_secret varchar(255), + authorization_uri varchar(255), + token_uri varchar(255), + scope varchar(255), + user_info_uri varchar(255), + user_name_attribute_name varchar(255), + jwk_set_uri varchar(255), + client_authentication_method varchar(255), + login_button_label varchar(255), + login_button_icon varchar(255), + allow_user_creation boolean, + activate_user boolean, + type varchar(31), + basic_email_attribute_key varchar(31), + basic_first_name_attribute_key varchar(31), + basic_last_name_attribute_key varchar(31), + basic_tenant_name_strategy varchar(31), + basic_tenant_name_pattern varchar(255), + basic_customer_name_pattern varchar(255), + basic_default_dashboard_name varchar(255), + basic_always_full_screen boolean, + custom_url varchar(255), + custom_username varchar(255), + custom_password varchar(255), + custom_send_token boolean, + CONSTRAINT fk_registration_oauth2_params FOREIGN KEY (oauth2_params_id) REFERENCES oauth2_params(id) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS oauth2_domain ( + id uuid NOT NULL CONSTRAINT oauth2_domain_pkey PRIMARY KEY, + oauth2_params_id uuid NOT NULL, + created_time bigint NOT NULL, + domain_name varchar(255), + domain_scheme varchar(31), + CONSTRAINT fk_domain_oauth2_params FOREIGN KEY (oauth2_params_id) REFERENCES oauth2_params(id) ON DELETE CASCADE, + CONSTRAINT oauth2_domain_unq_key UNIQUE (oauth2_params_id, domain_name, domain_scheme) +); + +CREATE TABLE IF NOT EXISTS oauth2_mobile ( + id uuid NOT NULL CONSTRAINT oauth2_mobile_pkey PRIMARY KEY, + oauth2_params_id uuid NOT NULL, + created_time bigint NOT NULL, + pkg_name varchar(255), + callback_url_scheme varchar(255), + CONSTRAINT fk_mobile_oauth2_params FOREIGN KEY (oauth2_params_id) REFERENCES oauth2_params(id) ON DELETE CASCADE, + CONSTRAINT oauth2_mobile_unq_key UNIQUE (oauth2_params_id, pkg_name) +); + ALTER TABLE dashboard ADD COLUMN IF NOT EXISTS image varchar(1000000); diff --git a/application/src/main/java/org/thingsboard/server/config/CustomOAuth2AuthorizationRequestResolver.java b/application/src/main/java/org/thingsboard/server/config/CustomOAuth2AuthorizationRequestResolver.java index bbcb7d656a..bc93253a73 100644 --- a/application/src/main/java/org/thingsboard/server/config/CustomOAuth2AuthorizationRequestResolver.java +++ b/application/src/main/java/org/thingsboard/server/config/CustomOAuth2AuthorizationRequestResolver.java @@ -37,6 +37,8 @@ import org.springframework.util.StringUtils; import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; import org.thingsboard.server.dao.oauth2.OAuth2Configuration; +import org.thingsboard.server.dao.oauth2.OAuth2Service; +import org.thingsboard.server.service.security.auth.oauth2.TbOAuth2ParameterNames; import org.thingsboard.server.utils.MiscUtils; import javax.servlet.http.HttpServletRequest; @@ -46,12 +48,13 @@ import java.security.NoSuchAlgorithmException; import java.util.Base64; import java.util.HashMap; import java.util.Map; +import java.util.UUID; @Service @Slf4j public class CustomOAuth2AuthorizationRequestResolver implements OAuth2AuthorizationRequestResolver { - public static final String DEFAULT_AUTHORIZATION_REQUEST_BASE_URI = "/oauth2/authorization"; - public static final String DEFAULT_LOGIN_PROCESSING_URI = "/login/oauth2/code/"; + private static final String DEFAULT_AUTHORIZATION_REQUEST_BASE_URI = "/oauth2/authorization"; + private static final String DEFAULT_LOGIN_PROCESSING_URI = "/login/oauth2/code/"; private static final String REGISTRATION_ID_URI_VARIABLE_NAME = "registrationId"; private static final char PATH_DELIMITER = '/'; @@ -63,6 +66,9 @@ public class CustomOAuth2AuthorizationRequestResolver implements OAuth2Authoriza @Autowired private ClientRegistrationRepository clientRegistrationRepository; + @Autowired + private OAuth2Service oAuth2Service; + @Autowired(required = false) private OAuth2Configuration oauth2Configuration; @@ -71,7 +77,8 @@ public class CustomOAuth2AuthorizationRequestResolver implements OAuth2Authoriza public OAuth2AuthorizationRequest resolve(HttpServletRequest request) { String registrationId = this.resolveRegistrationId(request); String redirectUriAction = getAction(request, "login"); - return resolve(request, registrationId, redirectUriAction); + String appPackage = getAppPackage(request); + return resolve(request, registrationId, redirectUriAction, appPackage); } @Override @@ -80,7 +87,8 @@ public class CustomOAuth2AuthorizationRequestResolver implements OAuth2Authoriza return null; } String redirectUriAction = getAction(request, "authorize"); - return resolve(request, registrationId, redirectUriAction); + String appPackage = getAppPackage(request); + return resolve(request, registrationId, redirectUriAction, appPackage); } private String getAction(HttpServletRequest request, String defaultAction) { @@ -91,8 +99,12 @@ public class CustomOAuth2AuthorizationRequestResolver implements OAuth2Authoriza return action; } + private String getAppPackage(HttpServletRequest request) { + return request.getParameter("pkg"); + } + @SuppressWarnings("deprecation") - private OAuth2AuthorizationRequest resolve(HttpServletRequest request, String registrationId, String redirectUriAction) { + private OAuth2AuthorizationRequest resolve(HttpServletRequest request, String registrationId, String redirectUriAction, String appPackage) { if (registrationId == null) { return null; } @@ -104,6 +116,14 @@ public class CustomOAuth2AuthorizationRequestResolver implements OAuth2Authoriza Map attributes = new HashMap<>(); attributes.put(OAuth2ParameterNames.REGISTRATION_ID, clientRegistration.getRegistrationId()); + if (!StringUtils.isEmpty(appPackage)) { + String callbackUrlScheme = this.oAuth2Service.findCallbackUrlScheme(UUID.fromString(registrationId), appPackage); + if (StringUtils.isEmpty(callbackUrlScheme)) { + throw new IllegalArgumentException("Invalid package: " + appPackage + ". No package info found for Client Registration."); + } else { + attributes.put(TbOAuth2ParameterNames.CALLBACK_URL_SCHEME, callbackUrlScheme); + } + } OAuth2AuthorizationRequest.Builder builder; if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(clientRegistration.getAuthorizationGrantType())) { diff --git a/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java b/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java index 6591a1577a..349c6de672 100644 --- a/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java +++ b/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java @@ -22,12 +22,13 @@ import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; -import org.thingsboard.server.common.data.oauth2.OAuth2ClientsParams; +import org.thingsboard.server.common.data.oauth2.OAuth2Info; import org.thingsboard.server.dao.oauth2.OAuth2Configuration; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.permission.Operation; @@ -49,7 +50,8 @@ public class OAuth2Controller extends BaseController { @RequestMapping(value = "/noauth/oauth2Clients", method = RequestMethod.POST) @ResponseBody - public List getOAuth2Clients(HttpServletRequest request) throws ThingsboardException { + public List getOAuth2Clients(HttpServletRequest request, + @RequestParam(required = false) String pkgName) throws ThingsboardException { try { if (log.isDebugEnabled()) { log.debug("Executing getOAuth2Clients: [{}][{}][{}]", request.getScheme(), request.getServerName(), request.getServerPort()); @@ -59,7 +61,7 @@ public class OAuth2Controller extends BaseController { log.debug("Header: {} {}", header, request.getHeader(header)); } } - return oAuth2Service.getOAuth2Clients(MiscUtils.getScheme(request), MiscUtils.getDomainNameAndPort(request)); + return oAuth2Service.getOAuth2Clients(MiscUtils.getScheme(request), MiscUtils.getDomainNameAndPort(request), pkgName); } catch (Exception e) { throw handleException(e); } @@ -68,10 +70,10 @@ public class OAuth2Controller extends BaseController { @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") @RequestMapping(value = "/oauth2/config", method = RequestMethod.GET, produces = "application/json") @ResponseBody - public OAuth2ClientsParams getCurrentOAuth2Params() throws ThingsboardException { + public OAuth2Info getCurrentOAuth2Info() throws ThingsboardException { try { accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_INFO, Operation.READ); - return oAuth2Service.findOAuth2Params(); + return oAuth2Service.findOAuth2Info(); } catch (Exception e) { throw handleException(e); } @@ -80,11 +82,11 @@ public class OAuth2Controller extends BaseController { @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") @RequestMapping(value = "/oauth2/config", method = RequestMethod.POST) @ResponseStatus(value = HttpStatus.OK) - public OAuth2ClientsParams saveOAuth2Params(@RequestBody OAuth2ClientsParams oauth2Params) throws ThingsboardException { + public OAuth2Info saveOAuth2Info(@RequestBody OAuth2Info oauth2Info) throws ThingsboardException { try { accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_INFO, Operation.WRITE); - oAuth2Service.saveOAuth2Params(oauth2Params); - return oAuth2Service.findOAuth2Params(); + oAuth2Service.saveOAuth2Info(oauth2Info); + return oAuth2Service.findOAuth2Info(); } catch (Exception e) { throw handleException(e); } diff --git a/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java b/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java index b91c46417c..8c27417f90 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java @@ -23,6 +23,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.rule.engine.profile.TbDeviceProfileNode; import org.thingsboard.rule.engine.profile.TbDeviceProfileNodeConfiguration; import org.thingsboard.server.common.data.EntityView; @@ -35,6 +36,8 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery; import org.thingsboard.server.common.data.kv.ReadTsKvQuery; import org.thingsboard.server.common.data.kv.TsKvEntry; +import org.thingsboard.server.common.data.oauth2.OAuth2Info; +import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientsParams; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.TimePageLink; @@ -45,10 +48,11 @@ import org.thingsboard.server.dao.alarm.AlarmDao; import org.thingsboard.server.dao.alarm.AlarmService; import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.dao.entityview.EntityViewService; +import org.thingsboard.server.dao.oauth2.OAuth2Service; +import org.thingsboard.server.dao.oauth2.OAuth2Utils; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.dao.timeseries.TimeseriesService; -import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.service.install.InstallScripts; import java.util.ArrayList; @@ -88,6 +92,9 @@ public class DefaultDataUpdateService implements DataUpdateService { @Autowired private AlarmDao alarmDao; + @Autowired + private OAuth2Service oAuth2Service; + @Override public void updateData(String fromVersion) throws Exception { switch (fromVersion) { @@ -107,6 +114,7 @@ public class DefaultDataUpdateService implements DataUpdateService { log.info("Updating data from version 3.2.2 to 3.3.0 ..."); tenantsDefaultEdgeRuleChainUpdater.updateEntities(null); tenantsAlarmsCustomerUpdater.updateEntities(null); + updateOAuth2Params(); break; default: throw new RuntimeException("Unable to update data, unsupported fromVersion: " + fromVersion); @@ -362,4 +370,20 @@ public class DefaultDataUpdateService implements DataUpdateService { } } + private void updateOAuth2Params() { + try { + OAuth2ClientsParams oauth2ClientsParams = oAuth2Service.findOAuth2Params(); + if (!oauth2ClientsParams.getDomainsParams().isEmpty()) { + log.info("Updating OAuth2 parameters ..."); + OAuth2Info oAuth2Info = OAuth2Utils.clientParamsToOAuth2Info(oauth2ClientsParams); + oAuth2Service.saveOAuth2Info(oAuth2Info); + oAuth2Service.saveOAuth2Params(new OAuth2ClientsParams(false, Collections.emptyList())); + log.info("Successfully updated OAuth2 parameters!"); + } + } + catch (Exception e) { + log.error("Failed to update OAuth2 parameters", e); + } + } + } diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java index 4f38cf7a23..3ff3534806 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java @@ -33,8 +33,8 @@ import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.IdBased; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; +import org.thingsboard.server.common.data.oauth2.OAuth2Registration; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; @@ -93,9 +93,9 @@ public abstract class AbstractOAuth2ClientMapper { private final Lock userCreationLock = new ReentrantLock(); - protected SecurityUser getOrCreateSecurityUserFromOAuth2User(OAuth2User oauth2User, OAuth2ClientRegistrationInfo clientRegistration) { + protected SecurityUser getOrCreateSecurityUserFromOAuth2User(OAuth2User oauth2User, OAuth2Registration registration) { - OAuth2MapperConfig config = clientRegistration.getMapperConfig(); + OAuth2MapperConfig config = registration.getMapperConfig(); UserPrincipal principal = new UserPrincipal(UserPrincipal.Type.USER_NAME, oauth2User.getEmail()); @@ -139,9 +139,9 @@ public abstract class AbstractOAuth2ClientMapper { } } - if (clientRegistration.getAdditionalInfo() != null && - clientRegistration.getAdditionalInfo().has("providerName")) { - additionalInfo.put("authProviderName", clientRegistration.getAdditionalInfo().get("providerName").asText()); + if (registration.getAdditionalInfo() != null && + registration.getAdditionalInfo().has("providerName")) { + additionalInfo.put("authProviderName", registration.getAdditionalInfo().get("providerName").asText()); } user.setAdditionalInfo(additionalInfo); diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicOAuth2ClientMapper.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicOAuth2ClientMapper.java index 940bf8ad0a..f5172b64e7 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicOAuth2ClientMapper.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicOAuth2ClientMapper.java @@ -18,8 +18,8 @@ package org.thingsboard.server.service.security.auth.oauth2; import lombok.extern.slf4j.Slf4j; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; import org.springframework.stereotype.Service; -import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; +import org.thingsboard.server.common.data.oauth2.OAuth2Registration; import org.thingsboard.server.dao.oauth2.OAuth2User; import org.thingsboard.server.service.security.model.SecurityUser; @@ -30,12 +30,12 @@ import java.util.Map; public class BasicOAuth2ClientMapper extends AbstractOAuth2ClientMapper implements OAuth2ClientMapper { @Override - public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2ClientRegistrationInfo clientRegistration) { - OAuth2MapperConfig config = clientRegistration.getMapperConfig(); + public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2Registration registration) { + OAuth2MapperConfig config = registration.getMapperConfig(); Map attributes = token.getPrincipal().getAttributes(); String email = BasicMapperUtils.getStringAttributeByKey(attributes, config.getBasic().getEmailAttributeKey()); OAuth2User oauth2User = BasicMapperUtils.getOAuth2User(email, attributes, config); - return getOrCreateSecurityUserFromOAuth2User(oauth2User, clientRegistration); + return getOrCreateSecurityUserFromOAuth2User(oauth2User, registration); } } diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CustomOAuth2ClientMapper.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CustomOAuth2ClientMapper.java index cb08bc9f96..65ebb384de 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CustomOAuth2ClientMapper.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CustomOAuth2ClientMapper.java @@ -23,9 +23,9 @@ import org.springframework.security.oauth2.client.authentication.OAuth2Authentic import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import org.springframework.web.client.RestTemplate; -import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; import org.thingsboard.server.common.data.oauth2.OAuth2CustomMapperConfig; import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; +import org.thingsboard.server.common.data.oauth2.OAuth2Registration; import org.thingsboard.server.dao.oauth2.OAuth2User; import org.thingsboard.server.service.security.model.SecurityUser; @@ -39,10 +39,10 @@ public class CustomOAuth2ClientMapper extends AbstractOAuth2ClientMapper impleme private RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder(); @Override - public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2ClientRegistrationInfo clientRegistration) { - OAuth2MapperConfig config = clientRegistration.getMapperConfig(); + public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2Registration registration) { + OAuth2MapperConfig config = registration.getMapperConfig(); OAuth2User oauth2User = getOAuth2User(token, providerAccessToken, config.getCustom()); - return getOrCreateSecurityUserFromOAuth2User(oauth2User, clientRegistration); + return getOrCreateSecurityUserFromOAuth2User(oauth2User, registration); } private synchronized OAuth2User getOAuth2User(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2CustomMapperConfig custom) { diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/GithubOAuth2ClientMapper.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/GithubOAuth2ClientMapper.java index 8e41c4a747..d6260fe482 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/GithubOAuth2ClientMapper.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/GithubOAuth2ClientMapper.java @@ -23,8 +23,8 @@ import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; -import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; +import org.thingsboard.server.common.data.oauth2.OAuth2Registration; import org.thingsboard.server.dao.oauth2.OAuth2Configuration; import org.thingsboard.server.dao.oauth2.OAuth2User; import org.thingsboard.server.service.security.model.SecurityUser; @@ -46,13 +46,13 @@ public class GithubOAuth2ClientMapper extends AbstractOAuth2ClientMapper impleme private OAuth2Configuration oAuth2Configuration; @Override - public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2ClientRegistrationInfo clientRegistration) { - OAuth2MapperConfig config = clientRegistration.getMapperConfig(); + public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2Registration registration) { + OAuth2MapperConfig config = registration.getMapperConfig(); Map githubMapperConfig = oAuth2Configuration.getGithubMapper(); String email = getEmail(githubMapperConfig.get(EMAIL_URL_KEY), providerAccessToken); Map attributes = token.getPrincipal().getAttributes(); OAuth2User oAuth2User = BasicMapperUtils.getOAuth2User(email, attributes, config); - return getOrCreateSecurityUserFromOAuth2User(oAuth2User, clientRegistration); + return getOrCreateSecurityUserFromOAuth2User(oAuth2User, registration); } private synchronized String getEmail(String emailUrl, String oauth2Token) { diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/OAuth2ClientMapper.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/OAuth2ClientMapper.java index 965b34d8b0..280418d066 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/OAuth2ClientMapper.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/OAuth2ClientMapper.java @@ -16,9 +16,10 @@ package org.thingsboard.server.service.security.auth.oauth2; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; -import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; +import org.thingsboard.server.common.data.oauth2.OAuth2Registration; +import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientRegistrationInfo; import org.thingsboard.server.service.security.model.SecurityUser; public interface OAuth2ClientMapper { - SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2ClientRegistrationInfo clientRegistration); + SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2Registration registration); } diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationFailureHandler.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationFailureHandler.java index b6345f1618..0e413b4f22 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationFailureHandler.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationFailureHandler.java @@ -18,8 +18,10 @@ package org.thingsboard.server.service.security.auth.oauth2; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.security.core.AuthenticationException; +import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest; import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; @@ -51,9 +53,19 @@ public class Oauth2AuthenticationFailureHandler extends SimpleUrlAuthenticationF public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { - String baseUrl = this.systemSecurityService.getBaseUrl(TenantId.SYS_TENANT_ID, new CustomerId(EntityId.NULL_UUID), request); + String baseUrl; + String errorPrefix; + OAuth2AuthorizationRequest authorizationRequest = httpCookieOAuth2AuthorizationRequestRepository.loadAuthorizationRequest(request); + String callbackUrlScheme = authorizationRequest.getAttribute(TbOAuth2ParameterNames.CALLBACK_URL_SCHEME); + if (!StringUtils.isEmpty(callbackUrlScheme)) { + baseUrl = callbackUrlScheme + ":"; + errorPrefix = "/?error="; + } else { + baseUrl = this.systemSecurityService.getBaseUrl(TenantId.SYS_TENANT_ID, new CustomerId(EntityId.NULL_UUID), request); + errorPrefix = "/login?loginError="; + } httpCookieOAuth2AuthorizationRequestRepository.removeAuthorizationRequestCookies(request, response); - getRedirectStrategy().sendRedirect(request, response, baseUrl + "/login?loginError=" + + getRedirectStrategy().sendRedirect(request, response, baseUrl + errorPrefix + URLEncoder.encode(exception.getMessage(), StandardCharsets.UTF_8.toString())); } } diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java index 72a6c65f7c..227d733ebd 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java @@ -20,12 +20,14 @@ import org.springframework.security.core.Authentication; import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; +import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest; import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; +import org.thingsboard.server.common.data.oauth2.OAuth2Registration; import org.thingsboard.server.common.data.security.model.JwtToken; import org.thingsboard.server.dao.oauth2.OAuth2Service; import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRepository; @@ -72,17 +74,24 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException { - String baseUrl = this.systemSecurityService.getBaseUrl(TenantId.SYS_TENANT_ID, new CustomerId(EntityId.NULL_UUID), request); + OAuth2AuthorizationRequest authorizationRequest = httpCookieOAuth2AuthorizationRequestRepository.loadAuthorizationRequest(request); + String callbackUrlScheme = authorizationRequest.getAttribute(TbOAuth2ParameterNames.CALLBACK_URL_SCHEME); + String baseUrl; + if (!StringUtils.isEmpty(callbackUrlScheme)) { + baseUrl = callbackUrlScheme + ":"; + } else { + baseUrl = this.systemSecurityService.getBaseUrl(TenantId.SYS_TENANT_ID, new CustomerId(EntityId.NULL_UUID), request); + } try { OAuth2AuthenticationToken token = (OAuth2AuthenticationToken) authentication; - OAuth2ClientRegistrationInfo clientRegistration = oAuth2Service.findClientRegistrationInfo(UUID.fromString(token.getAuthorizedClientRegistrationId())); + OAuth2Registration registration = oAuth2Service.findRegistration(UUID.fromString(token.getAuthorizedClientRegistrationId())); OAuth2AuthorizedClient oAuth2AuthorizedClient = oAuth2AuthorizedClientService.loadAuthorizedClient( token.getAuthorizedClientRegistrationId(), token.getPrincipal().getName()); - OAuth2ClientMapper mapper = oauth2ClientMapperProvider.getOAuth2ClientMapperByType(clientRegistration.getMapperConfig().getType()); + OAuth2ClientMapper mapper = oauth2ClientMapperProvider.getOAuth2ClientMapperByType(registration.getMapperConfig().getType()); SecurityUser securityUser = mapper.getOrCreateUserByClientPrincipal(token, oAuth2AuthorizedClient.getAccessToken().getTokenValue(), - clientRegistration); + registration); JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser); JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser); @@ -91,7 +100,13 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS getRedirectStrategy().sendRedirect(request, response, baseUrl + "/?accessToken=" + accessToken.getToken() + "&refreshToken=" + refreshToken.getToken()); } catch (Exception e) { clearAuthenticationAttributes(request, response); - getRedirectStrategy().sendRedirect(request, response, baseUrl + "/login?loginError=" + + String errorPrefix; + if (!StringUtils.isEmpty(callbackUrlScheme)) { + errorPrefix = "/?error="; + } else { + errorPrefix = "/login?loginError="; + } + getRedirectStrategy().sendRedirect(request, response, baseUrl + errorPrefix + URLEncoder.encode(e.getMessage(), StandardCharsets.UTF_8.toString())); } } diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/TbOAuth2ParameterNames.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/TbOAuth2ParameterNames.java new file mode 100644 index 0000000000..aa5b32f055 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/TbOAuth2ParameterNames.java @@ -0,0 +1,22 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.security.auth.oauth2; + +public interface TbOAuth2ParameterNames { + + String CALLBACK_URL_SCHEME = "callback_url_scheme"; + +} diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java index 3f9411d0aa..158c3911b4 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java @@ -16,20 +16,30 @@ package org.thingsboard.server.dao.oauth2; import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; -import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; -import org.thingsboard.server.common.data.oauth2.OAuth2ClientsParams; +import org.thingsboard.server.common.data.oauth2.OAuth2Info; +import org.thingsboard.server.common.data.oauth2.OAuth2Registration; +import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientRegistrationInfo; +import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientsParams; import java.util.List; import java.util.UUID; public interface OAuth2Service { - List getOAuth2Clients(String domainScheme, String domainName); + List getOAuth2Clients(String domainScheme, String domainName, String pkgName); + @Deprecated void saveOAuth2Params(OAuth2ClientsParams oauth2Params); + @Deprecated OAuth2ClientsParams findOAuth2Params(); - OAuth2ClientRegistrationInfo findClientRegistrationInfo(UUID id); + void saveOAuth2Info(OAuth2Info oauth2Info); - List findAllClientRegistrationInfos(); + OAuth2Info findOAuth2Info(); + + OAuth2Registration findRegistration(UUID id); + + List findAllRegistrations(); + + String findCallbackUrlScheme(UUID registrationId, String pkgName); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2DomainId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2DomainId.java new file mode 100644 index 0000000000..220232f16d --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2DomainId.java @@ -0,0 +1,33 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.id; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.UUID; + +public class OAuth2DomainId extends UUIDBased { + + @JsonCreator + public OAuth2DomainId(@JsonProperty("id") UUID id) { + super(id); + } + + public static OAuth2DomainId fromString(String oauth2DomainId) { + return new OAuth2DomainId(UUID.fromString(oauth2DomainId)); + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2MobileId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2MobileId.java new file mode 100644 index 0000000000..934d1f72a6 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2MobileId.java @@ -0,0 +1,33 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.id; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.UUID; + +public class OAuth2MobileId extends UUIDBased { + + @JsonCreator + public OAuth2MobileId(@JsonProperty("id") UUID id) { + super(id); + } + + public static OAuth2MobileId fromString(String oauth2MobileId) { + return new OAuth2MobileId(UUID.fromString(oauth2MobileId)); + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ParamsId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ParamsId.java new file mode 100644 index 0000000000..3aa0c5bb3d --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ParamsId.java @@ -0,0 +1,33 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.id; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.UUID; + +public class OAuth2ParamsId extends UUIDBased { + + @JsonCreator + public OAuth2ParamsId(@JsonProperty("id") UUID id) { + super(id); + } + + public static OAuth2ParamsId fromString(String oauth2ParamsId) { + return new OAuth2ParamsId(UUID.fromString(oauth2ParamsId)); + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2RegistrationId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2RegistrationId.java new file mode 100644 index 0000000000..0019ef1a04 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2RegistrationId.java @@ -0,0 +1,33 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.id; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.UUID; + +public class OAuth2RegistrationId extends UUIDBased { + + @JsonCreator + public OAuth2RegistrationId(@JsonProperty("id") UUID id) { + super(id); + } + + public static OAuth2RegistrationId fromString(String oauth2RegistrationId) { + return new OAuth2RegistrationId(UUID.fromString(oauth2RegistrationId)); + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ClientRegistrationId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/deprecated/OAuth2ClientRegistrationId.java similarity index 89% rename from common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ClientRegistrationId.java rename to common/data/src/main/java/org/thingsboard/server/common/data/id/deprecated/OAuth2ClientRegistrationId.java index 88d4245dc7..616fc3b29c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ClientRegistrationId.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/deprecated/OAuth2ClientRegistrationId.java @@ -13,13 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.data.id; +package org.thingsboard.server.common.data.id.deprecated; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import org.thingsboard.server.common.data.id.UUIDBased; import java.util.UUID; +@Deprecated public class OAuth2ClientRegistrationId extends UUIDBased { @JsonCreator diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ClientRegistrationInfoId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/deprecated/OAuth2ClientRegistrationInfoId.java similarity index 89% rename from common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ClientRegistrationInfoId.java rename to common/data/src/main/java/org/thingsboard/server/common/data/id/deprecated/OAuth2ClientRegistrationInfoId.java index 2e34eaf5ac..6ba959318c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ClientRegistrationInfoId.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/deprecated/OAuth2ClientRegistrationInfoId.java @@ -13,13 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.data.id; +package org.thingsboard.server.common.data.id.deprecated; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import org.thingsboard.server.common.data.id.UUIDBased; import java.util.UUID; +@Deprecated public class OAuth2ClientRegistrationInfoId extends UUIDBased { @JsonCreator diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistrationTemplate.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistrationTemplate.java index f75fb2aaa7..b46f23484f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistrationTemplate.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistrationTemplate.java @@ -20,10 +20,8 @@ import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.ToString; import org.thingsboard.server.common.data.HasName; -import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationTemplateId; -import org.thingsboard.server.common.data.id.TenantId; import java.util.List; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Domain.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Domain.java new file mode 100644 index 0000000000..0dee447023 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Domain.java @@ -0,0 +1,42 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.oauth2; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; +import org.thingsboard.server.common.data.BaseData; +import org.thingsboard.server.common.data.id.OAuth2DomainId; +import org.thingsboard.server.common.data.id.OAuth2ParamsId; + +@EqualsAndHashCode(callSuper = true) +@Data +@ToString +@NoArgsConstructor +public class OAuth2Domain extends BaseData { + + private OAuth2ParamsId oauth2ParamsId; + private String domainName; + private SchemeType domainScheme; + + public OAuth2Domain(OAuth2Domain domain) { + super(domain); + this.oauth2ParamsId = domain.oauth2ParamsId; + this.domainName = domain.domainName; + this.domainScheme = domain.domainScheme; + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2DomainInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2DomainInfo.java new file mode 100644 index 0000000000..9d9bd939da --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2DomainInfo.java @@ -0,0 +1,34 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.oauth2; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; + +@EqualsAndHashCode +@Data +@ToString +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class OAuth2DomainInfo { + private SchemeType scheme; + private String name; +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Info.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Info.java new file mode 100644 index 0000000000..72f4b06161 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Info.java @@ -0,0 +1,31 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.oauth2; + +import lombok.*; + +import java.util.List; + +@EqualsAndHashCode +@Data +@ToString +@Builder(toBuilder = true) +@NoArgsConstructor +@AllArgsConstructor +public class OAuth2Info { + private boolean enabled; + private List oauth2ParamsInfos; +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Mobile.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Mobile.java new file mode 100644 index 0000000000..b3249a1ed5 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Mobile.java @@ -0,0 +1,42 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.oauth2; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; +import org.thingsboard.server.common.data.BaseData; +import org.thingsboard.server.common.data.id.OAuth2MobileId; +import org.thingsboard.server.common.data.id.OAuth2ParamsId; + +@EqualsAndHashCode(callSuper = true) +@Data +@ToString +@NoArgsConstructor +public class OAuth2Mobile extends BaseData { + + private OAuth2ParamsId oauth2ParamsId; + private String pkgName; + private String callbackUrlScheme; + + public OAuth2Mobile(OAuth2Mobile mobile) { + super(mobile); + this.oauth2ParamsId = mobile.oauth2ParamsId; + this.pkgName = mobile.pkgName; + this.callbackUrlScheme = mobile.callbackUrlScheme; + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2MobileInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2MobileInfo.java new file mode 100644 index 0000000000..18fbd49dcd --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2MobileInfo.java @@ -0,0 +1,34 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.oauth2; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; + +@EqualsAndHashCode +@Data +@ToString +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class OAuth2MobileInfo { + private String pkgName; + private String callbackUrlScheme; +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Params.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Params.java new file mode 100644 index 0000000000..570fe6cb20 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Params.java @@ -0,0 +1,40 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.oauth2; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; +import org.thingsboard.server.common.data.BaseData; +import org.thingsboard.server.common.data.id.OAuth2ParamsId; +import org.thingsboard.server.common.data.id.TenantId; + +@EqualsAndHashCode(callSuper = true) +@Data +@ToString +@NoArgsConstructor +public class OAuth2Params extends BaseData { + + private boolean enabled; + private TenantId tenantId; + + public OAuth2Params(OAuth2Params oauth2Params) { + super(oauth2Params); + this.enabled = oauth2Params.enabled; + this.tenantId = oauth2Params.tenantId; + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ParamsInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ParamsInfo.java new file mode 100644 index 0000000000..1a1d729d6b --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ParamsInfo.java @@ -0,0 +1,39 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.oauth2; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import java.util.List; + +@EqualsAndHashCode +@Data +@ToString +@Builder(toBuilder = true) +@NoArgsConstructor +@AllArgsConstructor +public class OAuth2ParamsInfo { + + private List domainInfos; + private List mobileInfos; + private List clientRegistrations; + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Registration.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Registration.java new file mode 100644 index 0000000000..5120e3202f --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Registration.java @@ -0,0 +1,77 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.oauth2; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; +import org.thingsboard.server.common.data.HasName; +import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; +import org.thingsboard.server.common.data.id.OAuth2ParamsId; +import org.thingsboard.server.common.data.id.OAuth2RegistrationId; + +import java.util.List; + +@EqualsAndHashCode(callSuper = true) +@Data +@ToString(exclude = {"clientSecret"}) +@NoArgsConstructor +public class OAuth2Registration extends SearchTextBasedWithAdditionalInfo implements HasName { + + private OAuth2ParamsId oauth2ParamsId; + private OAuth2MapperConfig mapperConfig; + private String clientId; + private String clientSecret; + private String authorizationUri; + private String accessTokenUri; + private List scope; + private String userInfoUri; + private String userNameAttributeName; + private String jwkSetUri; + private String clientAuthenticationMethod; + private String loginButtonLabel; + private String loginButtonIcon; + + public OAuth2Registration(OAuth2Registration registration) { + super(registration); + this.oauth2ParamsId = registration.oauth2ParamsId; + this.mapperConfig = registration.mapperConfig; + this.clientId = registration.clientId; + this.clientSecret = registration.clientSecret; + this.authorizationUri = registration.authorizationUri; + this.accessTokenUri = registration.accessTokenUri; + this.scope = registration.scope; + this.userInfoUri = registration.userInfoUri; + this.userNameAttributeName = registration.userNameAttributeName; + this.jwkSetUri = registration.jwkSetUri; + this.clientAuthenticationMethod = registration.clientAuthenticationMethod; + this.loginButtonLabel = registration.loginButtonLabel; + this.loginButtonIcon = registration.loginButtonIcon; + } + + @Override + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public String getName() { + return loginButtonLabel; + } + + @Override + public String getSearchText() { + return getName(); + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/ClientRegistrationDto.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2RegistrationInfo.java similarity index 92% rename from common/data/src/main/java/org/thingsboard/server/common/data/oauth2/ClientRegistrationDto.java rename to common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2RegistrationInfo.java index 2598013ab6..449977f106 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/ClientRegistrationDto.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2RegistrationInfo.java @@ -17,7 +17,6 @@ package org.thingsboard.server.common.data.oauth2; import com.fasterxml.jackson.databind.JsonNode; import lombok.*; -import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationInfoId; import java.util.List; @@ -27,7 +26,7 @@ import java.util.List; @NoArgsConstructor @AllArgsConstructor @Builder -public class ClientRegistrationDto { +public class OAuth2RegistrationInfo { private OAuth2MapperConfig mapperConfig; private String clientId; private String clientSecret; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/ClientRegistrationDto.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/ClientRegistrationDto.java new file mode 100644 index 0000000000..4f491a2d8a --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/ClientRegistrationDto.java @@ -0,0 +1,45 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.oauth2.deprecated; + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.*; +import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; + +import java.util.List; + +@Deprecated +@EqualsAndHashCode +@Data +@ToString(exclude = {"clientSecret"}) +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class ClientRegistrationDto { + private OAuth2MapperConfig mapperConfig; + private String clientId; + private String clientSecret; + private String authorizationUri; + private String accessTokenUri; + private List scope; + private String userInfoUri; + private String userNameAttributeName; + private String jwkSetUri; + private String clientAuthenticationMethod; + private String loginButtonLabel; + private String loginButtonIcon; + private JsonNode additionalInfo; +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/DomainInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/DomainInfo.java similarity index 85% rename from common/data/src/main/java/org/thingsboard/server/common/data/oauth2/DomainInfo.java rename to common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/DomainInfo.java index fd6b93100c..5078c4483f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/DomainInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/DomainInfo.java @@ -13,10 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.data.oauth2; +package org.thingsboard.server.common.data.oauth2.deprecated; import lombok.*; +import org.thingsboard.server.common.data.oauth2.SchemeType; +@Deprecated @EqualsAndHashCode @Data @ToString diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/ExtendedOAuth2ClientRegistrationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/ExtendedOAuth2ClientRegistrationInfo.java similarity index 85% rename from common/data/src/main/java/org/thingsboard/server/common/data/oauth2/ExtendedOAuth2ClientRegistrationInfo.java rename to common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/ExtendedOAuth2ClientRegistrationInfo.java index 83647283a0..d071a6f1d4 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/ExtendedOAuth2ClientRegistrationInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/ExtendedOAuth2ClientRegistrationInfo.java @@ -13,11 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.data.oauth2; +package org.thingsboard.server.common.data.oauth2.deprecated; import lombok.Data; import lombok.EqualsAndHashCode; +import org.thingsboard.server.common.data.oauth2.SchemeType; +import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientRegistrationInfo; +@Deprecated @EqualsAndHashCode(callSuper = true) @Data public class ExtendedOAuth2ClientRegistrationInfo extends OAuth2ClientRegistrationInfo { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/OAuth2ClientRegistration.java similarity index 81% rename from common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java rename to common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/OAuth2ClientRegistration.java index d0fa6648c8..cfac2de329 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/OAuth2ClientRegistration.java @@ -13,16 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.data.oauth2; +package org.thingsboard.server.common.data.oauth2.deprecated; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.ToString; import org.thingsboard.server.common.data.BaseData; -import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationId; -import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationInfoId; +import org.thingsboard.server.common.data.id.deprecated.OAuth2ClientRegistrationId; +import org.thingsboard.server.common.data.id.deprecated.OAuth2ClientRegistrationInfoId; +import org.thingsboard.server.common.data.oauth2.SchemeType; +@Deprecated @EqualsAndHashCode(callSuper = true) @Data @ToString diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistrationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/OAuth2ClientRegistrationInfo.java similarity index 92% rename from common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistrationInfo.java rename to common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/OAuth2ClientRegistrationInfo.java index f70000693c..a99f56689b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistrationInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/OAuth2ClientRegistrationInfo.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.data.oauth2; +package org.thingsboard.server.common.data.oauth2.deprecated; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; @@ -22,10 +22,12 @@ import lombok.NoArgsConstructor; import lombok.ToString; import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; -import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationInfoId; +import org.thingsboard.server.common.data.id.deprecated.OAuth2ClientRegistrationInfoId; +import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; import java.util.List; +@Deprecated @EqualsAndHashCode(callSuper = true) @Data @ToString(exclude = {"clientSecret"}) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsDomainParams.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/OAuth2ClientsDomainParams.java similarity index 92% rename from common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsDomainParams.java rename to common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/OAuth2ClientsDomainParams.java index 1570f71107..ff17fe2819 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsDomainParams.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/OAuth2ClientsDomainParams.java @@ -13,13 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.data.oauth2; +package org.thingsboard.server.common.data.oauth2.deprecated; import lombok.*; import java.util.List; -import java.util.Set; +@Deprecated @EqualsAndHashCode @Data @ToString diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsParams.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/OAuth2ClientsParams.java similarity index 92% rename from common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsParams.java rename to common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/OAuth2ClientsParams.java index 57c7351295..4f7735ef04 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsParams.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/OAuth2ClientsParams.java @@ -13,13 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.data.oauth2; +package org.thingsboard.server.common.data.oauth2.deprecated; import lombok.*; import java.util.List; -import java.util.Set; +@Deprecated @EqualsAndHashCode @Data @ToString diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index bfb530dd35..02c8db42d4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -408,10 +408,20 @@ public class ModelConstants { /** * OAuth2 client registration constants. */ - public static final String OAUTH2_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY; + + public static final String OAUTH2_PARAMS_COLUMN_FAMILY_NAME = "oauth2_params"; + public static final String OAUTH2_PARAMS_ENABLED_PROPERTY = "enabled"; + public static final String OAUTH2_PARAMS_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY; + + public static final String OAUTH2_REGISTRATION_COLUMN_FAMILY_NAME = "oauth2_registration"; + public static final String OAUTH2_DOMAIN_COLUMN_FAMILY_NAME = "oauth2_domain"; + public static final String OAUTH2_MOBILE_COLUMN_FAMILY_NAME = "oauth2_mobile"; + public static final String OAUTH2_PARAMS_ID_PROPERTY = "oauth2_params_id"; + public static final String OAUTH2_PKG_NAME_PROPERTY = "pkg_name"; + public static final String OAUTH2_CALLBACK_URL_SCHEME_PROPERTY = "callback_url_scheme"; + public static final String OAUTH2_CLIENT_REGISTRATION_INFO_COLUMN_FAMILY_NAME = "oauth2_client_registration_info"; public static final String OAUTH2_CLIENT_REGISTRATION_COLUMN_FAMILY_NAME = "oauth2_client_registration"; - public static final String OAUTH2_CLIENT_REGISTRATION_TO_DOMAIN_COLUMN_FAMILY_NAME = "oauth2_client_registration_to_domain"; public static final String OAUTH2_CLIENT_REGISTRATION_TEMPLATE_COLUMN_FAMILY_NAME = "oauth2_client_registration_template"; public static final String OAUTH2_ENABLED_PROPERTY = "enabled"; public static final String OAUTH2_TEMPLATE_PROVIDER_ID_PROPERTY = "provider_id"; @@ -422,7 +432,6 @@ public class ModelConstants { public static final String OAUTH2_CLIENT_SECRET_PROPERTY = "client_secret"; public static final String OAUTH2_AUTHORIZATION_URI_PROPERTY = "authorization_uri"; public static final String OAUTH2_TOKEN_URI_PROPERTY = "token_uri"; - public static final String OAUTH2_REDIRECT_URI_TEMPLATE_PROPERTY = "redirect_uri_template"; public static final String OAUTH2_SCOPE_PROPERTY = "scope"; public static final String OAUTH2_USER_INFO_URI_PROPERTY = "user_info_uri"; public static final String OAUTH2_USER_NAME_ATTRIBUTE_NAME_PROPERTY = "user_name_attribute_name"; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2DomainEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2DomainEntity.java new file mode 100644 index 0000000000..f1dbe6fff7 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2DomainEntity.java @@ -0,0 +1,76 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.model.sql; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.thingsboard.server.common.data.id.OAuth2DomainId; +import org.thingsboard.server.common.data.id.OAuth2ParamsId; +import org.thingsboard.server.common.data.oauth2.OAuth2Domain; +import org.thingsboard.server.common.data.oauth2.SchemeType; +import org.thingsboard.server.dao.model.BaseSqlEntity; +import org.thingsboard.server.dao.model.ModelConstants; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.Table; +import java.util.UUID; + +@Data +@EqualsAndHashCode(callSuper = true) +@Entity +@Table(name = ModelConstants.OAUTH2_DOMAIN_COLUMN_FAMILY_NAME) +public class OAuth2DomainEntity extends BaseSqlEntity { + + @Column(name = ModelConstants.OAUTH2_PARAMS_ID_PROPERTY) + private UUID oauth2ParamsId; + + @Column(name = ModelConstants.OAUTH2_DOMAIN_NAME_PROPERTY) + private String domainName; + + @Enumerated(EnumType.STRING) + @Column(name = ModelConstants.OAUTH2_DOMAIN_SCHEME_PROPERTY) + private SchemeType domainScheme; + + public OAuth2DomainEntity() { + super(); + } + + public OAuth2DomainEntity(OAuth2Domain domain) { + if (domain.getId() != null) { + this.setUuid(domain.getId().getId()); + } + this.setCreatedTime(domain.getCreatedTime()); + if (domain.getOauth2ParamsId() != null) { + this.oauth2ParamsId = domain.getOauth2ParamsId().getId(); + } + this.domainName = domain.getDomainName(); + this.domainScheme = domain.getDomainScheme(); + } + + @Override + public OAuth2Domain toData() { + OAuth2Domain domain = new OAuth2Domain(); + domain.setId(new OAuth2DomainId(id)); + domain.setCreatedTime(createdTime); + domain.setOauth2ParamsId(new OAuth2ParamsId(oauth2ParamsId)); + domain.setDomainName(domainName); + domain.setDomainScheme(domainScheme); + return domain; + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2MobileEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2MobileEntity.java new file mode 100644 index 0000000000..403f7af958 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2MobileEntity.java @@ -0,0 +1,72 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.model.sql; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.thingsboard.server.common.data.id.OAuth2MobileId; +import org.thingsboard.server.common.data.id.OAuth2ParamsId; +import org.thingsboard.server.common.data.oauth2.OAuth2Mobile; +import org.thingsboard.server.dao.model.BaseSqlEntity; +import org.thingsboard.server.dao.model.ModelConstants; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; +import java.util.UUID; + +@Data +@EqualsAndHashCode(callSuper = true) +@Entity +@Table(name = ModelConstants.OAUTH2_MOBILE_COLUMN_FAMILY_NAME) +public class OAuth2MobileEntity extends BaseSqlEntity { + + @Column(name = ModelConstants.OAUTH2_PARAMS_ID_PROPERTY) + private UUID oauth2ParamsId; + + @Column(name = ModelConstants.OAUTH2_PKG_NAME_PROPERTY) + private String pkgName; + + @Column(name = ModelConstants.OAUTH2_CALLBACK_URL_SCHEME_PROPERTY) + private String callbackUrlScheme; + + public OAuth2MobileEntity() { + super(); + } + + public OAuth2MobileEntity(OAuth2Mobile mobile) { + if (mobile.getId() != null) { + this.setUuid(mobile.getId().getId()); + } + this.setCreatedTime(mobile.getCreatedTime()); + if (mobile.getOauth2ParamsId() != null) { + this.oauth2ParamsId = mobile.getOauth2ParamsId().getId(); + } + this.pkgName = mobile.getPkgName(); + this.callbackUrlScheme = mobile.getCallbackUrlScheme(); + } + + @Override + public OAuth2Mobile toData() { + OAuth2Mobile mobile = new OAuth2Mobile(); + mobile.setId(new OAuth2MobileId(id)); + mobile.setCreatedTime(createdTime); + mobile.setOauth2ParamsId(new OAuth2ParamsId(oauth2ParamsId)); + mobile.setPkgName(pkgName); + mobile.setCallbackUrlScheme(callbackUrlScheme); + return mobile; + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ParamsEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ParamsEntity.java new file mode 100644 index 0000000000..f2c893f6f3 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ParamsEntity.java @@ -0,0 +1,65 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.model.sql; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import org.thingsboard.server.common.data.id.OAuth2ParamsId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.oauth2.OAuth2Params; +import org.thingsboard.server.dao.model.BaseSqlEntity; +import org.thingsboard.server.dao.model.ModelConstants; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; +import java.util.UUID; + +@Data +@EqualsAndHashCode(callSuper = true) +@Entity +@Table(name = ModelConstants.OAUTH2_PARAMS_COLUMN_FAMILY_NAME) +@NoArgsConstructor +public class OAuth2ParamsEntity extends BaseSqlEntity { + + @Column(name = ModelConstants.OAUTH2_PARAMS_ENABLED_PROPERTY) + private Boolean enabled; + + @Column(name = ModelConstants.OAUTH2_PARAMS_TENANT_ID_PROPERTY) + private UUID tenantId; + + public OAuth2ParamsEntity(OAuth2Params oauth2Params) { + if (oauth2Params.getId() != null) { + this.setUuid(oauth2Params.getUuidId()); + } + this.setCreatedTime(oauth2Params.getCreatedTime()); + this.enabled = oauth2Params.isEnabled(); + if (oauth2Params.getTenantId() != null) { + this.tenantId = oauth2Params.getTenantId().getId(); + } + } + + @Override + public OAuth2Params toData() { + OAuth2Params oauth2Params = new OAuth2Params(); + oauth2Params.setId(new OAuth2ParamsId(id)); + oauth2Params.setCreatedTime(createdTime); + oauth2Params.setTenantId(new TenantId(tenantId)); + oauth2Params.setEnabled(enabled); + return oauth2Params; + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2RegistrationEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2RegistrationEntity.java new file mode 100644 index 0000000000..9c6c260316 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2RegistrationEntity.java @@ -0,0 +1,212 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.model.sql; + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.hibernate.annotations.Type; +import org.hibernate.annotations.TypeDef; +import org.thingsboard.server.common.data.id.OAuth2ParamsId; +import org.thingsboard.server.common.data.id.OAuth2RegistrationId; +import org.thingsboard.server.common.data.oauth2.MapperType; +import org.thingsboard.server.common.data.oauth2.OAuth2BasicMapperConfig; +import org.thingsboard.server.common.data.oauth2.OAuth2CustomMapperConfig; +import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; +import org.thingsboard.server.common.data.oauth2.OAuth2Registration; +import org.thingsboard.server.common.data.oauth2.TenantNameStrategyType; +import org.thingsboard.server.dao.model.BaseSqlEntity; +import org.thingsboard.server.dao.model.ModelConstants; +import org.thingsboard.server.dao.util.mapping.JsonStringType; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.Table; +import java.util.Arrays; +import java.util.UUID; + +@Data +@EqualsAndHashCode(callSuper = true) +@Entity +@TypeDef(name = "json", typeClass = JsonStringType.class) +@Table(name = ModelConstants.OAUTH2_REGISTRATION_COLUMN_FAMILY_NAME) +public class OAuth2RegistrationEntity extends BaseSqlEntity { + + @Column(name = ModelConstants.OAUTH2_PARAMS_ID_PROPERTY) + private UUID oauth2ParamsId; + @Column(name = ModelConstants.OAUTH2_CLIENT_ID_PROPERTY) + private String clientId; + @Column(name = ModelConstants.OAUTH2_CLIENT_SECRET_PROPERTY) + private String clientSecret; + @Column(name = ModelConstants.OAUTH2_AUTHORIZATION_URI_PROPERTY) + private String authorizationUri; + @Column(name = ModelConstants.OAUTH2_TOKEN_URI_PROPERTY) + private String tokenUri; + @Column(name = ModelConstants.OAUTH2_SCOPE_PROPERTY) + private String scope; + @Column(name = ModelConstants.OAUTH2_USER_INFO_URI_PROPERTY) + private String userInfoUri; + @Column(name = ModelConstants.OAUTH2_USER_NAME_ATTRIBUTE_NAME_PROPERTY) + private String userNameAttributeName; + @Column(name = ModelConstants.OAUTH2_JWK_SET_URI_PROPERTY) + private String jwkSetUri; + @Column(name = ModelConstants.OAUTH2_CLIENT_AUTHENTICATION_METHOD_PROPERTY) + private String clientAuthenticationMethod; + @Column(name = ModelConstants.OAUTH2_LOGIN_BUTTON_LABEL_PROPERTY) + private String loginButtonLabel; + @Column(name = ModelConstants.OAUTH2_LOGIN_BUTTON_ICON_PROPERTY) + private String loginButtonIcon; + @Column(name = ModelConstants.OAUTH2_ALLOW_USER_CREATION_PROPERTY) + private Boolean allowUserCreation; + @Column(name = ModelConstants.OAUTH2_ACTIVATE_USER_PROPERTY) + private Boolean activateUser; + @Enumerated(EnumType.STRING) + @Column(name = ModelConstants.OAUTH2_MAPPER_TYPE_PROPERTY) + private MapperType type; + @Column(name = ModelConstants.OAUTH2_EMAIL_ATTRIBUTE_KEY_PROPERTY) + private String emailAttributeKey; + @Column(name = ModelConstants.OAUTH2_FIRST_NAME_ATTRIBUTE_KEY_PROPERTY) + private String firstNameAttributeKey; + @Column(name = ModelConstants.OAUTH2_LAST_NAME_ATTRIBUTE_KEY_PROPERTY) + private String lastNameAttributeKey; + @Enumerated(EnumType.STRING) + @Column(name = ModelConstants.OAUTH2_TENANT_NAME_STRATEGY_PROPERTY) + private TenantNameStrategyType tenantNameStrategy; + @Column(name = ModelConstants.OAUTH2_TENANT_NAME_PATTERN_PROPERTY) + private String tenantNamePattern; + @Column(name = ModelConstants.OAUTH2_CUSTOMER_NAME_PATTERN_PROPERTY) + private String customerNamePattern; + @Column(name = ModelConstants.OAUTH2_DEFAULT_DASHBOARD_NAME_PROPERTY) + private String defaultDashboardName; + @Column(name = ModelConstants.OAUTH2_ALWAYS_FULL_SCREEN_PROPERTY) + private Boolean alwaysFullScreen; + @Column(name = ModelConstants.OAUTH2_MAPPER_URL_PROPERTY) + private String url; + @Column(name = ModelConstants.OAUTH2_MAPPER_USERNAME_PROPERTY) + private String username; + @Column(name = ModelConstants.OAUTH2_MAPPER_PASSWORD_PROPERTY) + private String password; + @Column(name = ModelConstants.OAUTH2_MAPPER_SEND_TOKEN_PROPERTY) + private Boolean sendToken; + + @Type(type = "json") + @Column(name = ModelConstants.OAUTH2_ADDITIONAL_INFO_PROPERTY) + private JsonNode additionalInfo; + + public OAuth2RegistrationEntity() { + super(); + } + + public OAuth2RegistrationEntity(OAuth2Registration registration) { + if (registration.getId() != null) { + this.setUuid(registration.getId().getId()); + } + this.setCreatedTime(registration.getCreatedTime()); + if (registration.getOauth2ParamsId() != null) { + this.oauth2ParamsId = registration.getOauth2ParamsId().getId(); + } + this.clientId = registration.getClientId(); + this.clientSecret = registration.getClientSecret(); + this.authorizationUri = registration.getAuthorizationUri(); + this.tokenUri = registration.getAccessTokenUri(); + this.scope = registration.getScope().stream().reduce((result, element) -> result + "," + element).orElse(""); + this.userInfoUri = registration.getUserInfoUri(); + this.userNameAttributeName = registration.getUserNameAttributeName(); + this.jwkSetUri = registration.getJwkSetUri(); + this.clientAuthenticationMethod = registration.getClientAuthenticationMethod(); + this.loginButtonLabel = registration.getLoginButtonLabel(); + this.loginButtonIcon = registration.getLoginButtonIcon(); + this.additionalInfo = registration.getAdditionalInfo(); + OAuth2MapperConfig mapperConfig = registration.getMapperConfig(); + if (mapperConfig != null) { + this.allowUserCreation = mapperConfig.isAllowUserCreation(); + this.activateUser = mapperConfig.isActivateUser(); + this.type = mapperConfig.getType(); + OAuth2BasicMapperConfig basicConfig = mapperConfig.getBasic(); + if (basicConfig != null) { + this.emailAttributeKey = basicConfig.getEmailAttributeKey(); + this.firstNameAttributeKey = basicConfig.getFirstNameAttributeKey(); + this.lastNameAttributeKey = basicConfig.getLastNameAttributeKey(); + this.tenantNameStrategy = basicConfig.getTenantNameStrategy(); + this.tenantNamePattern = basicConfig.getTenantNamePattern(); + this.customerNamePattern = basicConfig.getCustomerNamePattern(); + this.defaultDashboardName = basicConfig.getDefaultDashboardName(); + this.alwaysFullScreen = basicConfig.isAlwaysFullScreen(); + } + OAuth2CustomMapperConfig customConfig = mapperConfig.getCustom(); + if (customConfig != null) { + this.url = customConfig.getUrl(); + this.username = customConfig.getUsername(); + this.password = customConfig.getPassword(); + this.sendToken = customConfig.isSendToken(); + } + } + } + + @Override + public OAuth2Registration toData() { + OAuth2Registration registration = new OAuth2Registration(); + registration.setId(new OAuth2RegistrationId(id)); + registration.setCreatedTime(createdTime); + registration.setOauth2ParamsId(new OAuth2ParamsId(oauth2ParamsId)); + registration.setAdditionalInfo(additionalInfo); + registration.setMapperConfig( + OAuth2MapperConfig.builder() + .allowUserCreation(allowUserCreation) + .activateUser(activateUser) + .type(type) + .basic( + (type == MapperType.BASIC || type == MapperType.GITHUB) ? + OAuth2BasicMapperConfig.builder() + .emailAttributeKey(emailAttributeKey) + .firstNameAttributeKey(firstNameAttributeKey) + .lastNameAttributeKey(lastNameAttributeKey) + .tenantNameStrategy(tenantNameStrategy) + .tenantNamePattern(tenantNamePattern) + .customerNamePattern(customerNamePattern) + .defaultDashboardName(defaultDashboardName) + .alwaysFullScreen(alwaysFullScreen) + .build() + : null + ) + .custom( + type == MapperType.CUSTOM ? + OAuth2CustomMapperConfig.builder() + .url(url) + .username(username) + .password(password) + .sendToken(sendToken) + .build() + : null + ) + .build() + ); + registration.setClientId(clientId); + registration.setClientSecret(clientSecret); + registration.setAuthorizationUri(authorizationUri); + registration.setAccessTokenUri(tokenUri); + registration.setScope(Arrays.asList(scope.split(","))); + registration.setUserInfoUri(userInfoUri); + registration.setUserNameAttributeName(userNameAttributeName); + registration.setJwkSetUri(jwkSetUri); + registration.setClientAuthenticationMethod(clientAuthenticationMethod); + registration.setLoginButtonLabel(loginButtonLabel); + registration.setLoginButtonIcon(loginButtonIcon); + return registration; + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractOAuth2ClientRegistrationInfoEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/deprecated/AbstractOAuth2ClientRegistrationInfoEntity.java similarity index 97% rename from dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractOAuth2ClientRegistrationInfoEntity.java rename to dao/src/main/java/org/thingsboard/server/dao/model/sql/deprecated/AbstractOAuth2ClientRegistrationInfoEntity.java index 4799087457..616f128920 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractOAuth2ClientRegistrationInfoEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/deprecated/AbstractOAuth2ClientRegistrationInfoEntity.java @@ -13,22 +13,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.model.sql; +package org.thingsboard.server.dao.model.sql.deprecated; import com.fasterxml.jackson.databind.JsonNode; import lombok.Data; import lombok.EqualsAndHashCode; import org.hibernate.annotations.Type; import org.hibernate.annotations.TypeDef; -import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationInfoId; +import org.thingsboard.server.common.data.id.deprecated.OAuth2ClientRegistrationInfoId; import org.thingsboard.server.common.data.oauth2.*; +import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientRegistrationInfo; import org.thingsboard.server.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.ModelConstants; +import org.thingsboard.server.dao.model.sql.deprecated.OAuth2ClientRegistrationInfoEntity; import org.thingsboard.server.dao.util.mapping.JsonStringType; import javax.persistence.*; import java.util.Arrays; +@Deprecated @Data @EqualsAndHashCode(callSuper = true) @TypeDef(name = "json", typeClass = JsonStringType.class) diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/ExtendedOAuth2ClientRegistrationInfoEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/deprecated/ExtendedOAuth2ClientRegistrationInfoEntity.java similarity index 91% rename from dao/src/main/java/org/thingsboard/server/dao/model/sql/ExtendedOAuth2ClientRegistrationInfoEntity.java rename to dao/src/main/java/org/thingsboard/server/dao/model/sql/deprecated/ExtendedOAuth2ClientRegistrationInfoEntity.java index 129bcf730b..beb525e2e0 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/ExtendedOAuth2ClientRegistrationInfoEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/deprecated/ExtendedOAuth2ClientRegistrationInfoEntity.java @@ -13,13 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.model.sql; +package org.thingsboard.server.dao.model.sql.deprecated; import lombok.Data; import lombok.EqualsAndHashCode; -import org.thingsboard.server.common.data.oauth2.ExtendedOAuth2ClientRegistrationInfo; +import org.thingsboard.server.common.data.oauth2.deprecated.ExtendedOAuth2ClientRegistrationInfo; import org.thingsboard.server.common.data.oauth2.SchemeType; +@Deprecated @Data @EqualsAndHashCode(callSuper = true) public class ExtendedOAuth2ClientRegistrationInfoEntity extends AbstractOAuth2ClientRegistrationInfoEntity { diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/deprecated/OAuth2ClientRegistrationEntity.java similarity index 89% rename from dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java rename to dao/src/main/java/org/thingsboard/server/dao/model/sql/deprecated/OAuth2ClientRegistrationEntity.java index 505f2facf1..9de0b9daea 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/deprecated/OAuth2ClientRegistrationEntity.java @@ -13,22 +13,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.model.sql; +package org.thingsboard.server.dao.model.sql.deprecated; import lombok.Data; import lombok.EqualsAndHashCode; import org.hibernate.annotations.TypeDef; -import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationId; -import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationInfoId; +import org.thingsboard.server.common.data.id.deprecated.OAuth2ClientRegistrationId; +import org.thingsboard.server.common.data.id.deprecated.OAuth2ClientRegistrationInfoId; import org.thingsboard.server.common.data.oauth2.*; +import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientRegistration; import org.thingsboard.server.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.util.mapping.JsonStringType; import javax.persistence.*; -import java.util.Arrays; import java.util.UUID; +@Deprecated @Data @EqualsAndHashCode(callSuper = true) @Entity diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationInfoEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/deprecated/OAuth2ClientRegistrationInfoEntity.java similarity index 91% rename from dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationInfoEntity.java rename to dao/src/main/java/org/thingsboard/server/dao/model/sql/deprecated/OAuth2ClientRegistrationInfoEntity.java index 1a379baf23..fbb9ec3fca 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationInfoEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/deprecated/OAuth2ClientRegistrationInfoEntity.java @@ -13,18 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.model.sql; +package org.thingsboard.server.dao.model.sql.deprecated; import lombok.Data; import lombok.EqualsAndHashCode; import org.hibernate.annotations.TypeDef; -import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; +import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientRegistrationInfo; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.util.mapping.JsonStringType; import javax.persistence.Entity; import javax.persistence.Table; +@Deprecated @Data @EqualsAndHashCode(callSuper = true) @Entity diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java index 7361dc21d2..feba5c0158 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java @@ -21,7 +21,7 @@ import org.springframework.security.oauth2.client.registration.ClientRegistratio import org.springframework.security.oauth2.core.AuthorizationGrantType; import org.springframework.security.oauth2.core.ClientAuthenticationMethod; import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; +import org.thingsboard.server.common.data.oauth2.OAuth2Registration; import java.util.UUID; @@ -34,25 +34,25 @@ public class HybridClientRegistrationRepository implements ClientRegistrationRep @Override public ClientRegistration findByRegistrationId(String registrationId) { - OAuth2ClientRegistrationInfo oAuth2ClientRegistrationInfo = oAuth2Service.findClientRegistrationInfo(UUID.fromString(registrationId)); - return oAuth2ClientRegistrationInfo == null ? - null : toSpringClientRegistration(oAuth2ClientRegistrationInfo); + OAuth2Registration registration = oAuth2Service.findRegistration(UUID.fromString(registrationId)); + return registration == null ? + null : toSpringClientRegistration(registration); } - private ClientRegistration toSpringClientRegistration(OAuth2ClientRegistrationInfo localClientRegistration){ - String registrationId = localClientRegistration.getUuidId().toString(); + private ClientRegistration toSpringClientRegistration(OAuth2Registration registration){ + String registrationId = registration.getUuidId().toString(); return ClientRegistration.withRegistrationId(registrationId) - .clientName(localClientRegistration.getName()) - .clientId(localClientRegistration.getClientId()) - .authorizationUri(localClientRegistration.getAuthorizationUri()) - .clientSecret(localClientRegistration.getClientSecret()) - .tokenUri(localClientRegistration.getAccessTokenUri()) - .scope(localClientRegistration.getScope()) + .clientName(registration.getName()) + .clientId(registration.getClientId()) + .authorizationUri(registration.getAuthorizationUri()) + .clientSecret(registration.getClientSecret()) + .tokenUri(registration.getAccessTokenUri()) + .scope(registration.getScope()) .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) - .userInfoUri(localClientRegistration.getUserInfoUri()) - .userNameAttributeName(localClientRegistration.getUserNameAttributeName()) - .jwkSetUri(localClientRegistration.getJwkSetUri()) - .clientAuthenticationMethod(new ClientAuthenticationMethod(localClientRegistration.getClientAuthenticationMethod())) + .userInfoUri(registration.getUserInfoUri()) + .userNameAttributeName(registration.getUserNameAttributeName()) + .jwkSetUri(registration.getJwkSetUri()) + .clientAuthenticationMethod(new ClientAuthenticationMethod(registration.getClientAuthenticationMethod())) .redirectUri(defaultRedirectUriTemplate) .build(); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2DomainDao.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2DomainDao.java new file mode 100644 index 0000000000..78c237d943 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2DomainDao.java @@ -0,0 +1,28 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.oauth2; + +import org.thingsboard.server.common.data.oauth2.OAuth2Domain; +import org.thingsboard.server.dao.Dao; + +import java.util.List; +import java.util.UUID; + +public interface OAuth2DomainDao extends Dao { + + List findByOAuth2ParamsId(UUID oauth2ParamsId); + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2MobileDao.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2MobileDao.java new file mode 100644 index 0000000000..3bccb61b0b --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2MobileDao.java @@ -0,0 +1,28 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.oauth2; + +import org.thingsboard.server.common.data.oauth2.OAuth2Mobile; +import org.thingsboard.server.dao.Dao; + +import java.util.List; +import java.util.UUID; + +public interface OAuth2MobileDao extends Dao { + + List findByOAuth2ParamsId(UUID oauth2ParamsId); + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ParamsDao.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ParamsDao.java new file mode 100644 index 0000000000..5821431444 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ParamsDao.java @@ -0,0 +1,23 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.oauth2; + +import org.thingsboard.server.common.data.oauth2.OAuth2Params; +import org.thingsboard.server.dao.Dao; + +public interface OAuth2ParamsDao extends Dao { + void deleteAll(); +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2RegistrationDao.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2RegistrationDao.java new file mode 100644 index 0000000000..ad12b12e48 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2RegistrationDao.java @@ -0,0 +1,33 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.oauth2; + +import org.thingsboard.server.common.data.oauth2.OAuth2Registration; +import org.thingsboard.server.common.data.oauth2.SchemeType; +import org.thingsboard.server.dao.Dao; + +import java.util.List; +import java.util.UUID; + +public interface OAuth2RegistrationDao extends Dao { + + List findEnabledByDomainSchemesDomainNameAndPkgName(List domainSchemes, String domainName, String pkgName); + + List findByOAuth2ParamsId(UUID oauth2ParamsId); + + String findCallbackUrlScheme(UUID id, String pkgName); + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index acde25f8ed..8d06adee0b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -19,11 +19,21 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; +import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.oauth2.*; +import org.thingsboard.server.common.data.oauth2.deprecated.ClientRegistrationDto; +import org.thingsboard.server.common.data.oauth2.deprecated.DomainInfo; +import org.thingsboard.server.common.data.oauth2.deprecated.ExtendedOAuth2ClientRegistrationInfo; +import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientRegistration; +import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientRegistrationInfo; +import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientsDomainParams; +import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientsParams; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.IncorrectParameterException; +import org.thingsboard.server.dao.oauth2.deprecated.OAuth2ClientRegistrationDao; +import org.thingsboard.server.dao.oauth2.deprecated.OAuth2ClientRegistrationInfoDao; import javax.transaction.Transactional; import java.util.*; @@ -45,9 +55,17 @@ public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Se private OAuth2ClientRegistrationInfoDao clientRegistrationInfoDao; @Autowired private OAuth2ClientRegistrationDao clientRegistrationDao; + @Autowired + private OAuth2ParamsDao oauth2ParamsDao; + @Autowired + private OAuth2RegistrationDao oauth2RegistrationDao; + @Autowired + private OAuth2DomainDao oauth2DomainDao; + @Autowired + private OAuth2MobileDao oauth2MobileDao; @Override - public List getOAuth2Clients(String domainSchemeStr, String domainName) { + public List getOAuth2Clients(String domainSchemeStr, String domainName, String pkgName) { log.trace("Executing getOAuth2Clients [{}://{}]", domainSchemeStr, domainName); if (domainSchemeStr == null) { throw new IncorrectParameterException(INCORRECT_DOMAIN_SCHEME); @@ -59,12 +77,12 @@ public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Se throw new IncorrectParameterException(INCORRECT_DOMAIN_SCHEME); } validateString(domainName, INCORRECT_DOMAIN_NAME + domainName); - return clientRegistrationInfoDao.findByDomainSchemesAndDomainName(Arrays.asList(domainScheme, SchemeType.MIXED), domainName).stream() - .filter(OAuth2ClientRegistrationInfo::isEnabled) + return oauth2RegistrationDao.findEnabledByDomainSchemesDomainNameAndPkgName(Arrays.asList(domainScheme, SchemeType.MIXED), domainName, pkgName).stream() .map(OAuth2Utils::toClientInfo) .collect(Collectors.toList()); } + @Deprecated @Override @Transactional public void saveOAuth2Params(OAuth2ClientsParams oauth2Params) { @@ -85,6 +103,33 @@ public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Se }); } + @Override + @Transactional + public void saveOAuth2Info(OAuth2Info oauth2Info) { + log.trace("Executing saveOAuth2Info [{}]", oauth2Info); + oauth2InfoValidator.accept(oauth2Info); + oauth2ParamsDao.deleteAll(); + oauth2Info.getOauth2ParamsInfos().forEach(oauth2ParamsInfo -> { + OAuth2Params oauth2Params = OAuth2Utils.infoToOAuth2Params(oauth2Info); + OAuth2Params savedOauth2Params = oauth2ParamsDao.save(TenantId.SYS_TENANT_ID, oauth2Params); + oauth2ParamsInfo.getClientRegistrations().forEach(registrationInfo -> { + OAuth2Registration registration = OAuth2Utils.toOAuth2Registration(savedOauth2Params.getId(), registrationInfo); + oauth2RegistrationDao.save(TenantId.SYS_TENANT_ID, registration); + }); + oauth2ParamsInfo.getDomainInfos().forEach(domainInfo -> { + OAuth2Domain domain = OAuth2Utils.toOAuth2Domain(savedOauth2Params.getId(), domainInfo); + oauth2DomainDao.save(TenantId.SYS_TENANT_ID, domain); + }); + if (oauth2ParamsInfo.getMobileInfos() != null) { + oauth2ParamsInfo.getMobileInfos().forEach(mobileInfo -> { + OAuth2Mobile mobile = OAuth2Utils.toOAuth2Mobile(savedOauth2Params.getId(), mobileInfo); + oauth2MobileDao.save(TenantId.SYS_TENANT_ID, mobile); + }); + } + }); + } + + @Deprecated @Override public OAuth2ClientsParams findOAuth2Params() { log.trace("Executing findOAuth2Params"); @@ -93,16 +138,42 @@ public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Se } @Override - public OAuth2ClientRegistrationInfo findClientRegistrationInfo(UUID id) { - log.trace("Executing findClientRegistrationInfo [{}]", id); + public OAuth2Info findOAuth2Info() { + log.trace("Executing findOAuth2Info"); + OAuth2Info oauth2Info = new OAuth2Info(); + List oauth2ParamsList = oauth2ParamsDao.find(TenantId.SYS_TENANT_ID); + oauth2Info.setEnabled(oauth2ParamsList.stream().anyMatch(param -> param.isEnabled())); + List oauth2ParamsInfos = new ArrayList<>(); + oauth2Info.setOauth2ParamsInfos(oauth2ParamsInfos); + oauth2ParamsList.stream().sorted(Comparator.comparing(BaseData::getUuidId)).forEach(oauth2Params -> { + List registrations = oauth2RegistrationDao.findByOAuth2ParamsId(oauth2Params.getId().getId()); + List domains = oauth2DomainDao.findByOAuth2ParamsId(oauth2Params.getId().getId()); + List mobiles = oauth2MobileDao.findByOAuth2ParamsId(oauth2Params.getId().getId()); + oauth2ParamsInfos.add(OAuth2Utils.toOAuth2ParamsInfo(registrations, domains, mobiles)); + }); + return oauth2Info; + } + + @Override + public OAuth2Registration findRegistration(UUID id) { + log.trace("Executing findRegistration [{}]", id); + validateId(id, INCORRECT_CLIENT_REGISTRATION_ID + id); + return oauth2RegistrationDao.findById(null, id); + } + + @Override + public String findCallbackUrlScheme(UUID id, String pkgName) { + log.trace("Executing findCallbackUrlScheme [{}][{}]", id, pkgName); validateId(id, INCORRECT_CLIENT_REGISTRATION_ID + id); - return clientRegistrationInfoDao.findById(null, id); + validateString(pkgName, "Incorrect package name"); + return oauth2RegistrationDao.findCallbackUrlScheme(id, pkgName); } + @Override - public List findAllClientRegistrationInfos() { - log.trace("Executing findAllClientRegistrationInfos"); - return clientRegistrationInfoDao.findAll(); + public List findAllRegistrations() { + log.trace("Executing findAllRegistrations"); + return oauth2RegistrationDao.find(TenantId.SYS_TENANT_ID); } private final Consumer clientParamsValidator = oauth2Params -> { @@ -212,4 +283,136 @@ public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Se } } }; + + private final Consumer oauth2InfoValidator = oauth2Info -> { + if (oauth2Info == null + || oauth2Info.getOauth2ParamsInfos() == null) { + throw new DataValidationException("OAuth2 param infos should be specified!"); + } + for (OAuth2ParamsInfo oauth2Params : oauth2Info.getOauth2ParamsInfos()) { + if (oauth2Params.getDomainInfos() == null + || oauth2Params.getDomainInfos().isEmpty()) { + throw new DataValidationException("List of domain configuration should be specified!"); + } + for (OAuth2DomainInfo domainInfo : oauth2Params.getDomainInfos()) { + if (StringUtils.isEmpty(domainInfo.getName())) { + throw new DataValidationException("Domain name should be specified!"); + } + if (domainInfo.getScheme() == null) { + throw new DataValidationException("Domain scheme should be specified!"); + } + } + oauth2Params.getDomainInfos().stream() + .collect(Collectors.groupingBy(OAuth2DomainInfo::getName)) + .forEach((domainName, domainInfos) -> { + if (domainInfos.size() > 1 && domainInfos.stream().anyMatch(domainInfo -> domainInfo.getScheme() == SchemeType.MIXED)) { + throw new DataValidationException("MIXED scheme type shouldn't be combined with another scheme type!"); + } + domainInfos.stream() + .collect(Collectors.groupingBy(OAuth2DomainInfo::getScheme)) + .forEach((schemeType, domainInfosBySchemeType) -> { + if (domainInfosBySchemeType.size() > 1) { + throw new DataValidationException("Domain name and protocol must be unique within OAuth2 parameters!"); + } + }); + }); + if (oauth2Params.getMobileInfos() != null) { + for (OAuth2MobileInfo mobileInfo : oauth2Params.getMobileInfos()) { + if (StringUtils.isEmpty(mobileInfo.getPkgName())) { + throw new DataValidationException("Package should be specified!"); + } + if (StringUtils.isEmpty(mobileInfo.getCallbackUrlScheme())) { + throw new DataValidationException("Callback URL scheme should be specified!"); + } + } + oauth2Params.getMobileInfos().stream() + .collect(Collectors.groupingBy(OAuth2MobileInfo::getPkgName)) + .forEach((pkgName, mobileInfos) -> { + if (mobileInfos.size() > 1) { + throw new DataValidationException("Mobile app package name must be unique within OAuth2 parameters!"); + } + }); + } + if (oauth2Params.getClientRegistrations() == null || oauth2Params.getClientRegistrations().isEmpty()) { + throw new DataValidationException("Client registrations should be specified!"); + } + for (OAuth2RegistrationInfo clientRegistration : oauth2Params.getClientRegistrations()) { + if (StringUtils.isEmpty(clientRegistration.getClientId())) { + throw new DataValidationException("Client ID should be specified!"); + } + if (StringUtils.isEmpty(clientRegistration.getClientSecret())) { + throw new DataValidationException("Client secret should be specified!"); + } + if (StringUtils.isEmpty(clientRegistration.getAuthorizationUri())) { + throw new DataValidationException("Authorization uri should be specified!"); + } + if (StringUtils.isEmpty(clientRegistration.getAccessTokenUri())) { + throw new DataValidationException("Token uri should be specified!"); + } + if (StringUtils.isEmpty(clientRegistration.getScope())) { + throw new DataValidationException("Scope should be specified!"); + } + if (StringUtils.isEmpty(clientRegistration.getUserInfoUri())) { + throw new DataValidationException("User info uri should be specified!"); + } + if (StringUtils.isEmpty(clientRegistration.getUserNameAttributeName())) { + throw new DataValidationException("User name attribute name should be specified!"); + } + if (StringUtils.isEmpty(clientRegistration.getClientAuthenticationMethod())) { + throw new DataValidationException("Client authentication method should be specified!"); + } + if (StringUtils.isEmpty(clientRegistration.getLoginButtonLabel())) { + throw new DataValidationException("Login button label should be specified!"); + } + OAuth2MapperConfig mapperConfig = clientRegistration.getMapperConfig(); + if (mapperConfig == null) { + throw new DataValidationException("Mapper config should be specified!"); + } + if (mapperConfig.getType() == null) { + throw new DataValidationException("Mapper config type should be specified!"); + } + if (mapperConfig.getType() == MapperType.BASIC) { + OAuth2BasicMapperConfig basicConfig = mapperConfig.getBasic(); + if (basicConfig == null) { + throw new DataValidationException("Basic config should be specified!"); + } + if (StringUtils.isEmpty(basicConfig.getEmailAttributeKey())) { + throw new DataValidationException("Email attribute key should be specified!"); + } + if (basicConfig.getTenantNameStrategy() == null) { + throw new DataValidationException("Tenant name strategy should be specified!"); + } + if (basicConfig.getTenantNameStrategy() == TenantNameStrategyType.CUSTOM + && StringUtils.isEmpty(basicConfig.getTenantNamePattern())) { + throw new DataValidationException("Tenant name pattern should be specified!"); + } + } + if (mapperConfig.getType() == MapperType.GITHUB) { + OAuth2BasicMapperConfig basicConfig = mapperConfig.getBasic(); + if (basicConfig == null) { + throw new DataValidationException("Basic config should be specified!"); + } + if (!StringUtils.isEmpty(basicConfig.getEmailAttributeKey())) { + throw new DataValidationException("Email attribute key cannot be configured for GITHUB mapper type!"); + } + if (basicConfig.getTenantNameStrategy() == null) { + throw new DataValidationException("Tenant name strategy should be specified!"); + } + if (basicConfig.getTenantNameStrategy() == TenantNameStrategyType.CUSTOM + && StringUtils.isEmpty(basicConfig.getTenantNamePattern())) { + throw new DataValidationException("Tenant name pattern should be specified!"); + } + } + if (mapperConfig.getType() == MapperType.CUSTOM) { + OAuth2CustomMapperConfig customConfig = mapperConfig.getCustom(); + if (customConfig == null) { + throw new DataValidationException("Custom config should be specified!"); + } + if (StringUtils.isEmpty(customConfig.getUrl())) { + throw new DataValidationException("Custom mapper URL should be specified!"); + } + } + } + } + }; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java index 2a59ccb829..dd128b435b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java @@ -15,19 +15,30 @@ */ package org.thingsboard.server.dao.oauth2; -import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationInfoId; +import org.thingsboard.server.common.data.BaseData; +import org.thingsboard.server.common.data.id.OAuth2ParamsId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.deprecated.OAuth2ClientRegistrationInfoId; import org.thingsboard.server.common.data.oauth2.*; +import org.thingsboard.server.common.data.oauth2.deprecated.ClientRegistrationDto; +import org.thingsboard.server.common.data.oauth2.deprecated.DomainInfo; +import org.thingsboard.server.common.data.oauth2.deprecated.ExtendedOAuth2ClientRegistrationInfo; +import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientRegistration; +import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientRegistrationInfo; +import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientsDomainParams; +import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientsParams; import java.util.*; +import java.util.stream.Collectors; public class OAuth2Utils { public static final String OAUTH2_AUTHORIZATION_PATH_TEMPLATE = "/oauth2/authorization/%s"; - public static OAuth2ClientInfo toClientInfo(OAuth2ClientRegistrationInfo clientRegistrationInfo) { + public static OAuth2ClientInfo toClientInfo(OAuth2Registration registration) { OAuth2ClientInfo client = new OAuth2ClientInfo(); - client.setName(clientRegistrationInfo.getLoginButtonLabel()); - client.setUrl(String.format(OAUTH2_AUTHORIZATION_PATH_TEMPLATE, clientRegistrationInfo.getUuidId().toString())); - client.setIcon(clientRegistrationInfo.getLoginButtonIcon()); + client.setName(registration.getLoginButtonLabel()); + client.setUrl(String.format(OAUTH2_AUTHORIZATION_PATH_TEMPLATE, registration.getUuidId().toString())); + client.setIcon(registration.getLoginButtonIcon()); return client; } @@ -99,4 +110,127 @@ public class OAuth2Utils { clientRegistration.setDomainScheme(domainScheme); return clientRegistration; } + + public static OAuth2ParamsInfo toOAuth2ParamsInfo(List registrations, List domains, List mobiles) { + OAuth2ParamsInfo oauth2ParamsInfo = new OAuth2ParamsInfo(); + oauth2ParamsInfo.setClientRegistrations(registrations.stream().sorted(Comparator.comparing(BaseData::getUuidId)).map(OAuth2Utils::toOAuth2RegistrationInfo).collect(Collectors.toList())); + oauth2ParamsInfo.setDomainInfos(domains.stream().sorted(Comparator.comparing(BaseData::getUuidId)).map(OAuth2Utils::toOAuth2DomainInfo).collect(Collectors.toList())); + oauth2ParamsInfo.setMobileInfos(mobiles.stream().sorted(Comparator.comparing(BaseData::getUuidId)).map(OAuth2Utils::toOAuth2MobileInfo).collect(Collectors.toList())); + return oauth2ParamsInfo; + } + + public static OAuth2RegistrationInfo toOAuth2RegistrationInfo(OAuth2Registration registration) { + return OAuth2RegistrationInfo.builder() + .mapperConfig(registration.getMapperConfig()) + .clientId(registration.getClientId()) + .clientSecret(registration.getClientSecret()) + .authorizationUri(registration.getAuthorizationUri()) + .accessTokenUri(registration.getAccessTokenUri()) + .scope(registration.getScope()) + .userInfoUri(registration.getUserInfoUri()) + .userNameAttributeName(registration.getUserNameAttributeName()) + .jwkSetUri(registration.getJwkSetUri()) + .clientAuthenticationMethod(registration.getClientAuthenticationMethod()) + .loginButtonLabel(registration.getLoginButtonLabel()) + .loginButtonIcon(registration.getLoginButtonIcon()) + .additionalInfo(registration.getAdditionalInfo()) + .build(); + } + + public static OAuth2DomainInfo toOAuth2DomainInfo(OAuth2Domain domain) { + return OAuth2DomainInfo.builder() + .name(domain.getDomainName()) + .scheme(domain.getDomainScheme()) + .build(); + } + + public static OAuth2MobileInfo toOAuth2MobileInfo(OAuth2Mobile mobile) { + return OAuth2MobileInfo.builder() + .pkgName(mobile.getPkgName()) + .callbackUrlScheme(mobile.getCallbackUrlScheme()) + .build(); + } + + public static OAuth2Params infoToOAuth2Params(OAuth2Info oauth2Info) { + OAuth2Params oauth2Params = new OAuth2Params(); + oauth2Params.setEnabled(oauth2Info.isEnabled()); + oauth2Params.setTenantId(TenantId.SYS_TENANT_ID); + return oauth2Params; + } + + public static OAuth2Registration toOAuth2Registration(OAuth2ParamsId oauth2ParamsId, OAuth2RegistrationInfo registrationInfo) { + OAuth2Registration registration = new OAuth2Registration(); + registration.setOauth2ParamsId(oauth2ParamsId); + registration.setMapperConfig(registrationInfo.getMapperConfig()); + registration.setClientId(registrationInfo.getClientId()); + registration.setClientSecret(registrationInfo.getClientSecret()); + registration.setAuthorizationUri(registrationInfo.getAuthorizationUri()); + registration.setAccessTokenUri(registrationInfo.getAccessTokenUri()); + registration.setScope(registrationInfo.getScope()); + registration.setUserInfoUri(registrationInfo.getUserInfoUri()); + registration.setUserNameAttributeName(registrationInfo.getUserNameAttributeName()); + registration.setJwkSetUri(registrationInfo.getJwkSetUri()); + registration.setClientAuthenticationMethod(registrationInfo.getClientAuthenticationMethod()); + registration.setLoginButtonLabel(registrationInfo.getLoginButtonLabel()); + registration.setLoginButtonIcon(registrationInfo.getLoginButtonIcon()); + registration.setAdditionalInfo(registrationInfo.getAdditionalInfo()); + return registration; + } + + public static OAuth2Domain toOAuth2Domain(OAuth2ParamsId oauth2ParamsId, OAuth2DomainInfo domainInfo) { + OAuth2Domain domain = new OAuth2Domain(); + domain.setOauth2ParamsId(oauth2ParamsId); + domain.setDomainName(domainInfo.getName()); + domain.setDomainScheme(domainInfo.getScheme()); + return domain; + } + + public static OAuth2Mobile toOAuth2Mobile(OAuth2ParamsId oauth2ParamsId, OAuth2MobileInfo mobileInfo) { + OAuth2Mobile mobile = new OAuth2Mobile(); + mobile.setOauth2ParamsId(oauth2ParamsId); + mobile.setPkgName(mobileInfo.getPkgName()); + mobile.setCallbackUrlScheme(mobileInfo.getCallbackUrlScheme()); + return mobile; + } + + @Deprecated + public static OAuth2Info clientParamsToOAuth2Info(OAuth2ClientsParams clientsParams) { + OAuth2Info oauth2Info = new OAuth2Info(); + oauth2Info.setEnabled(clientsParams.isEnabled()); + oauth2Info.setOauth2ParamsInfos(clientsParams.getDomainsParams().stream().map(OAuth2Utils::clientsDomainParamsToOAuth2ParamsInfo).collect(Collectors.toList())); + return oauth2Info; + } + + private static OAuth2ParamsInfo clientsDomainParamsToOAuth2ParamsInfo(OAuth2ClientsDomainParams clientsDomainParams) { + OAuth2ParamsInfo oauth2ParamsInfo = new OAuth2ParamsInfo(); + oauth2ParamsInfo.setMobileInfos(Collections.emptyList()); + oauth2ParamsInfo.setClientRegistrations(clientsDomainParams.getClientRegistrations().stream().map(OAuth2Utils::clientRegistrationDtoToOAuth2RegistrationInfo).collect(Collectors.toList())); + oauth2ParamsInfo.setDomainInfos(clientsDomainParams.getDomainInfos().stream().map(OAuth2Utils::domainInfoToOAuth2DomainInfo).collect(Collectors.toList())); + return oauth2ParamsInfo; + } + + private static OAuth2RegistrationInfo clientRegistrationDtoToOAuth2RegistrationInfo(ClientRegistrationDto clientRegistrationDto) { + return OAuth2RegistrationInfo.builder() + .mapperConfig(clientRegistrationDto.getMapperConfig()) + .clientId(clientRegistrationDto.getClientId()) + .clientSecret(clientRegistrationDto.getClientSecret()) + .authorizationUri(clientRegistrationDto.getAuthorizationUri()) + .accessTokenUri(clientRegistrationDto.getAccessTokenUri()) + .scope(clientRegistrationDto.getScope()) + .userInfoUri(clientRegistrationDto.getUserInfoUri()) + .userNameAttributeName(clientRegistrationDto.getUserNameAttributeName()) + .jwkSetUri(clientRegistrationDto.getJwkSetUri()) + .clientAuthenticationMethod(clientRegistrationDto.getClientAuthenticationMethod()) + .loginButtonLabel(clientRegistrationDto.getLoginButtonLabel()) + .loginButtonIcon(clientRegistrationDto.getLoginButtonIcon()) + .additionalInfo(clientRegistrationDto.getAdditionalInfo()) + .build(); + } + + private static OAuth2DomainInfo domainInfoToOAuth2DomainInfo(DomainInfo domainInfo) { + return OAuth2DomainInfo.builder() + .name(domainInfo.getName()) + .scheme(domainInfo.getScheme()) + .build(); + } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationDao.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/deprecated/OAuth2ClientRegistrationDao.java similarity index 83% rename from dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationDao.java rename to dao/src/main/java/org/thingsboard/server/dao/oauth2/deprecated/OAuth2ClientRegistrationDao.java index 2f4f2c1be2..63b2c46034 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/deprecated/OAuth2ClientRegistrationDao.java @@ -13,11 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.oauth2; +package org.thingsboard.server.dao.oauth2.deprecated; -import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistration; +import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientRegistration; import org.thingsboard.server.dao.Dao; +@Deprecated public interface OAuth2ClientRegistrationDao extends Dao { void deleteAll(); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationInfoDao.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/deprecated/OAuth2ClientRegistrationInfoDao.java similarity index 81% rename from dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationInfoDao.java rename to dao/src/main/java/org/thingsboard/server/dao/oauth2/deprecated/OAuth2ClientRegistrationInfoDao.java index 6fe93bacf4..9f44335602 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationInfoDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/deprecated/OAuth2ClientRegistrationInfoDao.java @@ -13,16 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.oauth2; +package org.thingsboard.server.dao.oauth2.deprecated; -import org.thingsboard.server.common.data.oauth2.ExtendedOAuth2ClientRegistrationInfo; -import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; +import org.thingsboard.server.common.data.oauth2.deprecated.ExtendedOAuth2ClientRegistrationInfo; +import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientRegistrationInfo; import org.thingsboard.server.common.data.oauth2.SchemeType; import org.thingsboard.server.dao.Dao; import java.util.List; -import java.util.Set; +@Deprecated public interface OAuth2ClientRegistrationInfoDao extends Dao { List findAll(); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2DomainDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2DomainDao.java new file mode 100644 index 0000000000..0d74821678 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2DomainDao.java @@ -0,0 +1,52 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.sql.oauth2; + +import lombok.RequiredArgsConstructor; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.oauth2.OAuth2Domain; +import org.thingsboard.server.dao.DaoUtil; +import org.thingsboard.server.dao.model.sql.OAuth2DomainEntity; +import org.thingsboard.server.dao.oauth2.OAuth2DomainDao; +import org.thingsboard.server.dao.sql.JpaAbstractDao; + +import java.util.List; +import java.util.UUID; + +@Component +@RequiredArgsConstructor +public class JpaOAuth2DomainDao extends JpaAbstractDao implements OAuth2DomainDao { + + private final OAuth2DomainRepository repository; + + @Override + protected Class getEntityClass() { + return OAuth2DomainEntity.class; + } + + @Override + protected CrudRepository getCrudRepository() { + return repository; + } + + @Override + public List findByOAuth2ParamsId(UUID oauth2ParamsId) { + return DaoUtil.convertDataList(repository.findByOauth2ParamsId(oauth2ParamsId)); + } + +} + diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2MobileDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2MobileDao.java new file mode 100644 index 0000000000..6f216b23f2 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2MobileDao.java @@ -0,0 +1,52 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.sql.oauth2; + +import lombok.RequiredArgsConstructor; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.oauth2.OAuth2Mobile; +import org.thingsboard.server.dao.DaoUtil; +import org.thingsboard.server.dao.model.sql.OAuth2MobileEntity; +import org.thingsboard.server.dao.oauth2.OAuth2MobileDao; +import org.thingsboard.server.dao.sql.JpaAbstractDao; + +import java.util.List; +import java.util.UUID; + +@Component +@RequiredArgsConstructor +public class JpaOAuth2MobileDao extends JpaAbstractDao implements OAuth2MobileDao { + + private final OAuth2MobileRepository repository; + + @Override + protected Class getEntityClass() { + return OAuth2MobileEntity.class; + } + + @Override + protected CrudRepository getCrudRepository() { + return repository; + } + + @Override + public List findByOAuth2ParamsId(UUID oauth2ParamsId) { + return DaoUtil.convertDataList(repository.findByOauth2ParamsId(oauth2ParamsId)); + } + +} + diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ParamsDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ParamsDao.java new file mode 100644 index 0000000000..2f28cb58f6 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ParamsDao.java @@ -0,0 +1,47 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.sql.oauth2; + +import lombok.RequiredArgsConstructor; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.oauth2.OAuth2Params; +import org.thingsboard.server.dao.model.sql.OAuth2ParamsEntity; +import org.thingsboard.server.dao.oauth2.OAuth2ParamsDao; +import org.thingsboard.server.dao.sql.JpaAbstractDao; + +import java.util.UUID; + +@Component +@RequiredArgsConstructor +public class JpaOAuth2ParamsDao extends JpaAbstractDao implements OAuth2ParamsDao { + private final OAuth2ParamsRepository repository; + + @Override + protected Class getEntityClass() { + return OAuth2ParamsEntity.class; + } + + @Override + protected CrudRepository getCrudRepository() { + return repository; + } + + @Override + public void deleteAll() { + repository.deleteAll(); + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2RegistrationDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2RegistrationDao.java new file mode 100644 index 0000000000..c3ee8a8c03 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2RegistrationDao.java @@ -0,0 +1,62 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.sql.oauth2; + +import lombok.RequiredArgsConstructor; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.oauth2.OAuth2Registration; +import org.thingsboard.server.common.data.oauth2.SchemeType; +import org.thingsboard.server.dao.DaoUtil; +import org.thingsboard.server.dao.model.sql.OAuth2RegistrationEntity; +import org.thingsboard.server.dao.oauth2.OAuth2RegistrationDao; +import org.thingsboard.server.dao.sql.JpaAbstractDao; + +import java.util.List; +import java.util.UUID; + +@Component +@RequiredArgsConstructor +public class JpaOAuth2RegistrationDao extends JpaAbstractDao implements OAuth2RegistrationDao { + + private final OAuth2RegistrationRepository repository; + + @Override + protected Class getEntityClass() { + return OAuth2RegistrationEntity.class; + } + + @Override + protected CrudRepository getCrudRepository() { + return repository; + } + + @Override + public List findEnabledByDomainSchemesDomainNameAndPkgName(List domainSchemes, String domainName, String pkgName) { + return DaoUtil.convertDataList(repository.findAllEnabledByDomainSchemesNameAndPkgName(domainSchemes, domainName, pkgName)); + } + + @Override + public List findByOAuth2ParamsId(UUID oauth2ParamsId) { + return DaoUtil.convertDataList(repository.findByOauth2ParamsId(oauth2ParamsId)); + } + + @Override + public String findCallbackUrlScheme(UUID id, String pkgName) { + return repository.findCallbackUrlScheme(id, pkgName); + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2DomainRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2DomainRepository.java new file mode 100644 index 0000000000..26d084f613 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2DomainRepository.java @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.sql.oauth2; + +import org.springframework.data.repository.CrudRepository; +import org.thingsboard.server.dao.model.sql.OAuth2DomainEntity; + +import java.util.List; +import java.util.UUID; + +public interface OAuth2DomainRepository extends CrudRepository { + + List findByOauth2ParamsId(UUID oauth2ParamsId); + +} + diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2MobileRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2MobileRepository.java new file mode 100644 index 0000000000..d4660ef2ee --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2MobileRepository.java @@ -0,0 +1,28 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.sql.oauth2; + +import org.springframework.data.repository.CrudRepository; +import org.thingsboard.server.dao.model.sql.OAuth2MobileEntity; + +import java.util.List; +import java.util.UUID; + +public interface OAuth2MobileRepository extends CrudRepository { + + List findByOauth2ParamsId(UUID oauth2ParamsId); + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ParamsRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ParamsRepository.java new file mode 100644 index 0000000000..8e53b34e31 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ParamsRepository.java @@ -0,0 +1,24 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.sql.oauth2; + +import org.springframework.data.repository.CrudRepository; +import org.thingsboard.server.dao.model.sql.OAuth2ParamsEntity; + +import java.util.UUID; + +public interface OAuth2ParamsRepository extends CrudRepository { +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2RegistrationRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2RegistrationRepository.java new file mode 100644 index 0000000000..676df8adcc --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2RegistrationRepository.java @@ -0,0 +1,52 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.sql.oauth2; + +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.query.Param; +import org.thingsboard.server.common.data.oauth2.SchemeType; +import org.thingsboard.server.dao.model.sql.OAuth2RegistrationEntity; + +import java.util.List; +import java.util.UUID; + +public interface OAuth2RegistrationRepository extends CrudRepository { + + @Query("SELECT reg " + + "FROM OAuth2RegistrationEntity reg " + + "LEFT JOIN OAuth2ParamsEntity params on reg.oauth2ParamsId = params.id " + + "LEFT JOIN OAuth2DomainEntity domain on reg.oauth2ParamsId = domain.oauth2ParamsId " + + "LEFT JOIN OAuth2MobileEntity mobile on reg.oauth2ParamsId = mobile.oauth2ParamsId " + + "WHERE params.enabled = true " + + "AND domain.domainName = :domainName " + + "AND domain.domainScheme IN (:domainSchemes) " + + "AND (:pkgName IS NULL OR mobile.pkgName = :pkgName)") + List findAllEnabledByDomainSchemesNameAndPkgName(@Param("domainSchemes") List domainSchemes, + @Param("domainName") String domainName, + @Param("pkgName") String pkgName); + + List findByOauth2ParamsId(UUID oauth2ParamsId); + + @Query("SELECT mobile.callbackUrlScheme " + + "FROM OAuth2MobileEntity mobile " + + "LEFT JOIN OAuth2RegistrationEntity reg on mobile.oauth2ParamsId = reg.oauth2ParamsId " + + "WHERE reg.id = :registrationId " + + "AND mobile.pkgName = :pkgName") + String findCallbackUrlScheme(@Param("registrationId") UUID id, + @Param("pkgName") String pkgName); + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/deprecated/JpaOAuth2ClientRegistrationDao.java similarity index 82% rename from dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationDao.java rename to dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/deprecated/JpaOAuth2ClientRegistrationDao.java index 8aea9c9c3f..8dfe92b18d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/deprecated/JpaOAuth2ClientRegistrationDao.java @@ -13,19 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.sql.oauth2; +package org.thingsboard.server.dao.sql.oauth2.deprecated; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistration; -import org.thingsboard.server.dao.model.sql.OAuth2ClientRegistrationEntity; -import org.thingsboard.server.dao.oauth2.OAuth2ClientRegistrationDao; +import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientRegistration; +import org.thingsboard.server.dao.model.sql.deprecated.OAuth2ClientRegistrationEntity; +import org.thingsboard.server.dao.oauth2.deprecated.OAuth2ClientRegistrationDao; import org.thingsboard.server.dao.sql.JpaAbstractDao; import java.util.UUID; +@Deprecated @Component @RequiredArgsConstructor public class JpaOAuth2ClientRegistrationDao extends JpaAbstractDao implements OAuth2ClientRegistrationDao { diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationInfoDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/deprecated/JpaOAuth2ClientRegistrationInfoDao.java similarity index 85% rename from dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationInfoDao.java rename to dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/deprecated/JpaOAuth2ClientRegistrationInfoDao.java index 0a25678e16..d37ca981f2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationInfoDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/deprecated/JpaOAuth2ClientRegistrationInfoDao.java @@ -13,17 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.sql.oauth2; +package org.thingsboard.server.dao.sql.oauth2.deprecated; import lombok.RequiredArgsConstructor; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.oauth2.ExtendedOAuth2ClientRegistrationInfo; -import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; +import org.thingsboard.server.common.data.oauth2.deprecated.ExtendedOAuth2ClientRegistrationInfo; +import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientRegistrationInfo; import org.thingsboard.server.common.data.oauth2.SchemeType; import org.thingsboard.server.dao.DaoUtil; -import org.thingsboard.server.dao.model.sql.OAuth2ClientRegistrationInfoEntity; -import org.thingsboard.server.dao.oauth2.OAuth2ClientRegistrationInfoDao; +import org.thingsboard.server.dao.model.sql.deprecated.OAuth2ClientRegistrationInfoEntity; +import org.thingsboard.server.dao.oauth2.deprecated.OAuth2ClientRegistrationInfoDao; import org.thingsboard.server.dao.sql.JpaAbstractDao; import java.util.ArrayList; @@ -31,6 +31,7 @@ import java.util.List; import java.util.UUID; import java.util.stream.Collectors; +@Deprecated @Component @RequiredArgsConstructor public class JpaOAuth2ClientRegistrationInfoDao extends JpaAbstractDao implements OAuth2ClientRegistrationInfoDao { diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ClientRegistrationInfoRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/deprecated/OAuth2ClientRegistrationInfoRepository.java similarity index 81% rename from dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ClientRegistrationInfoRepository.java rename to dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/deprecated/OAuth2ClientRegistrationInfoRepository.java index 18ac3be05a..73a2ca9bba 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ClientRegistrationInfoRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/deprecated/OAuth2ClientRegistrationInfoRepository.java @@ -13,18 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.sql.oauth2; +package org.thingsboard.server.dao.sql.oauth2.deprecated; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.query.Param; import org.thingsboard.server.common.data.oauth2.SchemeType; -import org.thingsboard.server.dao.model.sql.ExtendedOAuth2ClientRegistrationInfoEntity; -import org.thingsboard.server.dao.model.sql.OAuth2ClientRegistrationInfoEntity; +import org.thingsboard.server.dao.model.sql.deprecated.ExtendedOAuth2ClientRegistrationInfoEntity; +import org.thingsboard.server.dao.model.sql.deprecated.OAuth2ClientRegistrationInfoEntity; import java.util.List; import java.util.UUID; +@Deprecated public interface OAuth2ClientRegistrationInfoRepository extends CrudRepository { @Query("SELECT new OAuth2ClientRegistrationInfoEntity(cr_info) " + "FROM OAuth2ClientRegistrationInfoEntity cr_info " + @@ -34,7 +35,7 @@ public interface OAuth2ClientRegistrationInfoRepository extends CrudRepository findAllByDomainSchemesAndName(@Param("domainSchemes") List domainSchemes, @Param("domainName") String domainName); - @Query("SELECT new org.thingsboard.server.dao.model.sql.ExtendedOAuth2ClientRegistrationInfoEntity(cr_info, cr.domainName, cr.domainScheme) " + + @Query("SELECT new org.thingsboard.server.dao.model.sql.deprecated.ExtendedOAuth2ClientRegistrationInfoEntity(cr_info, cr.domainName, cr.domainScheme) " + "FROM OAuth2ClientRegistrationInfoEntity cr_info " + "LEFT JOIN OAuth2ClientRegistrationEntity cr on cr_info.id = cr.clientRegistrationInfoId ") List findAllExtended(); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ClientRegistrationRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/deprecated/OAuth2ClientRegistrationRepository.java similarity index 83% rename from dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ClientRegistrationRepository.java rename to dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/deprecated/OAuth2ClientRegistrationRepository.java index 1849f46514..1f4e4783ab 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ClientRegistrationRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/deprecated/OAuth2ClientRegistrationRepository.java @@ -13,12 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.sql.oauth2; +package org.thingsboard.server.dao.sql.oauth2.deprecated; import org.springframework.data.repository.CrudRepository; -import org.thingsboard.server.dao.model.sql.OAuth2ClientRegistrationEntity; +import org.thingsboard.server.dao.model.sql.deprecated.OAuth2ClientRegistrationEntity; import java.util.UUID; +@Deprecated public interface OAuth2ClientRegistrationRepository extends CrudRepository { } diff --git a/dao/src/main/resources/sql/schema-entities-hsql.sql b/dao/src/main/resources/sql/schema-entities-hsql.sql index 2c5113867a..0e7aec5c19 100644 --- a/dao/src/main/resources/sql/schema-entities-hsql.sql +++ b/dao/src/main/resources/sql/schema-entities-hsql.sql @@ -374,9 +374,16 @@ CREATE TABLE IF NOT EXISTS ts_kv_dictionary ( CONSTRAINT ts_key_id_pkey PRIMARY KEY (key) ); -CREATE TABLE IF NOT EXISTS oauth2_client_registration_info ( - id uuid NOT NULL CONSTRAINT oauth2_client_registration_info_pkey PRIMARY KEY, +CREATE TABLE IF NOT EXISTS oauth2_params ( + id uuid NOT NULL CONSTRAINT oauth2_params_pkey PRIMARY KEY, enabled boolean, + tenant_id uuid, + created_time bigint NOT NULL +); + +CREATE TABLE IF NOT EXISTS oauth2_registration ( + id uuid NOT NULL CONSTRAINT oauth2_registration_pkey PRIMARY KEY, + oauth2_params_id uuid NOT NULL, created_time bigint NOT NULL, additional_info varchar, client_id varchar(255), @@ -404,15 +411,28 @@ CREATE TABLE IF NOT EXISTS oauth2_client_registration_info ( custom_url varchar(255), custom_username varchar(255), custom_password varchar(255), - custom_send_token boolean + custom_send_token boolean, + CONSTRAINT fk_registration_oauth2_params FOREIGN KEY (oauth2_params_id) REFERENCES oauth2_params(id) ON DELETE CASCADE ); -CREATE TABLE IF NOT EXISTS oauth2_client_registration ( - id uuid NOT NULL CONSTRAINT oauth2_client_registration_pkey PRIMARY KEY, +CREATE TABLE IF NOT EXISTS oauth2_domain ( + id uuid NOT NULL CONSTRAINT oauth2_domain_pkey PRIMARY KEY, + oauth2_params_id uuid NOT NULL, created_time bigint NOT NULL, domain_name varchar(255), domain_scheme varchar(31), - client_registration_info_id uuid + CONSTRAINT fk_domain_oauth2_params FOREIGN KEY (oauth2_params_id) REFERENCES oauth2_params(id) ON DELETE CASCADE, + CONSTRAINT oauth2_domain_unq_key UNIQUE (oauth2_params_id, domain_name, domain_scheme) +); + +CREATE TABLE IF NOT EXISTS oauth2_mobile ( + id uuid NOT NULL CONSTRAINT oauth2_mobile_pkey PRIMARY KEY, + oauth2_params_id uuid NOT NULL, + created_time bigint NOT NULL, + pkg_name varchar(255), + callback_url_scheme varchar(255), + CONSTRAINT fk_mobile_oauth2_params FOREIGN KEY (oauth2_params_id) REFERENCES oauth2_params(id) ON DELETE CASCADE, + CONSTRAINT oauth2_mobile_unq_key UNIQUE (oauth2_params_id, pkg_name) ); CREATE TABLE IF NOT EXISTS oauth2_client_registration_template ( @@ -443,6 +463,49 @@ CREATE TABLE IF NOT EXISTS oauth2_client_registration_template ( CONSTRAINT oauth2_template_provider_id_unq_key UNIQUE (provider_id) ); +-- Deprecated +CREATE TABLE IF NOT EXISTS oauth2_client_registration_info ( + id uuid NOT NULL CONSTRAINT oauth2_client_registration_info_pkey PRIMARY KEY, + enabled boolean, + created_time bigint NOT NULL, + additional_info varchar, + client_id varchar(255), + client_secret varchar(255), + authorization_uri varchar(255), + token_uri varchar(255), + scope varchar(255), + user_info_uri varchar(255), + user_name_attribute_name varchar(255), + jwk_set_uri varchar(255), + client_authentication_method varchar(255), + login_button_label varchar(255), + login_button_icon varchar(255), + allow_user_creation boolean, + activate_user boolean, + type varchar(31), + basic_email_attribute_key varchar(31), + basic_first_name_attribute_key varchar(31), + basic_last_name_attribute_key varchar(31), + basic_tenant_name_strategy varchar(31), + basic_tenant_name_pattern varchar(255), + basic_customer_name_pattern varchar(255), + basic_default_dashboard_name varchar(255), + basic_always_full_screen boolean, + custom_url varchar(255), + custom_username varchar(255), + custom_password varchar(255), + custom_send_token boolean +); + +-- Deprecated +CREATE TABLE IF NOT EXISTS oauth2_client_registration ( + id uuid NOT NULL CONSTRAINT oauth2_client_registration_pkey PRIMARY KEY, + created_time bigint NOT NULL, + domain_name varchar(255), + domain_scheme varchar(31), + client_registration_info_id uuid +); + CREATE TABLE IF NOT EXISTS api_usage_state ( id uuid NOT NULL CONSTRAINT usage_record_pkey PRIMARY KEY, created_time bigint NOT NULL, diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index 3140ff83d6..377d140597 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -411,9 +411,16 @@ CREATE TABLE IF NOT EXISTS ts_kv_dictionary CONSTRAINT ts_key_id_pkey PRIMARY KEY (key) ); -CREATE TABLE IF NOT EXISTS oauth2_client_registration_info ( - id uuid NOT NULL CONSTRAINT oauth2_client_registration_info_pkey PRIMARY KEY, +CREATE TABLE IF NOT EXISTS oauth2_params ( + id uuid NOT NULL CONSTRAINT oauth2_params_pkey PRIMARY KEY, enabled boolean, + tenant_id uuid, + created_time bigint NOT NULL +); + +CREATE TABLE IF NOT EXISTS oauth2_registration ( + id uuid NOT NULL CONSTRAINT oauth2_registration_pkey PRIMARY KEY, + oauth2_params_id uuid NOT NULL, created_time bigint NOT NULL, additional_info varchar, client_id varchar(255), @@ -441,15 +448,28 @@ CREATE TABLE IF NOT EXISTS oauth2_client_registration_info ( custom_url varchar(255), custom_username varchar(255), custom_password varchar(255), - custom_send_token boolean + custom_send_token boolean, + CONSTRAINT fk_registration_oauth2_params FOREIGN KEY (oauth2_params_id) REFERENCES oauth2_params(id) ON DELETE CASCADE ); -CREATE TABLE IF NOT EXISTS oauth2_client_registration ( - id uuid NOT NULL CONSTRAINT oauth2_client_registration_pkey PRIMARY KEY, +CREATE TABLE IF NOT EXISTS oauth2_domain ( + id uuid NOT NULL CONSTRAINT oauth2_domain_pkey PRIMARY KEY, + oauth2_params_id uuid NOT NULL, created_time bigint NOT NULL, domain_name varchar(255), domain_scheme varchar(31), - client_registration_info_id uuid + CONSTRAINT fk_domain_oauth2_params FOREIGN KEY (oauth2_params_id) REFERENCES oauth2_params(id) ON DELETE CASCADE, + CONSTRAINT oauth2_domain_unq_key UNIQUE (oauth2_params_id, domain_name, domain_scheme) +); + +CREATE TABLE IF NOT EXISTS oauth2_mobile ( + id uuid NOT NULL CONSTRAINT oauth2_mobile_pkey PRIMARY KEY, + oauth2_params_id uuid NOT NULL, + created_time bigint NOT NULL, + pkg_name varchar(255), + callback_url_scheme varchar(255), + CONSTRAINT fk_mobile_oauth2_params FOREIGN KEY (oauth2_params_id) REFERENCES oauth2_params(id) ON DELETE CASCADE, + CONSTRAINT oauth2_mobile_unq_key UNIQUE (oauth2_params_id, pkg_name) ); CREATE TABLE IF NOT EXISTS oauth2_client_registration_template ( @@ -480,6 +500,49 @@ CREATE TABLE IF NOT EXISTS oauth2_client_registration_template ( CONSTRAINT oauth2_template_provider_id_unq_key UNIQUE (provider_id) ); +-- Deprecated +CREATE TABLE IF NOT EXISTS oauth2_client_registration_info ( + id uuid NOT NULL CONSTRAINT oauth2_client_registration_info_pkey PRIMARY KEY, + enabled boolean, + created_time bigint NOT NULL, + additional_info varchar, + client_id varchar(255), + client_secret varchar(255), + authorization_uri varchar(255), + token_uri varchar(255), + scope varchar(255), + user_info_uri varchar(255), + user_name_attribute_name varchar(255), + jwk_set_uri varchar(255), + client_authentication_method varchar(255), + login_button_label varchar(255), + login_button_icon varchar(255), + allow_user_creation boolean, + activate_user boolean, + type varchar(31), + basic_email_attribute_key varchar(31), + basic_first_name_attribute_key varchar(31), + basic_last_name_attribute_key varchar(31), + basic_tenant_name_strategy varchar(31), + basic_tenant_name_pattern varchar(255), + basic_customer_name_pattern varchar(255), + basic_default_dashboard_name varchar(255), + basic_always_full_screen boolean, + custom_url varchar(255), + custom_username varchar(255), + custom_password varchar(255), + custom_send_token boolean +); + +-- Deprecated +CREATE TABLE IF NOT EXISTS oauth2_client_registration ( + id uuid NOT NULL CONSTRAINT oauth2_client_registration_pkey PRIMARY KEY, + created_time bigint NOT NULL, + domain_name varchar(255), + domain_scheme varchar(31), + client_registration_info_id uuid +); + CREATE TABLE IF NOT EXISTS api_usage_state ( id uuid NOT NULL CONSTRAINT usage_record_pkey PRIMARY KEY, created_time bigint NOT NULL, diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java index ebfbfccd2a..fa79e32bea 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java @@ -16,208 +16,225 @@ package org.thingsboard.server.dao.service; import com.google.common.collect.Lists; -import com.google.common.collect.Sets; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.thingsboard.server.common.data.oauth2.*; +import org.thingsboard.server.common.data.oauth2.MapperType; +import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; +import org.thingsboard.server.common.data.oauth2.OAuth2CustomMapperConfig; +import org.thingsboard.server.common.data.oauth2.OAuth2DomainInfo; +import org.thingsboard.server.common.data.oauth2.OAuth2Info; +import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; +import org.thingsboard.server.common.data.oauth2.OAuth2MobileInfo; +import org.thingsboard.server.common.data.oauth2.OAuth2ParamsInfo; +import org.thingsboard.server.common.data.oauth2.OAuth2Registration; +import org.thingsboard.server.common.data.oauth2.OAuth2RegistrationInfo; +import org.thingsboard.server.common.data.oauth2.SchemeType; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.oauth2.OAuth2Service; -import java.util.*; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.UUID; import java.util.stream.Collectors; public class BaseOAuth2ServiceTest extends AbstractServiceTest { - private static final OAuth2ClientsParams EMPTY_PARAMS = new OAuth2ClientsParams(false, new ArrayList<>()); + private static final OAuth2Info EMPTY_PARAMS = new OAuth2Info(false, Collections.emptyList()); @Autowired protected OAuth2Service oAuth2Service; @Before public void beforeRun() { - Assert.assertTrue(oAuth2Service.findAllClientRegistrationInfos().isEmpty()); + Assert.assertTrue(oAuth2Service.findAllRegistrations().isEmpty()); } @After public void after() { - oAuth2Service.saveOAuth2Params(EMPTY_PARAMS); - Assert.assertTrue(oAuth2Service.findAllClientRegistrationInfos().isEmpty()); - Assert.assertTrue(oAuth2Service.findOAuth2Params().getDomainsParams().isEmpty()); + oAuth2Service.saveOAuth2Info(EMPTY_PARAMS); + Assert.assertTrue(oAuth2Service.findAllRegistrations().isEmpty()); + Assert.assertTrue(oAuth2Service.findOAuth2Info().getOauth2ParamsInfos().isEmpty()); } @Test(expected = DataValidationException.class) public void testSaveHttpAndMixedDomainsTogether() { - OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Lists.newArrayList( - OAuth2ClientsDomainParams.builder() + OAuth2Info oAuth2Info = new OAuth2Info(true, Lists.newArrayList( + OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( - DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), - DomainInfo.builder().name("first-domain").scheme(SchemeType.MIXED).build(), - DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() + OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), + OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.MIXED).build(), + OAuth2DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() )) .clientRegistrations(Lists.newArrayList( - validClientRegistrationDto(), - validClientRegistrationDto(), - validClientRegistrationDto() + validRegistrationInfo(), + validRegistrationInfo(), + validRegistrationInfo() )) .build() )); - oAuth2Service.saveOAuth2Params(clientsParams); + oAuth2Service.saveOAuth2Info(oAuth2Info); } @Test(expected = DataValidationException.class) public void testSaveHttpsAndMixedDomainsTogether() { - OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Lists.newArrayList( - OAuth2ClientsDomainParams.builder() + OAuth2Info oAuth2Info = new OAuth2Info(true, Lists.newArrayList( + OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( - DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTPS).build(), - DomainInfo.builder().name("first-domain").scheme(SchemeType.MIXED).build(), - DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() + OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTPS).build(), + OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.MIXED).build(), + OAuth2DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() )) .clientRegistrations(Lists.newArrayList( - validClientRegistrationDto(), - validClientRegistrationDto(), - validClientRegistrationDto() + validRegistrationInfo(), + validRegistrationInfo(), + validRegistrationInfo() )) .build() )); - oAuth2Service.saveOAuth2Params(clientsParams); + oAuth2Service.saveOAuth2Info(oAuth2Info); } @Test public void testCreateAndFindParams() { - OAuth2ClientsParams clientsParams = createDefaultClientsParams(); - oAuth2Service.saveOAuth2Params(clientsParams); - OAuth2ClientsParams foundClientsParams = oAuth2Service.findOAuth2Params(); - Assert.assertNotNull(foundClientsParams); + OAuth2Info oAuth2Info = createDefaultOAuth2Info(); + oAuth2Service.saveOAuth2Info(oAuth2Info); + OAuth2Info foundOAuth2Info = oAuth2Service.findOAuth2Info(); + Assert.assertNotNull(foundOAuth2Info); // TODO ask if it's safe to check equality on AdditionalProperties - Assert.assertEquals(clientsParams, foundClientsParams); + Assert.assertEquals(oAuth2Info, foundOAuth2Info); } @Test public void testDisableParams() { - OAuth2ClientsParams clientsParams = createDefaultClientsParams(); - clientsParams.setEnabled(true); - oAuth2Service.saveOAuth2Params(clientsParams); - OAuth2ClientsParams foundClientsParams = oAuth2Service.findOAuth2Params(); - Assert.assertNotNull(foundClientsParams); - Assert.assertEquals(clientsParams, foundClientsParams); - - clientsParams.setEnabled(false); - oAuth2Service.saveOAuth2Params(clientsParams); - OAuth2ClientsParams foundDisabledClientsParams = oAuth2Service.findOAuth2Params(); - Assert.assertEquals(clientsParams, foundDisabledClientsParams); + OAuth2Info oAuth2Info = createDefaultOAuth2Info(); + oAuth2Info.setEnabled(true); + oAuth2Service.saveOAuth2Info(oAuth2Info); + OAuth2Info foundOAuth2Info = oAuth2Service.findOAuth2Info(); + Assert.assertNotNull(foundOAuth2Info); + Assert.assertEquals(oAuth2Info, foundOAuth2Info); + + oAuth2Info.setEnabled(false); + oAuth2Service.saveOAuth2Info(oAuth2Info); + OAuth2Info foundDisabledOAuth2Info = oAuth2Service.findOAuth2Info(); + Assert.assertEquals(oAuth2Info, foundDisabledOAuth2Info); } @Test public void testClearDomainParams() { - OAuth2ClientsParams clientsParams = createDefaultClientsParams(); - oAuth2Service.saveOAuth2Params(clientsParams); - OAuth2ClientsParams foundClientsParams = oAuth2Service.findOAuth2Params(); - Assert.assertNotNull(foundClientsParams); - Assert.assertEquals(clientsParams, foundClientsParams); - - oAuth2Service.saveOAuth2Params(EMPTY_PARAMS); - OAuth2ClientsParams foundAfterClearClientsParams = oAuth2Service.findOAuth2Params(); + OAuth2Info oAuth2Info = createDefaultOAuth2Info(); + oAuth2Service.saveOAuth2Info(oAuth2Info); + OAuth2Info foundOAuth2Info = oAuth2Service.findOAuth2Info(); + Assert.assertNotNull(foundOAuth2Info); + Assert.assertEquals(oAuth2Info, foundOAuth2Info); + + oAuth2Service.saveOAuth2Info(EMPTY_PARAMS); + OAuth2Info foundAfterClearClientsParams = oAuth2Service.findOAuth2Info(); Assert.assertNotNull(foundAfterClearClientsParams); Assert.assertEquals(EMPTY_PARAMS, foundAfterClearClientsParams); } @Test public void testUpdateClientsParams() { - OAuth2ClientsParams clientsParams = createDefaultClientsParams(); - oAuth2Service.saveOAuth2Params(clientsParams); - OAuth2ClientsParams foundClientsParams = oAuth2Service.findOAuth2Params(); - Assert.assertNotNull(foundClientsParams); - Assert.assertEquals(clientsParams, foundClientsParams); - - OAuth2ClientsParams newClientsParams = new OAuth2ClientsParams(true, Lists.newArrayList( - OAuth2ClientsDomainParams.builder() + OAuth2Info oAuth2Info = createDefaultOAuth2Info(); + oAuth2Service.saveOAuth2Info(oAuth2Info); + OAuth2Info foundOAuth2Info = oAuth2Service.findOAuth2Info(); + Assert.assertNotNull(foundOAuth2Info); + Assert.assertEquals(oAuth2Info, foundOAuth2Info); + + OAuth2Info newOAuth2Info = new OAuth2Info(true, Lists.newArrayList( + OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( - DomainInfo.builder().name("another-domain").scheme(SchemeType.HTTPS).build() + OAuth2DomainInfo.builder().name("another-domain").scheme(SchemeType.HTTPS).build() )) + .mobileInfos(Collections.emptyList()) .clientRegistrations(Lists.newArrayList( - validClientRegistrationDto() + validRegistrationInfo() )) .build(), - OAuth2ClientsDomainParams.builder() + OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( - DomainInfo.builder().name("test-domain").scheme(SchemeType.MIXED).build() + OAuth2DomainInfo.builder().name("test-domain").scheme(SchemeType.MIXED).build() )) + .mobileInfos(Collections.emptyList()) .clientRegistrations(Lists.newArrayList( - validClientRegistrationDto() + validRegistrationInfo() )) .build() )); - oAuth2Service.saveOAuth2Params(newClientsParams); - OAuth2ClientsParams foundAfterUpdateClientsParams = oAuth2Service.findOAuth2Params(); - Assert.assertNotNull(foundAfterUpdateClientsParams); - Assert.assertEquals(newClientsParams, foundAfterUpdateClientsParams); + oAuth2Service.saveOAuth2Info(newOAuth2Info); + OAuth2Info foundAfterUpdateOAuth2Info = oAuth2Service.findOAuth2Info(); + Assert.assertNotNull(foundAfterUpdateOAuth2Info); + Assert.assertEquals(newOAuth2Info, foundAfterUpdateOAuth2Info); } @Test public void testGetOAuth2Clients() { - List firstGroup = Lists.newArrayList( - validClientRegistrationDto(), - validClientRegistrationDto(), - validClientRegistrationDto(), - validClientRegistrationDto() + List firstGroup = Lists.newArrayList( + validRegistrationInfo(), + validRegistrationInfo(), + validRegistrationInfo(), + validRegistrationInfo() ); - List secondGroup = Lists.newArrayList( - validClientRegistrationDto(), - validClientRegistrationDto() + List secondGroup = Lists.newArrayList( + validRegistrationInfo(), + validRegistrationInfo() ); - List thirdGroup = Lists.newArrayList( - validClientRegistrationDto() + List thirdGroup = Lists.newArrayList( + validRegistrationInfo() ); - OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Lists.newArrayList( - OAuth2ClientsDomainParams.builder() + OAuth2Info oAuth2Info = new OAuth2Info(true, Lists.newArrayList( + OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( - DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), - DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), - DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() + OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), + OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), + OAuth2DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() )) + .mobileInfos(Collections.emptyList()) .clientRegistrations(firstGroup) .build(), - OAuth2ClientsDomainParams.builder() + OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( - DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(), - DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() + OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(), + OAuth2DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() )) + .mobileInfos(Collections.emptyList()) .clientRegistrations(secondGroup) .build(), - OAuth2ClientsDomainParams.builder() + OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( - DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTPS).build(), - DomainInfo.builder().name("fifth-domain").scheme(SchemeType.HTTP).build() + OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTPS).build(), + OAuth2DomainInfo.builder().name("fifth-domain").scheme(SchemeType.HTTP).build() )) + .mobileInfos(Collections.emptyList()) .clientRegistrations(thirdGroup) .build() )); - oAuth2Service.saveOAuth2Params(clientsParams); - OAuth2ClientsParams foundClientsParams = oAuth2Service.findOAuth2Params(); - Assert.assertNotNull(foundClientsParams); - Assert.assertEquals(clientsParams, foundClientsParams); + oAuth2Service.saveOAuth2Info(oAuth2Info); + OAuth2Info foundOAuth2Info = oAuth2Service.findOAuth2Info(); + Assert.assertNotNull(foundOAuth2Info); + Assert.assertEquals(oAuth2Info, foundOAuth2Info); List firstGroupClientInfos = firstGroup.stream() - .map(clientRegistrationDto -> new OAuth2ClientInfo( - clientRegistrationDto.getLoginButtonLabel(), clientRegistrationDto.getLoginButtonIcon(), null)) + .map(registrationInfo -> new OAuth2ClientInfo( + registrationInfo.getLoginButtonLabel(), registrationInfo.getLoginButtonIcon(), null)) .collect(Collectors.toList()); List secondGroupClientInfos = secondGroup.stream() - .map(clientRegistrationDto -> new OAuth2ClientInfo( - clientRegistrationDto.getLoginButtonLabel(), clientRegistrationDto.getLoginButtonIcon(), null)) + .map(registrationInfo -> new OAuth2ClientInfo( + registrationInfo.getLoginButtonLabel(), registrationInfo.getLoginButtonIcon(), null)) .collect(Collectors.toList()); List thirdGroupClientInfos = thirdGroup.stream() - .map(clientRegistrationDto -> new OAuth2ClientInfo( - clientRegistrationDto.getLoginButtonLabel(), clientRegistrationDto.getLoginButtonIcon(), null)) + .map(registrationInfo -> new OAuth2ClientInfo( + registrationInfo.getLoginButtonLabel(), registrationInfo.getLoginButtonIcon(), null)) .collect(Collectors.toList()); - List nonExistentDomainClients = oAuth2Service.getOAuth2Clients("http", "non-existent-domain"); + List nonExistentDomainClients = oAuth2Service.getOAuth2Clients("http", "non-existent-domain", null); Assert.assertTrue(nonExistentDomainClients.isEmpty()); - List firstDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "first-domain"); + List firstDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "first-domain", null); Assert.assertEquals(firstGroupClientInfos.size(), firstDomainHttpClients.size()); firstGroupClientInfos.forEach(firstGroupClientInfo -> { Assert.assertTrue( @@ -227,10 +244,10 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { ); }); - List firstDomainHttpsClients = oAuth2Service.getOAuth2Clients("https", "first-domain"); + List firstDomainHttpsClients = oAuth2Service.getOAuth2Clients("https", "first-domain", null); Assert.assertTrue(firstDomainHttpsClients.isEmpty()); - List fourthDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "fourth-domain"); + List fourthDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "fourth-domain", null); Assert.assertEquals(secondGroupClientInfos.size(), fourthDomainHttpClients.size()); secondGroupClientInfos.forEach(secondGroupClientInfo -> { Assert.assertTrue( @@ -239,7 +256,7 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { && clientInfo.getName().equals(secondGroupClientInfo.getName())) ); }); - List fourthDomainHttpsClients = oAuth2Service.getOAuth2Clients("https", "fourth-domain"); + List fourthDomainHttpsClients = oAuth2Service.getOAuth2Clients("https", "fourth-domain", null); Assert.assertEquals(secondGroupClientInfos.size(), fourthDomainHttpsClients.size()); secondGroupClientInfos.forEach(secondGroupClientInfo -> { Assert.assertTrue( @@ -249,7 +266,7 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { ); }); - List secondDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "second-domain"); + List secondDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "second-domain", null); Assert.assertEquals(firstGroupClientInfos.size() + secondGroupClientInfos.size(), secondDomainHttpClients.size()); firstGroupClientInfos.forEach(firstGroupClientInfo -> { Assert.assertTrue( @@ -266,7 +283,7 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { ); }); - List secondDomainHttpsClients = oAuth2Service.getOAuth2Clients("https", "second-domain"); + List secondDomainHttpsClients = oAuth2Service.getOAuth2Clients("https", "second-domain", null); Assert.assertEquals(firstGroupClientInfos.size() + thirdGroupClientInfos.size(), secondDomainHttpsClients.size()); firstGroupClientInfos.forEach(firstGroupClientInfo -> { Assert.assertTrue( @@ -286,34 +303,35 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { @Test public void testGetOAuth2ClientsForHttpAndHttps() { - List firstGroup = Lists.newArrayList( - validClientRegistrationDto(), - validClientRegistrationDto(), - validClientRegistrationDto(), - validClientRegistrationDto() + List firstGroup = Lists.newArrayList( + validRegistrationInfo(), + validRegistrationInfo(), + validRegistrationInfo(), + validRegistrationInfo() ); - OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Lists.newArrayList( - OAuth2ClientsDomainParams.builder() + OAuth2Info oAuth2Info = new OAuth2Info(true, Lists.newArrayList( + OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( - DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), - DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), - DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTPS).build() + OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), + OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), + OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTPS).build() )) + .mobileInfos(Collections.emptyList()) .clientRegistrations(firstGroup) .build() )); - oAuth2Service.saveOAuth2Params(clientsParams); - OAuth2ClientsParams foundClientsParams = oAuth2Service.findOAuth2Params(); - Assert.assertNotNull(foundClientsParams); - Assert.assertEquals(clientsParams, foundClientsParams); + oAuth2Service.saveOAuth2Info(oAuth2Info); + OAuth2Info foundOAuth2Info = oAuth2Service.findOAuth2Info(); + Assert.assertNotNull(foundOAuth2Info); + Assert.assertEquals(oAuth2Info, foundOAuth2Info); List firstGroupClientInfos = firstGroup.stream() - .map(clientRegistrationDto -> new OAuth2ClientInfo( - clientRegistrationDto.getLoginButtonLabel(), clientRegistrationDto.getLoginButtonIcon(), null)) + .map(registrationInfo -> new OAuth2ClientInfo( + registrationInfo.getLoginButtonLabel(), registrationInfo.getLoginButtonIcon(), null)) .collect(Collectors.toList()); - List firstDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "first-domain"); + List firstDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "first-domain", null); Assert.assertEquals(firstGroupClientInfos.size(), firstDomainHttpClients.size()); firstGroupClientInfos.forEach(firstGroupClientInfo -> { Assert.assertTrue( @@ -323,7 +341,7 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { ); }); - List firstDomainHttpsClients = oAuth2Service.getOAuth2Clients("https", "first-domain"); + List firstDomainHttpsClients = oAuth2Service.getOAuth2Clients("https", "first-domain", null); Assert.assertEquals(firstGroupClientInfos.size(), firstDomainHttpsClients.size()); firstGroupClientInfos.forEach(firstGroupClientInfo -> { Assert.assertTrue( @@ -336,166 +354,220 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { @Test public void testGetDisabledOAuth2Clients() { - OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Lists.newArrayList( - OAuth2ClientsDomainParams.builder() + OAuth2Info oAuth2Info = new OAuth2Info(true, Lists.newArrayList( + OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( - DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), - DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), - DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() + OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), + OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), + OAuth2DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() )) .clientRegistrations(Lists.newArrayList( - validClientRegistrationDto(), - validClientRegistrationDto(), - validClientRegistrationDto() + validRegistrationInfo(), + validRegistrationInfo(), + validRegistrationInfo() )) .build(), - OAuth2ClientsDomainParams.builder() + OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( - DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(), - DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() + OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(), + OAuth2DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() )) .clientRegistrations(Lists.newArrayList( - validClientRegistrationDto(), - validClientRegistrationDto() + validRegistrationInfo(), + validRegistrationInfo() )) .build() )); - oAuth2Service.saveOAuth2Params(clientsParams); + oAuth2Service.saveOAuth2Info(oAuth2Info); - List secondDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "second-domain"); + List secondDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "second-domain", null); Assert.assertEquals(5, secondDomainHttpClients.size()); - clientsParams.setEnabled(false); - oAuth2Service.saveOAuth2Params(clientsParams); + oAuth2Info.setEnabled(false); + oAuth2Service.saveOAuth2Info(oAuth2Info); - List secondDomainHttpDisabledClients = oAuth2Service.getOAuth2Clients("http", "second-domain"); + List secondDomainHttpDisabledClients = oAuth2Service.getOAuth2Clients("http", "second-domain", null); Assert.assertEquals(0, secondDomainHttpDisabledClients.size()); } @Test - public void testFindAllClientRegistrationInfos() { - OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Lists.newArrayList( - OAuth2ClientsDomainParams.builder() + public void testFindAllRegistrations() { + OAuth2Info oAuth2Info = new OAuth2Info(true, Lists.newArrayList( + OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( - DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), - DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), - DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() + OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), + OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), + OAuth2DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() )) .clientRegistrations(Lists.newArrayList( - validClientRegistrationDto(), - validClientRegistrationDto(), - validClientRegistrationDto() + validRegistrationInfo(), + validRegistrationInfo(), + validRegistrationInfo() )) .build(), - OAuth2ClientsDomainParams.builder() + OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( - DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(), - DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() + OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(), + OAuth2DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() )) .clientRegistrations(Lists.newArrayList( - validClientRegistrationDto(), - validClientRegistrationDto() + validRegistrationInfo(), + validRegistrationInfo() )) .build(), - OAuth2ClientsDomainParams.builder() + OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( - DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTPS).build(), - DomainInfo.builder().name("fifth-domain").scheme(SchemeType.HTTP).build() + OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTPS).build(), + OAuth2DomainInfo.builder().name("fifth-domain").scheme(SchemeType.HTTP).build() )) .clientRegistrations(Lists.newArrayList( - validClientRegistrationDto() + validRegistrationInfo() )) .build() )); - oAuth2Service.saveOAuth2Params(clientsParams); - List foundClientRegistrationInfos = oAuth2Service.findAllClientRegistrationInfos(); - Assert.assertEquals(6, foundClientRegistrationInfos.size()); - clientsParams.getDomainsParams().stream() - .flatMap(domainParams -> domainParams.getClientRegistrations().stream()) - .forEach(clientRegistrationDto -> + oAuth2Service.saveOAuth2Info(oAuth2Info); + List foundRegistrations = oAuth2Service.findAllRegistrations(); + Assert.assertEquals(6, foundRegistrations.size()); + oAuth2Info.getOauth2ParamsInfos().stream() + .flatMap(paramsInfo -> paramsInfo.getClientRegistrations().stream()) + .forEach(registrationInfo -> Assert.assertTrue( - foundClientRegistrationInfos.stream() - .anyMatch(clientRegistrationInfo -> clientRegistrationInfo.getClientId().equals(clientRegistrationDto.getClientId())) + foundRegistrations.stream() + .anyMatch(registration -> registration.getClientId().equals(registrationInfo.getClientId())) ) ); } @Test - public void testFindClientRegistrationById() { - OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Lists.newArrayList( - OAuth2ClientsDomainParams.builder() + public void testFindRegistrationById() { + OAuth2Info oAuth2Info = new OAuth2Info(true, Lists.newArrayList( + OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( - DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), - DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), - DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() + OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), + OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), + OAuth2DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() )) .clientRegistrations(Lists.newArrayList( - validClientRegistrationDto(), - validClientRegistrationDto(), - validClientRegistrationDto() + validRegistrationInfo(), + validRegistrationInfo(), + validRegistrationInfo() )) .build(), - OAuth2ClientsDomainParams.builder() + OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( - DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(), - DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() + OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(), + OAuth2DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() )) .clientRegistrations(Lists.newArrayList( - validClientRegistrationDto(), - validClientRegistrationDto() + validRegistrationInfo(), + validRegistrationInfo() )) .build(), - OAuth2ClientsDomainParams.builder() + OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( - DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTPS).build(), - DomainInfo.builder().name("fifth-domain").scheme(SchemeType.HTTP).build() + OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTPS).build(), + OAuth2DomainInfo.builder().name("fifth-domain").scheme(SchemeType.HTTP).build() )) .clientRegistrations(Lists.newArrayList( - validClientRegistrationDto() + validRegistrationInfo() )) .build() )); - oAuth2Service.saveOAuth2Params(clientsParams); - List clientRegistrationInfos = oAuth2Service.findAllClientRegistrationInfos(); - clientRegistrationInfos.forEach(clientRegistrationInfo -> { - OAuth2ClientRegistrationInfo foundClientRegistrationInfo = oAuth2Service.findClientRegistrationInfo(clientRegistrationInfo.getUuidId()); - Assert.assertEquals(clientRegistrationInfo, foundClientRegistrationInfo); + oAuth2Service.saveOAuth2Info(oAuth2Info); + List foundRegistrations = oAuth2Service.findAllRegistrations(); + foundRegistrations.forEach(registration -> { + OAuth2Registration foundRegistration = oAuth2Service.findRegistration(registration.getUuidId()); + Assert.assertEquals(registration, foundRegistration); }); } - private OAuth2ClientsParams createDefaultClientsParams() { - return new OAuth2ClientsParams(true, Lists.newArrayList( - OAuth2ClientsDomainParams.builder() + @Test + public void testFindCallbackUrlScheme() { + OAuth2Info oAuth2Info = new OAuth2Info(true, Lists.newArrayList( + OAuth2ParamsInfo.builder() + .domainInfos(Lists.newArrayList( + OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), + OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), + OAuth2DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() + )) + .mobileInfos(Lists.newArrayList( + OAuth2MobileInfo.builder().pkgName("com.test.pkg1").callbackUrlScheme("testPkg1Callback").build(), + OAuth2MobileInfo.builder().pkgName("com.test.pkg2").callbackUrlScheme("testPkg2Callback").build() + )) + .clientRegistrations(Lists.newArrayList( + validRegistrationInfo(), + validRegistrationInfo(), + validRegistrationInfo() + )) + .build(), + OAuth2ParamsInfo.builder() + .domainInfos(Lists.newArrayList( + OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(), + OAuth2DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() + )) + .mobileInfos(Collections.emptyList()) + .clientRegistrations(Lists.newArrayList( + validRegistrationInfo(), + validRegistrationInfo() + )) + .build() + )); + oAuth2Service.saveOAuth2Info(oAuth2Info); + + OAuth2Info foundOAuth2Info = oAuth2Service.findOAuth2Info(); + Assert.assertEquals(oAuth2Info, foundOAuth2Info); + + List firstDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "first-domain", "com.test.pkg1"); + Assert.assertEquals(3, firstDomainHttpClients.size()); + for (OAuth2ClientInfo clientInfo : firstDomainHttpClients) { + String[] segments = clientInfo.getUrl().split("/"); + String registrationId = segments[segments.length-1]; + String callbackUrlScheme = oAuth2Service.findCallbackUrlScheme(UUID.fromString(registrationId), "com.test.pkg1"); + Assert.assertNotNull(callbackUrlScheme); + Assert.assertEquals("testPkg1Callback", callbackUrlScheme); + callbackUrlScheme = oAuth2Service.findCallbackUrlScheme(UUID.fromString(registrationId), "com.test.pkg2"); + Assert.assertNotNull(callbackUrlScheme); + Assert.assertEquals("testPkg2Callback", callbackUrlScheme); + callbackUrlScheme = oAuth2Service.findCallbackUrlScheme(UUID.fromString(registrationId), "com.test.pkg3"); + Assert.assertNull(callbackUrlScheme); + } + } + + private OAuth2Info createDefaultOAuth2Info() { + return new OAuth2Info(true, Lists.newArrayList( + OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( - DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), - DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), - DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() + OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), + OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), + OAuth2DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() )) + .mobileInfos(Collections.emptyList()) .clientRegistrations(Lists.newArrayList( - validClientRegistrationDto(), - validClientRegistrationDto(), - validClientRegistrationDto(), - validClientRegistrationDto() + validRegistrationInfo(), + validRegistrationInfo(), + validRegistrationInfo(), + validRegistrationInfo() )) .build(), - OAuth2ClientsDomainParams.builder() + OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( - DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), - DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() + OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), + OAuth2DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() )) + .mobileInfos(Collections.emptyList()) .clientRegistrations(Lists.newArrayList( - validClientRegistrationDto(), - validClientRegistrationDto() + validRegistrationInfo(), + validRegistrationInfo() )) .build() )); } - private ClientRegistrationDto validClientRegistrationDto() { - return ClientRegistrationDto.builder() + private OAuth2RegistrationInfo validRegistrationInfo() { + return OAuth2RegistrationInfo.builder() .clientId(UUID.randomUUID().toString()) .clientSecret(UUID.randomUUID().toString()) .authorizationUri(UUID.randomUUID().toString()) diff --git a/dao/src/test/resources/sql/hsql/drop-all-tables.sql b/dao/src/test/resources/sql/hsql/drop-all-tables.sql index 726b4ba412..f7b1b4118d 100644 --- a/dao/src/test/resources/sql/hsql/drop-all-tables.sql +++ b/dao/src/test/resources/sql/hsql/drop-all-tables.sql @@ -24,9 +24,13 @@ DROP TABLE IF EXISTS dashboard; DROP TABLE IF EXISTS rule_node_state; DROP TABLE IF EXISTS rule_node; DROP TABLE IF EXISTS rule_chain; +DROP TABLE IF EXISTS oauth2_mobile; +DROP TABLE IF EXISTS oauth2_domain; +DROP TABLE IF EXISTS oauth2_registration; +DROP TABLE IF EXISTS oauth2_params; +DROP TABLE IF EXISTS oauth2_client_registration_template; DROP TABLE IF EXISTS oauth2_client_registration; DROP TABLE IF EXISTS oauth2_client_registration_info; -DROP TABLE IF EXISTS oauth2_client_registration_template; DROP TABLE IF EXISTS api_usage_state; DROP TABLE IF EXISTS resource; DROP TABLE IF EXISTS ota_package; diff --git a/dao/src/test/resources/sql/psql/drop-all-tables.sql b/dao/src/test/resources/sql/psql/drop-all-tables.sql index 855a53df2d..a29dea43c2 100644 --- a/dao/src/test/resources/sql/psql/drop-all-tables.sql +++ b/dao/src/test/resources/sql/psql/drop-all-tables.sql @@ -25,9 +25,13 @@ DROP TABLE IF EXISTS rule_node_state; DROP TABLE IF EXISTS rule_node; DROP TABLE IF EXISTS rule_chain; DROP TABLE IF EXISTS tb_schema_settings; +DROP TABLE IF EXISTS oauth2_mobile; +DROP TABLE IF EXISTS oauth2_domain; +DROP TABLE IF EXISTS oauth2_registration; +DROP TABLE IF EXISTS oauth2_params; +DROP TABLE IF EXISTS oauth2_client_registration_template; DROP TABLE IF EXISTS oauth2_client_registration; DROP TABLE IF EXISTS oauth2_client_registration_info; -DROP TABLE IF EXISTS oauth2_client_registration_template; DROP TABLE IF EXISTS api_usage_state; DROP TABLE IF EXISTS resource; DROP TABLE IF EXISTS firmware; diff --git a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java index 8ad95805a5..14a008634b 100644 --- a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java +++ b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java @@ -103,7 +103,7 @@ import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate; -import org.thingsboard.server.common.data.oauth2.OAuth2ClientsParams; +import org.thingsboard.server.common.data.oauth2.OAuth2Info; import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.page.PageData; @@ -142,7 +142,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; @@ -1773,21 +1772,28 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { }).getBody(); } - public List getOAuth2Clients() { + public List getOAuth2Clients(String pkgName) { + Map params = new HashMap<>(); + StringBuilder urlBuilder = new StringBuilder(baseURL); + urlBuilder.append("/api/noauth/oauth2Clients"); + if (pkgName != null) { + urlBuilder.append("?pkgName={pkgName}"); + params.put("pkgName", pkgName); + } return restTemplate.exchange( - baseURL + "/api/noauth/oauth2Clients", + urlBuilder.toString(), HttpMethod.POST, HttpEntity.EMPTY, new ParameterizedTypeReference>() { - }).getBody(); + }, params).getBody(); } - public OAuth2ClientsParams getCurrentOAuth2Params() { - return restTemplate.getForEntity(baseURL + "/api/oauth2/config", OAuth2ClientsParams.class).getBody(); + public OAuth2Info getCurrentOAuth2Info() { + return restTemplate.getForEntity(baseURL + "/api/oauth2/config", OAuth2Info.class).getBody(); } - public OAuth2ClientsParams saveOAuth2Params(OAuth2ClientsParams oauth2Params) { - return restTemplate.postForEntity(baseURL + "/api/oauth2/config", oauth2Params, OAuth2ClientsParams.class).getBody(); + public OAuth2Info saveOAuth2Info(OAuth2Info oauth2Info) { + return restTemplate.postForEntity(baseURL + "/api/oauth2/config", oauth2Info, OAuth2Info.class).getBody(); } public String getLoginProcessingUrl() { diff --git a/ui-ngx/src/app/core/auth/auth.service.ts b/ui-ngx/src/app/core/auth/auth.service.ts index c17d993bf7..68724fa0dc 100644 --- a/ui-ngx/src/app/core/auth/auth.service.ts +++ b/ui-ngx/src/app/core/auth/auth.service.ts @@ -45,7 +45,7 @@ import { ActionNotificationShow } from '@core/notification/notification.actions' import { MatDialog, MatDialogConfig } from '@angular/material/dialog'; import { AlertDialogComponent } from '@shared/components/dialog/alert-dialog.component'; import { OAuth2ClientInfo } from '@shared/models/oauth2.models'; -import { isMobileApp } from '@core/utils'; +import { isDefinedAndNotNull, isMobileApp } from '@core/utils'; @Injectable({ providedIn: 'root' @@ -204,8 +204,12 @@ export class AuthService { } } - public loadOAuth2Clients(): Observable> { - return this.http.post>(`/api/noauth/oauth2Clients`, + public loadOAuth2Clients(pkgName?: string): Observable> { + let url = '/api/noauth/oauth2Clients'; + if (isDefinedAndNotNull(pkgName)) { + url += `?pkgName=${pkgName}`; + } + return this.http.post>(url, null, defaultHttpOptions()).pipe( catchError(err => of([])), tap((OAuth2Clients) => { diff --git a/ui-ngx/src/app/core/http/oauth2.service.ts b/ui-ngx/src/app/core/http/oauth2.service.ts index e81d472130..72a6bc77af 100644 --- a/ui-ngx/src/app/core/http/oauth2.service.ts +++ b/ui-ngx/src/app/core/http/oauth2.service.ts @@ -18,7 +18,7 @@ import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { defaultHttpOptionsFromConfig, RequestConfig } from '@core/http/http-utils'; import { Observable } from 'rxjs'; -import { OAuth2ClientRegistrationTemplate, OAuth2ClientsParams } from '@shared/models/oauth2.models'; +import { OAuth2ClientRegistrationTemplate, OAuth2Info } from '@shared/models/oauth2.models'; @Injectable({ providedIn: 'root' @@ -29,16 +29,16 @@ export class OAuth2Service { private http: HttpClient ) { } - public getOAuth2Settings(config?: RequestConfig): Observable { - return this.http.get(`/api/oauth2/config`, defaultHttpOptionsFromConfig(config)); + public getOAuth2Settings(config?: RequestConfig): Observable { + return this.http.get(`/api/oauth2/config`, defaultHttpOptionsFromConfig(config)); } public getOAuth2Template(config?: RequestConfig): Observable> { return this.http.get>(`/api/oauth2/config/template`, defaultHttpOptionsFromConfig(config)); } - public saveOAuth2Settings(OAuth2Setting: OAuth2ClientsParams, config?: RequestConfig): Observable { - return this.http.post('/api/oauth2/config', OAuth2Setting, + public saveOAuth2Settings(OAuth2Setting: OAuth2Info, config?: RequestConfig): Observable { + return this.http.post('/api/oauth2/config', OAuth2Setting, defaultHttpOptionsFromConfig(config)); } diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html index 3998b4fc9a..3ea9a1ca11 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html @@ -34,19 +34,19 @@ {{ 'admin.oauth2.enable' | translate }}
- +
- + - {{ domainListTittle(domain) }} + {{ domainListTittle(oauth2ParamsInfo) }} + + + + + + + +
- - {{ 'admin.domain-name-unique' | translate }} - - -
- - admin.oauth2.redirect-uri-template - - - - - - - -
+
+
+ +
+ + + + +
+
+ admin.oauth2.no-mobile-apps +
+
+
+
+
+ + admin.oauth2.mobile-package + + + + {{ 'admin.oauth2.mobile-package-unique' | translate }} + +
+ + admin.oauth2.mobile-callback-url-scheme + + +
+
+ - +
- - -
-
+
+
- - -
- -
-
- + +
+ +
admin.oauth2.providers
- @@ -140,8 +190,8 @@ @@ -467,7 +517,7 @@ diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.scss b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.scss index e17d7c34e4..aeb401dedf 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.scss +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.scss @@ -62,7 +62,7 @@ } } } - .domains-list{ + .domains-list, .apps-list { margin-bottom: 1.5em; .mat-form-field-suffix .mat-icon-button .mat-icon{ font-size: 24px; diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts index e93ea168fe..39fb6ac6ff 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts @@ -18,8 +18,6 @@ import { Component, Inject, OnDestroy, OnInit } from '@angular/core'; import { AbstractControl, FormArray, FormBuilder, FormGroup, ValidationErrors, Validators } from '@angular/forms'; import { ClientAuthenticationMethod, - ClientRegistration, - DomainInfo, DomainSchema, domainSchemaTranslations, MapperConfig, @@ -27,8 +25,10 @@ import { MapperConfigCustom, MapperConfigType, OAuth2ClientRegistrationTemplate, - OAuth2ClientsDomainParams, - OAuth2ClientsParams, + OAuth2DomainInfo, + OAuth2Info, OAuth2MobileInfo, + OAuth2ParamsInfo, + OAuth2RegistrationInfo, TenantNameStrategy } from '@shared/models/oauth2.models'; import { Store } from '@ngrx/store'; @@ -52,6 +52,20 @@ import { ActivatedRoute } from '@angular/router'; }) export class OAuth2SettingsComponent extends PageComponent implements OnInit, HasConfirmForm, OnDestroy { + constructor(protected store: Store, + private route: ActivatedRoute, + private oauth2Service: OAuth2Service, + private fb: FormBuilder, + private dialogService: DialogService, + private translate: TranslateService, + @Inject(WINDOW) private window: Window) { + super(store); + } + + get oauth2ParamsInfos(): FormArray { + return this.oauth2SettingsForm.get('oauth2ParamsInfos') as FormArray; + } + private URL_REGEXP = /^[A-Za-z][A-Za-z\d.+-]*:\/*(?:\w+(?::\w+)?@)?[^\s/]+(?::\d+)?(?:\/[\w#!:.,?+=&%@\-/]*)?$/; private DOMAIN_AND_PORT_REGEXP = /^(?:\w+(?::\w+)?@)?[^\s/]+(?::\d+)?$/; private subscriptions: Subscription[] = []; @@ -77,7 +91,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha readonly separatorKeysCodes: number[] = [ENTER, COMMA]; oauth2SettingsForm: FormGroup; - auth2ClientsParams: OAuth2ClientsParams; + oauth2Info: OAuth2Info; clientAuthenticationMethods = Object.keys(ClientAuthenticationMethod); mapperConfigType = MapperConfigType; @@ -90,14 +104,14 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha private loginProcessingUrl: string = this.route.snapshot.data.loginProcessingUrl; - constructor(protected store: Store, - private route: ActivatedRoute, - private oauth2Service: OAuth2Service, - private fb: FormBuilder, - private dialogService: DialogService, - private translate: TranslateService, - @Inject(WINDOW) private window: Window) { - super(store); + private static validateScope(control: AbstractControl): ValidationErrors | null { + const scope: string[] = control.value; + if (!scope || !scope.length) { + return { + required: true + }; + } + return null; } ngOnInit(): void { @@ -106,10 +120,10 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha this.oauth2Service.getOAuth2Template(), this.oauth2Service.getOAuth2Settings() ]).subscribe( - ([templates, auth2ClientsParams]) => { + ([templates, oauth2Info]) => { this.initTemplates(templates); - this.auth2ClientsParams = auth2ClientsParams; - this.initOAuth2Settings(this.auth2ClientsParams); + this.oauth2Info = oauth2Info; + this.initOAuth2Settings(this.oauth2Info); } ); } @@ -130,11 +144,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha this.templateProvider.sort(); } - get domainsParams(): FormArray { - return this.oauth2SettingsForm.get('domainsParams') as FormArray; - } - - private formBasicGroup(type: MapperConfigType, mapperConfigBasic?: MapperConfigBasic): FormGroup { + private formBasicGroup(mapperConfigBasic?: MapperConfigBasic): FormGroup { let tenantNamePattern; if (mapperConfigBasic?.tenantNamePattern) { tenantNamePattern = mapperConfigBasic.tenantNamePattern; @@ -173,16 +183,16 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha private buildOAuth2SettingsForm(): void { this.oauth2SettingsForm = this.fb.group({ - domainsParams: this.fb.array([]), + oauth2ParamsInfos: this.fb.array([]), enabled: [false] }); } - private initOAuth2Settings(auth2ClientsParams: OAuth2ClientsParams): void { - if (auth2ClientsParams) { - this.oauth2SettingsForm.patchValue({enabled: auth2ClientsParams.enabled}, {emitEvent: false}); - auth2ClientsParams.domainsParams.forEach((domain) => { - this.domainsParams.push(this.buildDomainsForm(domain)); + private initOAuth2Settings(oauth2Info: OAuth2Info): void { + if (oauth2Info) { + this.oauth2SettingsForm.patchValue({enabled: oauth2Info.enabled}, {emitEvent: false}); + oauth2Info.oauth2ParamsInfos.forEach((oauth2ParamsInfo) => { + this.oauth2ParamsInfos.push(this.buildOAuth2ParamsInfoForm(oauth2ParamsInfo)); }); } } @@ -201,8 +211,20 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha return null; } + private uniquePkgNameValidator(control: FormGroup): { [key: string]: boolean } | null { + if (control.parent?.value) { + const pkgName = control.value.pkgName; + const mobileInfosList = control.parent.getRawValue() + .filter((mobileInfo) => mobileInfo.pkgName === pkgName); + if (mobileInfosList.length > 1) { + return {unique: true}; + } + } + return null; + } + public domainListTittle(control: AbstractControl): string { - const domainInfos = control.get('domainInfos').value as DomainInfo[]; + const domainInfos = control.get('domainInfos').value as OAuth2DomainInfo[]; if (domainInfos.length) { const domainList = new Set(); domainInfos.forEach((domain) => { @@ -213,41 +235,51 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha return this.translate.instant('admin.oauth2.new-domain'); } - private buildDomainsForm(auth2ClientsDomainParams?: OAuth2ClientsDomainParams): FormGroup { - const formDomain = this.fb.group({ + private buildOAuth2ParamsInfoForm(oauth2ParamsInfo?: OAuth2ParamsInfo): FormGroup { + const formOAuth2Params = this.fb.group({ domainInfos: this.fb.array([], Validators.required), + mobileInfos: this.fb.array([]), clientRegistrations: this.fb.array([], Validators.required) }); - if (auth2ClientsDomainParams) { - auth2ClientsDomainParams.domainInfos.forEach((domain) => { - this.clientDomainInfos(formDomain).push(this.buildDomainForm(domain)); + if (oauth2ParamsInfo) { + oauth2ParamsInfo.domainInfos.forEach((domain) => { + this.domainInfos(formOAuth2Params).push(this.buildDomainInfoForm(domain)); + }); + oauth2ParamsInfo.mobileInfos.forEach((mobile) => { + this.mobileInfos(formOAuth2Params).push(this.buildMobileInfoForm(mobile)); }); - auth2ClientsDomainParams.clientRegistrations.forEach((registration) => { - this.clientDomainProviders(formDomain).push(this.buildProviderForm(registration)); + oauth2ParamsInfo.clientRegistrations.forEach((registration) => { + this.clientRegistrations(formOAuth2Params).push(this.buildRegistrationForm(registration)); }); } else { - this.clientDomainProviders(formDomain).push(this.buildProviderForm()); - this.clientDomainInfos(formDomain).push(this.buildDomainForm()); + this.clientRegistrations(formOAuth2Params).push(this.buildRegistrationForm()); + this.domainInfos(formOAuth2Params).push(this.buildDomainInfoForm()); } - return formDomain; + return formOAuth2Params; } - private buildDomainForm(domainInfo?: DomainInfo): FormGroup { - const domain = this.fb.group({ + private buildDomainInfoForm(domainInfo?: OAuth2DomainInfo): FormGroup { + return this.fb.group({ name: [domainInfo ? domainInfo.name : this.window.location.hostname, [ Validators.required, Validators.pattern(this.DOMAIN_AND_PORT_REGEXP)]], scheme: [domainInfo?.scheme ? domainInfo.scheme : DomainSchema.HTTPS, Validators.required] }, {validators: this.uniqueDomainValidator}); - return domain; } - private buildProviderForm(clientRegistration?: ClientRegistration): FormGroup { + private buildMobileInfoForm(mobileInfo?: OAuth2MobileInfo): FormGroup { + return this.fb.group({ + pkgName: [mobileInfo?.pkgName, [Validators.required]], + callbackUrlScheme: [mobileInfo?.callbackUrlScheme, [Validators.required]], + }, {validators: this.uniquePkgNameValidator}); + } + + private buildRegistrationForm(registration?: OAuth2RegistrationInfo): FormGroup { let additionalInfo = null; - if (isDefinedAndNotNull(clientRegistration?.additionalInfo)) { - additionalInfo = clientRegistration.additionalInfo; + if (isDefinedAndNotNull(registration?.additionalInfo)) { + additionalInfo = registration.additionalInfo; if (this.templateProvider.indexOf(additionalInfo.providerName) === -1) { additionalInfo.providerName = 'Custom'; } @@ -261,43 +293,43 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha additionalInfo: this.fb.group({ providerName: [additionalInfo?.providerName ? additionalInfo?.providerName : defaultProviderName, Validators.required] }), - loginButtonLabel: [clientRegistration?.loginButtonLabel ? clientRegistration.loginButtonLabel : null, Validators.required], - loginButtonIcon: [clientRegistration?.loginButtonIcon ? clientRegistration.loginButtonIcon : null], - clientId: [clientRegistration?.clientId ? clientRegistration.clientId : '', Validators.required], - clientSecret: [clientRegistration?.clientSecret ? clientRegistration.clientSecret : '', Validators.required], - accessTokenUri: [clientRegistration?.accessTokenUri ? clientRegistration.accessTokenUri : '', + loginButtonLabel: [registration?.loginButtonLabel ? registration.loginButtonLabel : null, Validators.required], + loginButtonIcon: [registration?.loginButtonIcon ? registration.loginButtonIcon : null], + clientId: [registration?.clientId ? registration.clientId : '', Validators.required], + clientSecret: [registration?.clientSecret ? registration.clientSecret : '', Validators.required], + accessTokenUri: [registration?.accessTokenUri ? registration.accessTokenUri : '', [Validators.required, Validators.pattern(this.URL_REGEXP)]], - authorizationUri: [clientRegistration?.authorizationUri ? clientRegistration.authorizationUri : '', + authorizationUri: [registration?.authorizationUri ? registration.authorizationUri : '', [Validators.required, Validators.pattern(this.URL_REGEXP)]], - scope: this.fb.array(clientRegistration?.scope ? clientRegistration.scope : [], this.validateScope), - jwkSetUri: [clientRegistration?.jwkSetUri ? clientRegistration.jwkSetUri : '', Validators.pattern(this.URL_REGEXP)], - userInfoUri: [clientRegistration?.userInfoUri ? clientRegistration.userInfoUri : '', + scope: this.fb.array(registration?.scope ? registration.scope : [], OAuth2SettingsComponent.validateScope), + jwkSetUri: [registration?.jwkSetUri ? registration.jwkSetUri : '', Validators.pattern(this.URL_REGEXP)], + userInfoUri: [registration?.userInfoUri ? registration.userInfoUri : '', [Validators.required, Validators.pattern(this.URL_REGEXP)]], clientAuthenticationMethod: [ - clientRegistration?.clientAuthenticationMethod ? clientRegistration.clientAuthenticationMethod : ClientAuthenticationMethod.POST, + registration?.clientAuthenticationMethod ? registration.clientAuthenticationMethod : ClientAuthenticationMethod.POST, Validators.required], userNameAttributeName: [ - clientRegistration?.userNameAttributeName ? clientRegistration.userNameAttributeName : 'email', Validators.required], + registration?.userNameAttributeName ? registration.userNameAttributeName : 'email', Validators.required], mapperConfig: this.fb.group({ allowUserCreation: [ - isDefinedAndNotNull(clientRegistration?.mapperConfig?.allowUserCreation) ? - clientRegistration.mapperConfig.allowUserCreation : true + isDefinedAndNotNull(registration?.mapperConfig?.allowUserCreation) ? + registration.mapperConfig.allowUserCreation : true ], activateUser: [ - isDefinedAndNotNull(clientRegistration?.mapperConfig?.activateUser) ? clientRegistration.mapperConfig.activateUser : false + isDefinedAndNotNull(registration?.mapperConfig?.activateUser) ? registration.mapperConfig.activateUser : false ], type: [ - clientRegistration?.mapperConfig?.type ? clientRegistration.mapperConfig.type : MapperConfigType.BASIC, Validators.required + registration?.mapperConfig?.type ? registration.mapperConfig.type : MapperConfigType.BASIC, Validators.required ] } ) }); - if (clientRegistration) { - this.changeMapperConfigType(clientRegistrationFormGroup, clientRegistration.mapperConfig.type, clientRegistration.mapperConfig); + if (registration) { + this.changeMapperConfigType(clientRegistrationFormGroup, registration.mapperConfig.type, registration.mapperConfig); } else { this.changeMapperConfigType(clientRegistrationFormGroup, MapperConfigType.BASIC); this.setProviderDefaultValue(defaultProviderName, clientRegistrationFormGroup); @@ -315,16 +347,6 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha return clientRegistrationFormGroup; } - private validateScope(control: AbstractControl): ValidationErrors | null { - const scope: string[] = control.value; - if (!scope || !scope.length) { - return { - required: true - }; - } - return null; - } - private setProviderDefaultValue(provider: string, clientRegistration: FormGroup) { if (provider === 'Custom') { clientRegistration.reset(this.defaultProvider, {emitEvent: false}); @@ -355,7 +377,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha } else { mapperConfig.removeControl('custom'); if (!mapperConfig.get('basic')) { - mapperConfig.addControl('basic', this.formBasicGroup(type, predefinedValue?.basic)); + mapperConfig.addControl('basic', this.formBasicGroup(predefinedValue?.basic)); } if (type === MapperConfigType.GITHUB) { mapperConfig.get('basic.emailAttributeKey').disable(); @@ -370,7 +392,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha const setting = this.oauth2SettingsForm.getRawValue(); this.oauth2Service.saveOAuth2Settings(setting).subscribe( (oauth2Settings) => { - this.auth2ClientsParams = oauth2Settings; + this.oauth2Info = oauth2Settings; this.oauth2SettingsForm.patchValue(this.oauth2SettingsForm, {emitEvent: false}); this.oauth2SettingsForm.markAsUntouched(); this.oauth2SettingsForm.markAsPristine(); @@ -403,58 +425,62 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha controller.markAsDirty(); } - addDomain(): void { - this.domainsParams.push(this.buildDomainsForm()); + addOAuth2ParamsInfo(): void { + this.oauth2ParamsInfos.push(this.buildOAuth2ParamsInfoForm()); } - deleteDomain($event: Event, index: number): void { + deleteOAuth2ParamsInfo($event: Event, index: number): void { if ($event) { $event.stopPropagation(); $event.preventDefault(); } - const domainName = this.domainListTittle(this.domainsParams.at(index)); + const domainName = this.domainListTittle(this.oauth2ParamsInfos.at(index)); this.dialogService.confirm( this.translate.instant('admin.oauth2.delete-domain-title', {domainName: domainName || ''}), this.translate.instant('admin.oauth2.delete-domain-text'), null, this.translate.instant('action.delete') ).subscribe((data) => { if (data) { - this.domainsParams.removeAt(index); - this.domainsParams.markAsTouched(); - this.domainsParams.markAsDirty(); + this.oauth2ParamsInfos.removeAt(index); + this.oauth2ParamsInfos.markAsTouched(); + this.oauth2ParamsInfos.markAsDirty(); } }); } - clientDomainProviders(control: AbstractControl): FormArray { + clientRegistrations(control: AbstractControl): FormArray { return control.get('clientRegistrations') as FormArray; } - clientDomainInfos(control: AbstractControl): FormArray { + domainInfos(control: AbstractControl): FormArray { return control.get('domainInfos') as FormArray; } - addProvider(control: AbstractControl): void { - this.clientDomainProviders(control).push(this.buildProviderForm()); + mobileInfos(control: AbstractControl): FormArray { + return control.get('mobileInfos') as FormArray; } - deleteProvider($event: Event, control: AbstractControl, index: number): void { + addRegistration(control: AbstractControl): void { + this.clientRegistrations(control).push(this.buildRegistrationForm()); + } + + deleteRegistration($event: Event, control: AbstractControl, index: number): void { if ($event) { $event.stopPropagation(); $event.preventDefault(); } - const providerName = this.clientDomainProviders(control).at(index).get('additionalInfo.providerName').value; + const providerName = this.clientRegistrations(control).at(index).get('additionalInfo.providerName').value; this.dialogService.confirm( this.translate.instant('admin.oauth2.delete-registration-title', {name: providerName || ''}), this.translate.instant('admin.oauth2.delete-registration-text'), null, this.translate.instant('action.delete') ).subscribe((data) => { if (data) { - this.clientDomainProviders(control).removeAt(index); - this.clientDomainProviders(control).markAsTouched(); - this.clientDomainProviders(control).markAsDirty(); + this.clientRegistrations(control).removeAt(index); + this.clientRegistrations(control).markAsTouched(); + this.clientRegistrations(control).markAsDirty(); } }); } @@ -480,24 +506,41 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha } addDomainInfo(control: AbstractControl): void { - this.clientDomainInfos(control).push(this.buildDomainForm({ + this.domainInfos(control).push(this.buildDomainInfoForm({ name: '', scheme: DomainSchema.HTTPS })); } - removeDomain($event: Event, control: AbstractControl, index: number): void { + removeDomainInfo($event: Event, control: AbstractControl, index: number): void { + if ($event) { + $event.stopPropagation(); + $event.preventDefault(); + } + this.domainInfos(control).removeAt(index); + this.domainInfos(control).markAsTouched(); + this.domainInfos(control).markAsDirty(); + } + + addMobileInfo(control: AbstractControl): void { + this.mobileInfos(control).push(this.buildMobileInfoForm({ + pkgName: '', + callbackUrlScheme: '' + })); + } + + removeMobileInfo($event: Event, control: AbstractControl, index: number): void { if ($event) { $event.stopPropagation(); $event.preventDefault(); } - this.clientDomainInfos(control).removeAt(index); - this.clientDomainInfos(control).markAsTouched(); - this.clientDomainInfos(control).markAsDirty(); + this.mobileInfos(control).removeAt(index); + this.mobileInfos(control).markAsTouched(); + this.mobileInfos(control).markAsDirty(); } redirectURI(control: AbstractControl, schema?: DomainSchema): string { - const domainInfo = control.value as DomainInfo; + const domainInfo = control.value as OAuth2DomainInfo; if (domainInfo.name !== '') { let protocol; if (isDefined(schema)) { diff --git a/ui-ngx/src/app/shared/models/oauth2.models.ts b/ui-ngx/src/app/shared/models/oauth2.models.ts index 08ff402502..93b147c887 100644 --- a/ui-ngx/src/app/shared/models/oauth2.models.ts +++ b/ui-ngx/src/app/shared/models/oauth2.models.ts @@ -16,21 +16,27 @@ import { HasUUID } from '@shared/models/id/has-uuid'; -export interface OAuth2ClientsParams { +export interface OAuth2Info { enabled: boolean; - domainsParams: OAuth2ClientsDomainParams[]; + oauth2ParamsInfos: OAuth2ParamsInfo[]; } -export interface OAuth2ClientsDomainParams { - clientRegistrations: ClientRegistration[]; - domainInfos: DomainInfo[]; +export interface OAuth2ParamsInfo { + clientRegistrations: OAuth2RegistrationInfo[]; + domainInfos: OAuth2DomainInfo[]; + mobileInfos: OAuth2MobileInfo[]; } -export interface DomainInfo { +export interface OAuth2DomainInfo { name: string; scheme: DomainSchema; } +export interface OAuth2MobileInfo { + pkgName: string; + callbackUrlScheme: string; +} + export enum DomainSchema{ HTTP = 'HTTP', HTTPS = 'HTTPS', @@ -57,7 +63,7 @@ export enum TenantNameStrategy{ CUSTOM = 'CUSTOM' } -export interface OAuth2ClientRegistrationTemplate extends ClientRegistration{ +export interface OAuth2ClientRegistrationTemplate extends OAuth2RegistrationInfo{ comment: string; createdTime: number; helpLink: string; @@ -66,7 +72,7 @@ export interface OAuth2ClientRegistrationTemplate extends ClientRegistration{ id: HasUUID; } -export interface ClientRegistration { +export interface OAuth2RegistrationInfo { loginButtonLabel: string; loginButtonIcon: string; clientId: string; diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index aabd4207d8..5fbb6ed3fb 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -219,7 +219,16 @@ "domain-schema-http": "HTTP", "domain-schema-https": "HTTPS", "domain-schema-mixed": "HTTP+HTTPS", - "enable": "Enable OAuth2 settings" + "enable": "Enable OAuth2 settings", + "domains": "Domains", + "mobile-apps": "Mobile applications", + "no-mobile-apps": "No applications configured", + "mobile-package": "Application package", + "mobile-package-unique": "Application package must be unique.", + "mobile-callback-url-scheme": "Callback URL scheme", + "add-mobile-app": "Add application", + "delete-mobile-app": "Delete application info", + "providers": "Providers" } }, "alarm": { From bb5ac4954209591039a8e5306b3878c48b806fc5 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Thu, 10 Jun 2021 11:53:10 +0300 Subject: [PATCH 05/11] UI: Device wizard - move transport type selector to step Transport configuration --- .../device-wizard-dialog.component.html | 34 ++++++++----------- .../wizard/device-wizard-dialog.component.ts | 18 +++------- 2 files changed, 20 insertions(+), 32 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html b/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html index 39be34b823..54cf110d5b 100644 --- a/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html @@ -48,20 +48,6 @@ device.label - - device-profile.transport-type - - - {{deviceTransportTypeTranslations.get(type) | translate}} - - - - {{deviceTransportTypeHints.get(deviceWizardFormGroup.get('transportType').value) | translate}} - - - {{ 'device-profile.transport-type-required' | translate }} - -
@@ -74,13 +60,10 @@
- +
- {{ 'device-profile.transport-configuration' | translate }} + {{ 'device-profile.transport-configuration' | translate }} + device-profile.transport-type + + + {{deviceTransportTypeTranslations.get(type) | translate}} + + + + {{deviceTransportTypeHints.get(transportConfigFormGroup.get('transportType').value) | translate}} + + + {{ 'device-profile.transport-type-required' | translate }} + + diff --git a/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.ts b/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.ts index 4e2b5b2ff8..b9c38d24dd 100644 --- a/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.ts @@ -29,7 +29,6 @@ import { DeviceProvisionConfiguration, DeviceProvisionType, DeviceTransportType, - deviceTransportTypeConfigurationInfoMap, deviceTransportTypeHintMap, deviceTransportTypeTranslationMap } from '@shared/models/device.models'; @@ -66,7 +65,6 @@ export class DeviceWizardDialogComponent extends showNext = true; createProfile = false; - createTransportConfiguration = false; entityType = EntityType; @@ -88,7 +86,7 @@ export class DeviceWizardDialogComponent extends customerFormGroup: FormGroup; - labelPosition = 'end'; + labelPosition: MatHorizontalStepper['labelPosition'] = 'end'; serviceType = ServiceType.TB_RULE_ENGINE; @@ -109,7 +107,6 @@ export class DeviceWizardDialogComponent extends label: [''], gateway: [false], overwriteActivityTime: [false], - transportType: [DeviceTransportType.DEFAULT, Validators.required], addProfileType: [0], deviceProfileId: [null, Validators.required], newDeviceProfileTitle: [{value: null, disabled: true}], @@ -130,7 +127,6 @@ export class DeviceWizardDialogComponent extends this.deviceWizardFormGroup.get('defaultQueueName').disable(); this.deviceWizardFormGroup.updateValueAndValidity(); this.createProfile = false; - this.createTransportConfiguration = false; } else { this.deviceWizardFormGroup.get('deviceProfileId').setValidators(null); this.deviceWizardFormGroup.get('deviceProfileId').disable(); @@ -141,18 +137,18 @@ export class DeviceWizardDialogComponent extends this.deviceWizardFormGroup.updateValueAndValidity(); this.createProfile = true; - this.createTransportConfiguration = this.deviceWizardFormGroup.get('transportType').value && - deviceTransportTypeConfigurationInfoMap.get(this.deviceWizardFormGroup.get('transportType').value).hasProfileConfiguration; } } )); this.transportConfigFormGroup = this.fb.group( { + transportType: [DeviceTransportType.DEFAULT, Validators.required], transportConfiguration: [createDeviceProfileTransportConfiguration(DeviceTransportType.DEFAULT), Validators.required] } ); - this.subscriptions.push(this.deviceWizardFormGroup.get('transportType').valueChanges.subscribe((transportType) => { + + this.subscriptions.push(this.transportConfigFormGroup.get('transportType').valueChanges.subscribe((transportType) => { this.deviceProfileTransportTypeChanged(transportType); })); @@ -229,8 +225,6 @@ export class DeviceWizardDialogComponent extends if (index > 0) { if (!this.createProfile) { index += 3; - } else if (!this.createTransportConfiguration) { - index += 1; } } switch (index) { @@ -256,8 +250,6 @@ export class DeviceWizardDialogComponent extends private deviceProfileTransportTypeChanged(deviceTransportType: DeviceTransportType): void { this.transportConfigFormGroup.patchValue( {transportConfiguration: createDeviceProfileTransportConfiguration(deviceTransportType)}); - this.createTransportConfiguration = this.createProfile && deviceTransportType && - deviceTransportTypeConfigurationInfoMap.get(deviceTransportType).hasProfileConfiguration; } add(): void { @@ -281,7 +273,7 @@ export class DeviceWizardDialogComponent extends const deviceProfile: DeviceProfile = { name: this.deviceWizardFormGroup.get('newDeviceProfileTitle').value, type: DeviceProfileType.DEFAULT, - transportType: this.deviceWizardFormGroup.get('transportType').value, + transportType: this.transportConfigFormGroup.get('transportType').value, provisionType: deviceProvisionConfiguration.type, provisionDeviceKey, profileData: { From 308650a308aa583dffc44d37cb1930ac747560e7 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Wed, 9 Jun 2021 19:21:40 +0300 Subject: [PATCH 06/11] LWM2M: front add coap resource URI --- .../transport/coap/CoapTransportResource.java | 21 ++- .../DefaultLwM2MTransportMsgHandler.java | 8 +- .../server/LwM2mTransportCoapResource.java | 38 +++--- .../server/client/LwM2mClientContextImpl.java | 16 ++- .../lwm2m/server/client/LwM2mFwSwUpdate.java | 11 +- ...ile-transport-configuration.component.html | 128 +++++++++++++----- ...ofile-transport-configuration.component.ts | 80 ++++++++++- .../lwm2m/lwm2m-profile-config.models.ts | 15 +- .../assets/locale/locale.constant-en_US.json | 5 +- 9 files changed, 246 insertions(+), 76 deletions(-) diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java index cd80aa42df..a1a2a4b656 100644 --- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java @@ -398,7 +398,7 @@ public class CoapTransportResource extends AbstractCoapTransportResource { private Optional decodeCredentials(Request request) { List uriPath = request.getOptions().getUriPath(); - if (uriPath.size() > ACCESS_TOKEN_POSITION) { + if (uriPath.size() >= ACCESS_TOKEN_POSITION) { return Optional.of(new DeviceTokenCredentials(uriPath.get(ACCESS_TOKEN_POSITION - 1))); } else { return Optional.empty(); @@ -482,13 +482,16 @@ public class CoapTransportResource extends AbstractCoapTransportResource { String title = exchange.getQueryParameter("title"); String version = exchange.getQueryParameter("version"); if (msg.getResponseStatus().equals(TransportProtos.ResponseStatus.SUCCESS)) { + String firmwareId = new UUID(msg.getOtaPackageIdMSB(), msg.getOtaPackageIdLSB()).toString(); if (msg.getTitle().equals(title) && msg.getVersion().equals(version)) { - String firmwareId = new UUID(msg.getOtaPackageIdMSB(), msg.getOtaPackageIdLSB()).toString(); String strChunkSize = exchange.getQueryParameter("size"); String strChunk = exchange.getQueryParameter("chunk"); int chunkSize = StringUtils.isEmpty(strChunkSize) ? 0 : Integer.parseInt(strChunkSize); int chunk = StringUtils.isEmpty(strChunk) ? 0 : Integer.parseInt(strChunk); exchange.respond(CoAP.ResponseCode.CONTENT, transportContext.getOtaPackageDataCache().get(firmwareId, chunkSize, chunk)); + } + else if (firmwareId != null) { + sendOtaData(exchange, firmwareId); } else { exchange.respond(CoAP.ResponseCode.BAD_REQUEST); } @@ -504,6 +507,20 @@ public class CoapTransportResource extends AbstractCoapTransportResource { } } + private void sendOtaData(CoapExchange exchange, String firmwareId) { + Response response = new Response(CoAP.ResponseCode.CONTENT); + byte[] fwData = transportContext.getOtaPackageDataCache().get(firmwareId); + if (fwData != null && fwData.length > 0) { + response.setPayload(fwData); + if (exchange.getRequestOptions().getBlock2() != null) { + int chunkSize = exchange.getRequestOptions().getBlock2().getSzx(); + boolean moreFlag = fwData.length > chunkSize; + response.getOptions().setBlock2(chunkSize, moreFlag, 0); + } + exchange.respond(response); + } + } + private static class CoapSessionListener implements SessionMsgListener { private final CoapTransportResource coapTransportResource; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java index 4f97273cb6..bff3b9d60d 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java @@ -41,10 +41,10 @@ import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.server.cache.ota.OtaPackageDataCache; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.ota.OtaPackageKey; import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.ota.OtaPackageUtil; -import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.transport.TransportService; import org.thingsboard.server.common.transport.TransportServiceCallback; import org.thingsboard.server.common.transport.adaptor.AdaptorException; @@ -89,11 +89,9 @@ import java.util.stream.Collectors; import static org.eclipse.californium.core.coap.CoAP.ResponseCode.BAD_REQUEST; import static org.eclipse.leshan.core.attributes.Attribute.OBJECT_VERSION; -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADED; +import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.FAILED; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.INITIATED; -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATING; -import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper.getValueFromKvProto; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.DEVICE_ATTRIBUTES_REQUEST; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_5_ID; @@ -267,8 +265,8 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler clientContext.unregister(client, registration); SessionInfoProto sessionInfo = client.getSession(); if (sessionInfo != null) { - transportService.deregisterSession(sessionInfo); this.doCloseSession(sessionInfo); + transportService.deregisterSession(sessionInfo); sessionStore.remove(registration.getEndpoint()); log.info("Client close session: [{}] unReg [{}] name [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType()); } else { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java index 4e45b172cb..1236c8b6d2 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java @@ -69,10 +69,10 @@ public class LwM2mTransportCoapResource extends AbstractLwM2mTransportResource { @Override protected void processHandleGet(CoapExchange exchange) { log.warn("90) processHandleGet [{}]", exchange); - if (exchange.getRequestOptions().getUriPath().size() == 2 && - (FIRMWARE_UPDATE_COAP_RECOURSE.equals(exchange.getRequestOptions().getUriPath().get(0)) || - SOFTWARE_UPDATE_COAP_RECOURSE.equals(exchange.getRequestOptions().getUriPath().get(0)))) { - this.sentOtaData(exchange); + if (exchange.getRequestOptions().getUriPath().size() >= 2 && + (FIRMWARE_UPDATE_COAP_RECOURSE.equals(exchange.getRequestOptions().getUriPath().get(exchange.getRequestOptions().getUriPath().size()-2)) || + SOFTWARE_UPDATE_COAP_RECOURSE.equals(exchange.getRequestOptions().getUriPath().get(exchange.getRequestOptions().getUriPath().size()-2)))) { + this.sendOtaData(exchange); } } @@ -129,23 +129,25 @@ public class LwM2mTransportCoapResource extends AbstractLwM2mTransportResource { } } - private void sentOtaData(CoapExchange exchange) { - String idStr = exchange.getRequestOptions().getUriPath().get(1); + private void sendOtaData(CoapExchange exchange) { + String idStr = exchange.getRequestOptions().getUriPath().get(exchange.getRequestOptions().getUriPath().size()-1 + ); UUID currentId = UUID.fromString(idStr); - if (exchange.getRequestOptions().getBlock2() != null) { - int chunkSize = exchange.getRequestOptions().getBlock2().getSzx(); - int chunk = 0; - Response response = new Response(CoAP.ResponseCode.CONTENT); - byte[] fwData = this.getOtaData(currentId); - log.warn("91) read softWare data (length): [{}]", fwData.length); - if (fwData != null && fwData.length > 0) { - response.setPayload(fwData); + Response response = new Response(CoAP.ResponseCode.CONTENT); + byte[] fwData = this.getOtaData(currentId); + log.warn("91) read softWare data (length): [{}]", fwData.length); + if (fwData != null && fwData.length > 0) { + response.setPayload(fwData); + if (exchange.getRequestOptions().getBlock2() != null) { + int chunkSize = exchange.getRequestOptions().getBlock2().getSzx(); boolean moreFlag = fwData.length > chunkSize; - response.getOptions().setBlock2(chunkSize, moreFlag, chunk); - log.warn("92) Send currentId: [{}], length: [{}], chunkSize [{}], moreFlag [{}]", currentId.toString(), fwData.length, chunkSize, moreFlag); - exchange.respond(response); + response.getOptions().setBlock2(chunkSize, moreFlag, 0); + log.warn("92) with blokc2 Send currentId: [{}], length: [{}], chunkSize [{}], moreFlag [{}]", currentId.toString(), fwData.length, chunkSize, moreFlag); } - + else { + log.warn("92) with block1 Send currentId: [{}], length: [{}], ", currentId.toString(), fwData.length); + } + exchange.respond(response); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java index c088ac8477..02d3425ede 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java @@ -36,6 +36,7 @@ import java.util.Optional; import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Predicate; import static org.eclipse.leshan.core.SecurityMode.NO_SEC; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer; @@ -137,14 +138,19 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { @Override public LwM2mClient getClientBySessionInfo(TransportProtos.SessionInfoProto sessionInfo) { - LwM2mClient lwM2mClient = lwM2mClientsByEndpoint.values().stream().filter(c -> + LwM2mClient lwM2mClient = null; + Predicate isClientFilter = c -> (new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())) - .equals((new UUID(c.getSession().getSessionIdMSB(), c.getSession().getSessionIdLSB()))) - - ).findAny().orElse(null); + .equals((new UUID(c.getSession().getSessionIdMSB(), c.getSession().getSessionIdLSB()))); + if (this.lwM2mClientsByEndpoint.size() > 0) { + lwM2mClient = this.lwM2mClientsByEndpoint.values().stream().filter(isClientFilter).findAny().orElse(null); + } + if (lwM2mClient == null && this.lwM2mClientsByRegistrationId.size() > 0) { + lwM2mClient = this.lwM2mClientsByRegistrationId.values().stream().filter(isClientFilter).findAny().orElse(null); + } if (lwM2mClient == null) { log.warn("Device TimeOut? lwM2mClient is null."); - log.warn("SessionInfo input [{}], lwM2mClientsByEndpoint size: [{}]", sessionInfo, lwM2mClientsByEndpoint.values().size()); + log.warn("SessionInfo input [{}], lwM2mClientsByEndpoint size: [{}] lwM2mClientsByRegistrationId: [{}]", sessionInfo, lwM2mClientsByEndpoint.values(), lwM2mClientsByRegistrationId.values()); log.error("", new RuntimeException()); } return lwM2mClient; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java index 6b289bac59..95d69e3f7e 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java @@ -61,6 +61,7 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.L import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.EXECUTE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.READ; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_REPLACE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_INSTALL_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_NAME_ID; @@ -194,11 +195,17 @@ public class LwM2mFwSwUpdate { } else if (LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL.code == this.updateStrategy) { Registration registration = this.getLwM2MClient().getRegistration(); // String api = handler.config.getHostRequests(); - String api = "0.0.0.0"; + String api = "176.36.143.9"; int port = registration.getIdentity().isSecure() ? handler.config.getSecurePort() : handler.config.getPort(); +// String uri = "coap://" + api + ":" + Integer.valueOf(port) + "/" + "api/v1/test_url" + "/" + this.currentId.toString(); String uri = "coap://" + api + ":" + Integer.valueOf(port) + "/" + FIRMWARE_UPDATE_COAP_RECOURSE + "/" + this.currentId.toString(); +// home +// String uri = "coap://176.100.5.79:5683/api/v1/test_url/firmware"; +// office +// String uri = "coap://176.36.143.9:5683/api/v1/test_url/firmware"; log.warn("89) coapUri: [{}]", uri); - request.sendAllRequest(this.lwM2MClient, targetIdVer, WRITE_REPLACE, null, +// request.sendAllRequest(this.lwM2MClient, targetIdVer, WRITE_REPLACE, null, + request.sendAllRequest(this.lwM2MClient, targetIdVer, READ, null, uri, handler.config.getTimeout(), this.rpcRequest); } else if (LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_19_BINARY.code == this.updateStrategy) { diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html index b164e168cf..e75aedd47c 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html @@ -142,42 +142,98 @@
-
- - {{ 'device-profile.lwm2m.client-strategy-label' | translate }} - - {{ 'device-profile.lwm2m.client-strategy' | translate: - {count: 1} }} - {{ 'device-profile.lwm2m.client-strategy' | translate: - {count: 2} }} - - -
-
- - {{ 'device-profile.lwm2m.fw-update-strategy-label' | translate }} - - {{ 'device-profile.lwm2m.fw-update-strategy' | translate: - {count: 1} }} - {{ 'device-profile.lwm2m.fw-update-strategy' | translate: - {count: 2} }} - {{ 'device-profile.lwm2m.fw-update-strategy' | translate: - {count: 3} }} - - - - {{ 'device-profile.lwm2m.sw-update-strategy-label' | translate }} - - {{ 'device-profile.lwm2m.sw-update-strategy' | translate: - {count: 1} }} - {{ 'device-profile.lwm2m.sw-update-strategy' | translate: - {count: 2} }} - - -
+ +
+ + + +
{{ 'device-profile.lwm2m.client-strategy' | translate | uppercase }}
+
+
+ +
+ + {{ 'device-profile.lwm2m.client-strategy-label' | translate }} + + {{ 'device-profile.lwm2m.client-strategy-connect' | translate: + {count: 1} }} + {{ 'device-profile.lwm2m.client-strategy-connect' | translate: + {count: 2} }} + + +
+
+
+
+ + + +
{{ 'device-profile.lwm2m.ota-update-strategy' | translate | uppercase }}
+
+
+ +
+ + {{ 'device-profile.lwm2m.fw-update-strategy-label' | translate }} + + {{ 'device-profile.lwm2m.fw-update-strategy' | translate: + {count: 1} }} + {{ 'device-profile.lwm2m.fw-update-strategy' | translate: + {count: 2} }} + {{ 'device-profile.lwm2m.fw-update-strategy' | translate: + {count: 3} }} + + + + {{ 'device-profile.lwm2m.sw-update-strategy-label' | translate }} + + {{ 'device-profile.lwm2m.sw-update-strategy' | translate: + {count: 1} }} + {{ 'device-profile.lwm2m.sw-update-strategy' | translate: + {count: 2} }} + + +
+
+
+ + + +
{{ 'device-profile.lwm2m.ota-update-recourse' | translate | uppercase }}
+
+
+ +
+
+ + {{ 'device-profile.lwm2m.fw-update-recourse' | translate }} + + + {{ 'device-profile.lwm2m.fw-update-recourse' | translate }} + {{ 'device-profile.lwm2m.required' | translate }} + + +
+
+ + {{ 'device-profile.lwm2m.sw-update-recourse' | translate }} + + + {{ 'device-profile.lwm2m.sw-update-recourse' | translate }} + {{ 'device-profile.lwm2m.required' | translate }} + + +
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts index 7d2856e1dc..3e83d1c6a6 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts @@ -22,6 +22,8 @@ import { ATTRIBUTE, BINDING_MODE, BINDING_MODE_NAMES, + DEFAULT_FW_UPDATE_RESOURCE, + DEFAULT_SW_UPDATE_RESOURCE, getDefaultProfileConfig, Instance, INSTANCES, @@ -41,6 +43,7 @@ import { Direction } from '@shared/models/page/sort-order'; import _ from 'lodash'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; +import { MatSelectChange } from '@angular/material/select'; @Component({ selector: 'tb-profile-lwm2m-device-transport-configuration', @@ -67,6 +70,8 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro bootstrapServer: string; lwm2mServer: string; sortFunction: (key: string, value: object) => object; + isFwUpdateStrategy: boolean; + isSwUpdateStrategy: boolean; get required(): boolean { return this.requiredValue; @@ -95,6 +100,8 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro clientStrategy: [1, []], fwUpdateStrategy: [1, []], swUpdateStrategy: [1, []], + fwUpdateRecourse: ['', []], + swUpdateRecourse: ['', []] }); this.lwm2mDeviceConfigFormGroup = this.fb.group({ configurationJson: [null, Validators.required] @@ -169,6 +176,13 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } private updateWriteValue = (value: ModelValue): void => { + debugger + let fwResource = this.configurationValue.clientLwM2mSettings.fwUpdateStrategy === '2' && + isDefinedAndNotNull(this.configurationValue.clientLwM2mSettings.fwUpdateRecourse) ? + this.configurationValue.clientLwM2mSettings.fwUpdateRecourse : ''; + let swResource = this.configurationValue.clientLwM2mSettings.swUpdateStrategy === '2' && + isDefinedAndNotNull(this.configurationValue.clientLwM2mSettings.fwUpdateRecourse) ? + this.configurationValue.clientLwM2mSettings.swUpdateRecourse : ''; this.lwm2mDeviceProfileFormGroup.patchValue({ objectIds: value, observeAttrTelemetry: this.getObserveAttrTelemetryObjects(value.objectsList), @@ -181,9 +195,17 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro lwm2mServer: this.configurationValue.bootstrap.lwm2mServer, clientStrategy: this.configurationValue.clientLwM2mSettings.clientStrategy, fwUpdateStrategy: this.configurationValue.clientLwM2mSettings.fwUpdateStrategy, - swUpdateStrategy: this.configurationValue.clientLwM2mSettings.swUpdateStrategy + swUpdateStrategy: this.configurationValue.clientLwM2mSettings.swUpdateStrategy, + fwUpdateRecourse: fwResource, + swUpdateRecourse: swResource }, {emitEvent: false}); + this.configurationValue.clientLwM2mSettings.fwUpdateRecourse = fwResource; + this.configurationValue.clientLwM2mSettings.swUpdateRecourse = swResource; + this.isFwUpdateStrategy = this.configurationValue.clientLwM2mSettings.fwUpdateStrategy === '2'; + this.isSwUpdateStrategy = this.configurationValue.clientLwM2mSettings.swUpdateStrategy === '2'; + this.otaUpdateSwStrategyValidate(); + this.otaUpdateFwStrategyValidate(); } private updateModel = (): void => { @@ -215,6 +237,8 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro this.configurationValue.clientLwM2mSettings.clientStrategy = config.clientStrategy; this.configurationValue.clientLwM2mSettings.fwUpdateStrategy = config.fwUpdateStrategy; this.configurationValue.clientLwM2mSettings.swUpdateStrategy = config.swUpdateStrategy; + this.configurationValue.clientLwM2mSettings.fwUpdateRecourse = config.fwUpdateRecourse; + this.configurationValue.clientLwM2mSettings.swUpdateRecourse = config.swUpdateRecourse; this.upDateJsonAllConfig(); this.updateModel(); } @@ -264,7 +288,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro const pathParameter = Array.from(path.split('/'), String); const objectLwM2M = clientObserveAttrTelemetry.find(x => x.keyId === pathParameter[0]); if (objectLwM2M) { - const instance = this.updateInInstanceKeyName (objectLwM2M.instances[0], +pathParameter[1]); + const instance = this.updateInInstanceKeyName(objectLwM2M.instances[0], +pathParameter[1]); objectLwM2M.instances.push(instance); } }); @@ -489,4 +513,56 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } }); } + + changeFwUpdateStrategy($event: MatSelectChange) { + debugger + if ($event.value === '2') { + this.isFwUpdateStrategy = true; + if (isEmpty(this.lwm2mDeviceProfileFormGroup.get('fwUpdateRecourse').value)) { + this.lwm2mDeviceProfileFormGroup.patchValue({ + fwUpdateRecourse: DEFAULT_FW_UPDATE_RESOURCE + }, + {emitEvent: false}); + } + } else { + this.isFwUpdateStrategy = false; + } + this.otaUpdateFwStrategyValidate(); + } + + changeSwUpdateStrategy($event: MatSelectChange) { + debugger + if ($event.value === '2') { + this.isSwUpdateStrategy = true; + if (isEmpty(this.lwm2mDeviceProfileFormGroup.get('swUpdateRecourse').value)) { + this.lwm2mDeviceProfileFormGroup.patchValue({ + swUpdateRecourse: DEFAULT_SW_UPDATE_RESOURCE + }, + {emitEvent: false}); + + } + } else { + this.isSwUpdateStrategy = false; + } + this.otaUpdateSwStrategyValidate(); + } + + private otaUpdateFwStrategyValidate(): void { + if (this.isFwUpdateStrategy) { + this.lwm2mDeviceProfileFormGroup.get('fwUpdateRecourse').setValidators([Validators.required]); + } else { + this.lwm2mDeviceProfileFormGroup.get('fwUpdateRecourse').clearValidators(); + } + this.lwm2mDeviceProfileFormGroup.get('fwUpdateRecourse').updateValueAndValidity(); + } + + private otaUpdateSwStrategyValidate(): void { + if (this.isSwUpdateStrategy) { + this.lwm2mDeviceProfileFormGroup.get('swUpdateRecourse').setValidators([Validators.required]); + } else { + this.lwm2mDeviceProfileFormGroup.get('swUpdateRecourse').clearValidators(); + } + this.lwm2mDeviceProfileFormGroup.get('swUpdateRecourse').updateValueAndValidity(); + } + } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts index 2a12f892e2..fb85eb0858 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts @@ -28,7 +28,7 @@ export const TELEMETRY = 'telemetry'; export const KEY_NAME = 'keyName'; export const DEFAULT_ID_SERVER = 123; export const DEFAULT_ID_BOOTSTRAP = 111; -export const DEFAULT_HOST_NAME = 'localhost'; +export const DEFAULT_LOCAL_HOST_NAME = 'localhost'; export const DEFAULT_PORT_SERVER_NO_SEC = 5685; export const DEFAULT_PORT_BOOTSTRAP_NO_SEC = 5687; export const DEFAULT_CLIENT_HOLD_OFF_TIME = 1; @@ -43,6 +43,11 @@ export const KEY_REGEXP_HEX_DEC = /^[-+]?[0-9A-Fa-f]+\.?[0-9A-Fa-f]*?$/; export const KEY_REGEXP_NUMBER = /^(\-?|\+?)\d*$/; export const INSTANCES_ID_VALUE_MIN = 0; export const INSTANCES_ID_VALUE_MAX = 65535; +export const DEFAULT_OTA_UPDATE_PROTOCOL = "coap://"; +export const DEFAULT_FIRMWARE_UPDATE_COAP_RECOURSE = "firmwareUpdateCoapRecourse"; +export const DEFAULT_SOFTWARE_UPDATE_COAP_RECOURSE = "softwareUpdateCoapRecourse"; +export const DEFAULT_FW_UPDATE_RESOURCE = DEFAULT_OTA_UPDATE_PROTOCOL + DEFAULT_LOCAL_HOST_NAME + ":"+ DEFAULT_PORT_SERVER_NO_SEC + "/" + DEFAULT_FIRMWARE_UPDATE_COAP_RECOURSE; +export const DEFAULT_SW_UPDATE_RESOURCE = DEFAULT_OTA_UPDATE_PROTOCOL + DEFAULT_LOCAL_HOST_NAME + ":"+ DEFAULT_PORT_SERVER_NO_SEC + "/" + DEFAULT_SOFTWARE_UPDATE_COAP_RECOURSE; export enum BINDING_MODE { @@ -168,6 +173,8 @@ export interface ClientLwM2mSettings { clientStrategy: string; fwUpdateStrategy: string; swUpdateStrategy: string; + fwUpdateRecourse: string; + swUpdateRecourse: string; } export interface ObservableAttributes { @@ -231,7 +238,7 @@ export function getDefaultProfileConfig(hostname?: any): Lwm2mProfileConfigModel return { clientLwM2mSettings: getDefaultProfileClientLwM2mSettingsConfig(), observeAttr: getDefaultProfileObserveAttrConfig(), - bootstrap: getDefaultProfileBootstrapSecurityConfig((hostname) ? hostname : DEFAULT_HOST_NAME) + bootstrap: getDefaultProfileBootstrapSecurityConfig((hostname) ? hostname : DEFAULT_LOCAL_HOST_NAME) }; } @@ -239,7 +246,9 @@ function getDefaultProfileClientLwM2mSettingsConfig(): ClientLwM2mSettings { return { clientStrategy: "1", fwUpdateStrategy: "1", - swUpdateStrategy: "1" + swUpdateStrategy: "1", + fwUpdateRecourse: DEFAULT_FW_UPDATE_RESOURCE, + swUpdateRecourse: DEFAULT_SW_UPDATE_RESOURCE }; } diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 5fbb6ed3fb..90a93c09e6 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -1262,6 +1262,7 @@ "bootstrap-server-account-timeout": "Account after the timeout", "bootstrap-server-account-timeout-tip": "Bootstrap-Server Account after the timeout value given by this resource.", "others-tab": "Other settings...", + "client-strategy": "Client strategy when connecting", "client-strategy-label": "Strategy", "client-strategy-connect": "{ count, plural, 1 {1: Only Observe Request to the client after the initial connection} other {2: Read All Resources & Observe Request to the client after registration} }", "client-strategy-tip": "{ count, plural, 1 {Strategy 1: After the initial connection of the LWM2M Client, the server sends Observe resources Request to the client, those resources that are marked as observation in the Device profile and which exist on the LWM2M client.} other {Strategy 2: After the registration, request the client to read all the resource values for all objects that the LWM2M client has,\n then execute: the server sends Observe resources Request to the client, those resources that are marked as observation in the Device profile and which exist on the LWM2M client.} }", @@ -1270,9 +1271,7 @@ "fw-update-strategy": "{ count, plural, 1 {Push firmware update as binary file using Object 5 and Resource 0 (Package).} 2 {Auto-generate unique CoAP URL to download the package and push firmware update as Object 5 and Resource 1 (Package URI).} other {Push firmware update as binary file using Object 19 and Resource 0 (Data).} }", "sw-update-strategy-label": "Software update strategy", "sw-update-strategy": "{ count, plural, 1 {Push binary file using Object 9 and Resource 2 (Package).} other {Auto-generate unique CoAP URL to download the package and push software update using Object 9 and Resource 3 (Package URI).} }", - "blockwise-settings": "Blockwise settings", - "blockwise-enabled": "Enable blockwise", - "blockwise-status-lifetime": "Blockwise Status Lifetime", + "ota-update-recourse": "Ota update Coap recourse", "fw-update-recourse": "Firmware update Coap recourse", "sw-update-recourse": "Software update Coap recourse", "config-json-tab": "Json Config Profile Device" From 26bc7841aedc4335f9169e43c3bc7ed796b5b65b Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Thu, 10 Jun 2021 13:08:13 +0300 Subject: [PATCH 07/11] LWM2M: front del debugger --- .../transport/coap/CoapTransportResource.java | 2 +- .../lwm2m/server/client/LwM2mFwSwUpdate.java | 16 +++------------- ...-profile-transport-configuration.component.ts | 3 --- .../device/lwm2m/lwm2m-profile-config.models.ts | 6 ++---- 4 files changed, 6 insertions(+), 21 deletions(-) diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java index a1a2a4b656..33f122fa3f 100644 --- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java @@ -398,7 +398,7 @@ public class CoapTransportResource extends AbstractCoapTransportResource { private Optional decodeCredentials(Request request) { List uriPath = request.getOptions().getUriPath(); - if (uriPath.size() >= ACCESS_TOKEN_POSITION) { + if (uriPath.size() > ACCESS_TOKEN_POSITION) { return Optional.of(new DeviceTokenCredentials(uriPath.get(ACCESS_TOKEN_POSITION - 1))); } else { return Optional.empty(); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java index 95d69e3f7e..6828761fb0 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java @@ -61,7 +61,6 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.L import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.EXECUTE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.READ; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_REPLACE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_INSTALL_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_NAME_ID; @@ -193,19 +192,10 @@ public class LwM2mFwSwUpdate { request.sendAllRequest(this.lwM2MClient, targetIdVer, WRITE_REPLACE, ContentFormat.OPAQUE, firmwareChunk, handler.config.getTimeout(), this.rpcRequest); } else if (LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL.code == this.updateStrategy) { - Registration registration = this.getLwM2MClient().getRegistration(); -// String api = handler.config.getHostRequests(); - String api = "176.36.143.9"; - int port = registration.getIdentity().isSecure() ? handler.config.getSecurePort() : handler.config.getPort(); -// String uri = "coap://" + api + ":" + Integer.valueOf(port) + "/" + "api/v1/test_url" + "/" + this.currentId.toString(); - String uri = "coap://" + api + ":" + Integer.valueOf(port) + "/" + FIRMWARE_UPDATE_COAP_RECOURSE + "/" + this.currentId.toString(); -// home -// String uri = "coap://176.100.5.79:5683/api/v1/test_url/firmware"; -// office -// String uri = "coap://176.36.143.9:5683/api/v1/test_url/firmware"; + String apiFont = "coap://176.36.143.9:5685"; + String uri = apiFont + "/" + FIRMWARE_UPDATE_COAP_RECOURSE + "/" + this.currentId.toString(); log.warn("89) coapUri: [{}]", uri); -// request.sendAllRequest(this.lwM2MClient, targetIdVer, WRITE_REPLACE, null, - request.sendAllRequest(this.lwM2MClient, targetIdVer, READ, null, + request.sendAllRequest(this.lwM2MClient, targetIdVer, WRITE_REPLACE, null, uri, handler.config.getTimeout(), this.rpcRequest); } else if (LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_19_BINARY.code == this.updateStrategy) { diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts index 3e83d1c6a6..bc9b36271d 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts @@ -176,7 +176,6 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } private updateWriteValue = (value: ModelValue): void => { - debugger let fwResource = this.configurationValue.clientLwM2mSettings.fwUpdateStrategy === '2' && isDefinedAndNotNull(this.configurationValue.clientLwM2mSettings.fwUpdateRecourse) ? this.configurationValue.clientLwM2mSettings.fwUpdateRecourse : ''; @@ -515,7 +514,6 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } changeFwUpdateStrategy($event: MatSelectChange) { - debugger if ($event.value === '2') { this.isFwUpdateStrategy = true; if (isEmpty(this.lwm2mDeviceProfileFormGroup.get('fwUpdateRecourse').value)) { @@ -531,7 +529,6 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } changeSwUpdateStrategy($event: MatSelectChange) { - debugger if ($event.value === '2') { this.isSwUpdateStrategy = true; if (isEmpty(this.lwm2mDeviceProfileFormGroup.get('swUpdateRecourse').value)) { diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts index fb85eb0858..ff8225a27d 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts @@ -44,10 +44,8 @@ export const KEY_REGEXP_NUMBER = /^(\-?|\+?)\d*$/; export const INSTANCES_ID_VALUE_MIN = 0; export const INSTANCES_ID_VALUE_MAX = 65535; export const DEFAULT_OTA_UPDATE_PROTOCOL = "coap://"; -export const DEFAULT_FIRMWARE_UPDATE_COAP_RECOURSE = "firmwareUpdateCoapRecourse"; -export const DEFAULT_SOFTWARE_UPDATE_COAP_RECOURSE = "softwareUpdateCoapRecourse"; -export const DEFAULT_FW_UPDATE_RESOURCE = DEFAULT_OTA_UPDATE_PROTOCOL + DEFAULT_LOCAL_HOST_NAME + ":"+ DEFAULT_PORT_SERVER_NO_SEC + "/" + DEFAULT_FIRMWARE_UPDATE_COAP_RECOURSE; -export const DEFAULT_SW_UPDATE_RESOURCE = DEFAULT_OTA_UPDATE_PROTOCOL + DEFAULT_LOCAL_HOST_NAME + ":"+ DEFAULT_PORT_SERVER_NO_SEC + "/" + DEFAULT_SOFTWARE_UPDATE_COAP_RECOURSE; +export const DEFAULT_FW_UPDATE_RESOURCE = DEFAULT_OTA_UPDATE_PROTOCOL + DEFAULT_LOCAL_HOST_NAME + ":"+ DEFAULT_PORT_SERVER_NO_SEC; +export const DEFAULT_SW_UPDATE_RESOURCE = DEFAULT_OTA_UPDATE_PROTOCOL + DEFAULT_LOCAL_HOST_NAME + ":"+ DEFAULT_PORT_SERVER_NO_SEC; export enum BINDING_MODE { From cc531daaeee9f4ff2f38b3f2dcaa25f5854358a6 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 10 Jun 2021 17:01:16 +0300 Subject: [PATCH 08/11] Add platform type filter for oauth2 registrations. --- .../main/data/upgrade/3.2.2/schema_update.sql | 1 + .../server/controller/OAuth2Controller.java | 13 ++- .../install/ThingsboardInstallService.java | 1 + .../server/dao/oauth2/OAuth2Service.java | 3 +- .../data/oauth2/OAuth2Registration.java | 2 + .../data/oauth2/OAuth2RegistrationInfo.java | 1 + .../common/data/oauth2/PlatformType.java | 20 +++++ .../server/dao/model/ModelConstants.java | 1 + .../model/sql/OAuth2RegistrationEntity.java | 9 ++ .../dao/oauth2/OAuth2RegistrationDao.java | 3 +- .../server/dao/oauth2/OAuth2ServiceImpl.java | 8 +- .../server/dao/oauth2/OAuth2Utils.java | 3 + .../sql/oauth2/JpaOAuth2RegistrationDao.java | 6 +- .../oauth2/OAuth2RegistrationRepository.java | 11 +-- .../resources/sql/schema-entities-hsql.sql | 1 + .../main/resources/sql/schema-entities.sql | 1 + .../dao/service/BaseOAuth2ServiceTest.java | 83 ++++++++++++++++--- ui-ngx/src/app/core/auth/auth.service.ts | 9 +- .../admin/oauth2-settings.component.html | 27 ++++-- .../pages/admin/oauth2-settings.component.ts | 6 +- ui-ngx/src/app/shared/models/oauth2.models.ts | 15 ++++ .../assets/locale/locale.constant-en_US.json | 7 +- 22 files changed, 187 insertions(+), 44 deletions(-) create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/oauth2/PlatformType.java diff --git a/application/src/main/data/upgrade/3.2.2/schema_update.sql b/application/src/main/data/upgrade/3.2.2/schema_update.sql index 9e94caf1af..30bc80a458 100644 --- a/application/src/main/data/upgrade/3.2.2/schema_update.sql +++ b/application/src/main/data/upgrade/3.2.2/schema_update.sql @@ -96,6 +96,7 @@ CREATE TABLE IF NOT EXISTS oauth2_registration ( authorization_uri varchar(255), token_uri varchar(255), scope varchar(255), + platforms varchar(255), user_info_uri varchar(255), user_name_attribute_name varchar(255), jwk_set_uri varchar(255), diff --git a/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java b/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java index 349c6de672..7497e20122 100644 --- a/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java +++ b/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java @@ -26,9 +26,11 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; import org.thingsboard.server.common.data.oauth2.OAuth2Info; +import org.thingsboard.server.common.data.oauth2.PlatformType; import org.thingsboard.server.dao.oauth2.OAuth2Configuration; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.permission.Operation; @@ -51,7 +53,8 @@ public class OAuth2Controller extends BaseController { @RequestMapping(value = "/noauth/oauth2Clients", method = RequestMethod.POST) @ResponseBody public List getOAuth2Clients(HttpServletRequest request, - @RequestParam(required = false) String pkgName) throws ThingsboardException { + @RequestParam(required = false) String pkgName, + @RequestParam(required = false) String platform) throws ThingsboardException { try { if (log.isDebugEnabled()) { log.debug("Executing getOAuth2Clients: [{}][{}][{}]", request.getScheme(), request.getServerName(), request.getServerPort()); @@ -61,7 +64,13 @@ public class OAuth2Controller extends BaseController { log.debug("Header: {} {}", header, request.getHeader(header)); } } - return oAuth2Service.getOAuth2Clients(MiscUtils.getScheme(request), MiscUtils.getDomainNameAndPort(request), pkgName); + PlatformType platformType = null; + if (StringUtils.isNotEmpty(platform)) { + try { + platformType = PlatformType.valueOf(platform); + } catch (Exception e) {} + } + return oAuth2Service.getOAuth2Clients(MiscUtils.getScheme(request), MiscUtils.getDomainNameAndPort(request), pkgName, platformType); } catch (Exception e) { throw handleException(e); } diff --git a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java index 267fb31ad7..b3f0d644c7 100644 --- a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java +++ b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java @@ -199,6 +199,7 @@ public class ThingsboardInstallService { databaseEntitiesUpgradeService.upgradeDatabase("3.2.2"); dataUpdateService.updateData("3.2.2"); + systemDataLoaderService.createOAuth2Templates(); log.info("Updating system data..."); systemDataLoaderService.updateSystemWidgets(); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java index 158c3911b4..0e9a36c05e 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java @@ -18,6 +18,7 @@ package org.thingsboard.server.dao.oauth2; import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; import org.thingsboard.server.common.data.oauth2.OAuth2Info; import org.thingsboard.server.common.data.oauth2.OAuth2Registration; +import org.thingsboard.server.common.data.oauth2.PlatformType; import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientRegistrationInfo; import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientsParams; @@ -25,7 +26,7 @@ import java.util.List; import java.util.UUID; public interface OAuth2Service { - List getOAuth2Clients(String domainScheme, String domainName, String pkgName); + List getOAuth2Clients(String domainScheme, String domainName, String pkgName, PlatformType platformType); @Deprecated void saveOAuth2Params(OAuth2ClientsParams oauth2Params); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Registration.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Registration.java index 5120e3202f..095462b16f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Registration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Registration.java @@ -46,6 +46,7 @@ public class OAuth2Registration extends SearchTextBasedWithAdditionalInfo platforms; public OAuth2Registration(OAuth2Registration registration) { super(registration); @@ -62,6 +63,7 @@ public class OAuth2Registration extends SearchTextBasedWithAdditionalInfo platforms; private JsonNode additionalInfo; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/PlatformType.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/PlatformType.java new file mode 100644 index 0000000000..9c775e86e7 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/PlatformType.java @@ -0,0 +1,20 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.oauth2; + +public enum PlatformType { + WEB, ANDROID, IOS +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index 02c8db42d4..32b5afc9aa 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -433,6 +433,7 @@ public class ModelConstants { public static final String OAUTH2_AUTHORIZATION_URI_PROPERTY = "authorization_uri"; public static final String OAUTH2_TOKEN_URI_PROPERTY = "token_uri"; public static final String OAUTH2_SCOPE_PROPERTY = "scope"; + public static final String OAUTH2_PLATFORMS_PROPERTY = "platforms"; public static final String OAUTH2_USER_INFO_URI_PROPERTY = "user_info_uri"; public static final String OAUTH2_USER_NAME_ATTRIBUTE_NAME_PROPERTY = "user_name_attribute_name"; public static final String OAUTH2_JWK_SET_URI_PROPERTY = "jwk_set_uri"; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2RegistrationEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2RegistrationEntity.java index 9c6c260316..edcaa794a6 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2RegistrationEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2RegistrationEntity.java @@ -16,6 +16,7 @@ package org.thingsboard.server.dao.model.sql; import com.fasterxml.jackson.databind.JsonNode; +import io.micrometer.core.instrument.util.StringUtils; import lombok.Data; import lombok.EqualsAndHashCode; import org.hibernate.annotations.Type; @@ -27,6 +28,7 @@ import org.thingsboard.server.common.data.oauth2.OAuth2BasicMapperConfig; import org.thingsboard.server.common.data.oauth2.OAuth2CustomMapperConfig; import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; import org.thingsboard.server.common.data.oauth2.OAuth2Registration; +import org.thingsboard.server.common.data.oauth2.PlatformType; import org.thingsboard.server.common.data.oauth2.TenantNameStrategyType; import org.thingsboard.server.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.ModelConstants; @@ -38,7 +40,9 @@ import javax.persistence.EnumType; import javax.persistence.Enumerated; import javax.persistence.Table; import java.util.Arrays; +import java.util.Collections; import java.util.UUID; +import java.util.stream.Collectors; @Data @EqualsAndHashCode(callSuper = true) @@ -59,6 +63,8 @@ public class OAuth2RegistrationEntity extends BaseSqlEntity private String tokenUri; @Column(name = ModelConstants.OAUTH2_SCOPE_PROPERTY) private String scope; + @Column(name = ModelConstants.OAUTH2_PLATFORMS_PROPERTY) + private String platforms; @Column(name = ModelConstants.OAUTH2_USER_INFO_URI_PROPERTY) private String userInfoUri; @Column(name = ModelConstants.OAUTH2_USER_NAME_ATTRIBUTE_NAME_PROPERTY) @@ -125,6 +131,7 @@ public class OAuth2RegistrationEntity extends BaseSqlEntity this.authorizationUri = registration.getAuthorizationUri(); this.tokenUri = registration.getAccessTokenUri(); this.scope = registration.getScope().stream().reduce((result, element) -> result + "," + element).orElse(""); + this.platforms = registration.getPlatforms() != null ? registration.getPlatforms().stream().map(Enum::name).reduce((result, element) -> result + "," + element).orElse("") : ""; this.userInfoUri = registration.getUserInfoUri(); this.userNameAttributeName = registration.getUserNameAttributeName(); this.jwkSetUri = registration.getJwkSetUri(); @@ -201,6 +208,8 @@ public class OAuth2RegistrationEntity extends BaseSqlEntity registration.setAuthorizationUri(authorizationUri); registration.setAccessTokenUri(tokenUri); registration.setScope(Arrays.asList(scope.split(","))); + registration.setPlatforms(StringUtils.isNotEmpty(platforms) ? Arrays.stream(platforms.split(",")) + .map(str -> PlatformType.valueOf(str)).collect(Collectors.toList()) : Collections.emptyList()); registration.setUserInfoUri(userInfoUri); registration.setUserNameAttributeName(userNameAttributeName); registration.setJwkSetUri(jwkSetUri); diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2RegistrationDao.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2RegistrationDao.java index ad12b12e48..de80334577 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2RegistrationDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2RegistrationDao.java @@ -16,6 +16,7 @@ package org.thingsboard.server.dao.oauth2; import org.thingsboard.server.common.data.oauth2.OAuth2Registration; +import org.thingsboard.server.common.data.oauth2.PlatformType; import org.thingsboard.server.common.data.oauth2.SchemeType; import org.thingsboard.server.dao.Dao; @@ -24,7 +25,7 @@ import java.util.UUID; public interface OAuth2RegistrationDao extends Dao { - List findEnabledByDomainSchemesDomainNameAndPkgName(List domainSchemes, String domainName, String pkgName); + List findEnabledByDomainSchemesDomainNameAndPkgNameAndPlatformType(List domainSchemes, String domainName, String pkgName, PlatformType platformType); List findByOAuth2ParamsId(UUID oauth2ParamsId); diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index 8d06adee0b..6c2126a005 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -65,8 +65,8 @@ public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Se private OAuth2MobileDao oauth2MobileDao; @Override - public List getOAuth2Clients(String domainSchemeStr, String domainName, String pkgName) { - log.trace("Executing getOAuth2Clients [{}://{}]", domainSchemeStr, domainName); + public List getOAuth2Clients(String domainSchemeStr, String domainName, String pkgName, PlatformType platformType) { + log.trace("Executing getOAuth2Clients [{}://{}] pkgName=[{}] platformType=[{}]", domainSchemeStr, domainName, pkgName, platformType); if (domainSchemeStr == null) { throw new IncorrectParameterException(INCORRECT_DOMAIN_SCHEME); } @@ -77,7 +77,9 @@ public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Se throw new IncorrectParameterException(INCORRECT_DOMAIN_SCHEME); } validateString(domainName, INCORRECT_DOMAIN_NAME + domainName); - return oauth2RegistrationDao.findEnabledByDomainSchemesDomainNameAndPkgName(Arrays.asList(domainScheme, SchemeType.MIXED), domainName, pkgName).stream() + return oauth2RegistrationDao.findEnabledByDomainSchemesDomainNameAndPkgNameAndPlatformType( + Arrays.asList(domainScheme, SchemeType.MIXED), domainName, pkgName, platformType) + .stream() .map(OAuth2Utils::toClientInfo) .collect(Collectors.toList()); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java index dd128b435b..896c619c82 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java @@ -127,6 +127,7 @@ public class OAuth2Utils { .authorizationUri(registration.getAuthorizationUri()) .accessTokenUri(registration.getAccessTokenUri()) .scope(registration.getScope()) + .platforms(registration.getPlatforms()) .userInfoUri(registration.getUserInfoUri()) .userNameAttributeName(registration.getUserNameAttributeName()) .jwkSetUri(registration.getJwkSetUri()) @@ -167,6 +168,7 @@ public class OAuth2Utils { registration.setAuthorizationUri(registrationInfo.getAuthorizationUri()); registration.setAccessTokenUri(registrationInfo.getAccessTokenUri()); registration.setScope(registrationInfo.getScope()); + registration.setPlatforms(registrationInfo.getPlatforms()); registration.setUserInfoUri(registrationInfo.getUserInfoUri()); registration.setUserNameAttributeName(registrationInfo.getUserNameAttributeName()); registration.setJwkSetUri(registrationInfo.getJwkSetUri()); @@ -224,6 +226,7 @@ public class OAuth2Utils { .loginButtonLabel(clientRegistrationDto.getLoginButtonLabel()) .loginButtonIcon(clientRegistrationDto.getLoginButtonIcon()) .additionalInfo(clientRegistrationDto.getAdditionalInfo()) + .platforms(Collections.emptyList()) .build(); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2RegistrationDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2RegistrationDao.java index c3ee8a8c03..0b09f7ddea 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2RegistrationDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2RegistrationDao.java @@ -19,6 +19,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.oauth2.OAuth2Registration; +import org.thingsboard.server.common.data.oauth2.PlatformType; import org.thingsboard.server.common.data.oauth2.SchemeType; import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.model.sql.OAuth2RegistrationEntity; @@ -45,8 +46,9 @@ public class JpaOAuth2RegistrationDao extends JpaAbstractDao findEnabledByDomainSchemesDomainNameAndPkgName(List domainSchemes, String domainName, String pkgName) { - return DaoUtil.convertDataList(repository.findAllEnabledByDomainSchemesNameAndPkgName(domainSchemes, domainName, pkgName)); + public List findEnabledByDomainSchemesDomainNameAndPkgNameAndPlatformType(List domainSchemes, String domainName, String pkgName, PlatformType platformType) { + return DaoUtil.convertDataList(repository.findEnabledByDomainSchemesDomainNameAndPkgNameAndPlatformType(domainSchemes, domainName, pkgName, + platformType != null ? "%" + platformType.name() + "%" : null)); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2RegistrationRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2RegistrationRepository.java index 676df8adcc..dad50e6180 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2RegistrationRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2RegistrationRepository.java @@ -30,14 +30,15 @@ public interface OAuth2RegistrationRepository extends CrudRepository findAllEnabledByDomainSchemesNameAndPkgName(@Param("domainSchemes") List domainSchemes, - @Param("domainName") String domainName, - @Param("pkgName") String pkgName); + "AND (:pkgName IS NULL OR EXISTS (SELECT mobile FROM OAuth2MobileEntity mobile WHERE mobile.oauth2ParamsId = reg.oauth2ParamsId AND mobile.pkgName = :pkgName)) " + + "AND (:platformFilter IS NULL OR reg.platforms IS NULL OR reg.platforms = '' OR reg.platforms LIKE :platformFilter)") + List findEnabledByDomainSchemesDomainNameAndPkgNameAndPlatformType(@Param("domainSchemes") List domainSchemes, + @Param("domainName") String domainName, + @Param("pkgName") String pkgName, + @Param("platformFilter") String platformFilter); List findByOauth2ParamsId(UUID oauth2ParamsId); diff --git a/dao/src/main/resources/sql/schema-entities-hsql.sql b/dao/src/main/resources/sql/schema-entities-hsql.sql index 0e7aec5c19..ad3a023a41 100644 --- a/dao/src/main/resources/sql/schema-entities-hsql.sql +++ b/dao/src/main/resources/sql/schema-entities-hsql.sql @@ -391,6 +391,7 @@ CREATE TABLE IF NOT EXISTS oauth2_registration ( authorization_uri varchar(255), token_uri varchar(255), scope varchar(255), + platforms varchar(255), user_info_uri varchar(255), user_name_attribute_name varchar(255), jwk_set_uri varchar(255), diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index 377d140597..890d639a01 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -428,6 +428,7 @@ CREATE TABLE IF NOT EXISTS oauth2_registration ( authorization_uri varchar(255), token_uri varchar(255), scope varchar(255), + platforms varchar(255), user_info_uri varchar(255), user_name_attribute_name varchar(255), jwk_set_uri varchar(255), diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java index fa79e32bea..ae2ff054ec 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.dao.service; +import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.collect.Lists; import org.junit.After; import org.junit.Assert; @@ -31,6 +32,7 @@ import org.thingsboard.server.common.data.oauth2.OAuth2MobileInfo; import org.thingsboard.server.common.data.oauth2.OAuth2ParamsInfo; import org.thingsboard.server.common.data.oauth2.OAuth2Registration; import org.thingsboard.server.common.data.oauth2.OAuth2RegistrationInfo; +import org.thingsboard.server.common.data.oauth2.PlatformType; import org.thingsboard.server.common.data.oauth2.SchemeType; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.oauth2.OAuth2Service; @@ -231,10 +233,10 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { registrationInfo.getLoginButtonLabel(), registrationInfo.getLoginButtonIcon(), null)) .collect(Collectors.toList()); - List nonExistentDomainClients = oAuth2Service.getOAuth2Clients("http", "non-existent-domain", null); + List nonExistentDomainClients = oAuth2Service.getOAuth2Clients("http", "non-existent-domain", null, null); Assert.assertTrue(nonExistentDomainClients.isEmpty()); - List firstDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "first-domain", null); + List firstDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "first-domain", null, null); Assert.assertEquals(firstGroupClientInfos.size(), firstDomainHttpClients.size()); firstGroupClientInfos.forEach(firstGroupClientInfo -> { Assert.assertTrue( @@ -244,10 +246,10 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { ); }); - List firstDomainHttpsClients = oAuth2Service.getOAuth2Clients("https", "first-domain", null); + List firstDomainHttpsClients = oAuth2Service.getOAuth2Clients("https", "first-domain", null, null); Assert.assertTrue(firstDomainHttpsClients.isEmpty()); - List fourthDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "fourth-domain", null); + List fourthDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "fourth-domain", null, null); Assert.assertEquals(secondGroupClientInfos.size(), fourthDomainHttpClients.size()); secondGroupClientInfos.forEach(secondGroupClientInfo -> { Assert.assertTrue( @@ -256,7 +258,7 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { && clientInfo.getName().equals(secondGroupClientInfo.getName())) ); }); - List fourthDomainHttpsClients = oAuth2Service.getOAuth2Clients("https", "fourth-domain", null); + List fourthDomainHttpsClients = oAuth2Service.getOAuth2Clients("https", "fourth-domain", null, null); Assert.assertEquals(secondGroupClientInfos.size(), fourthDomainHttpsClients.size()); secondGroupClientInfos.forEach(secondGroupClientInfo -> { Assert.assertTrue( @@ -266,7 +268,7 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { ); }); - List secondDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "second-domain", null); + List secondDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "second-domain", null, null); Assert.assertEquals(firstGroupClientInfos.size() + secondGroupClientInfos.size(), secondDomainHttpClients.size()); firstGroupClientInfos.forEach(firstGroupClientInfo -> { Assert.assertTrue( @@ -283,7 +285,7 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { ); }); - List secondDomainHttpsClients = oAuth2Service.getOAuth2Clients("https", "second-domain", null); + List secondDomainHttpsClients = oAuth2Service.getOAuth2Clients("https", "second-domain", null, null); Assert.assertEquals(firstGroupClientInfos.size() + thirdGroupClientInfos.size(), secondDomainHttpsClients.size()); firstGroupClientInfos.forEach(firstGroupClientInfo -> { Assert.assertTrue( @@ -331,7 +333,7 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { registrationInfo.getLoginButtonLabel(), registrationInfo.getLoginButtonIcon(), null)) .collect(Collectors.toList()); - List firstDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "first-domain", null); + List firstDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "first-domain", null, null); Assert.assertEquals(firstGroupClientInfos.size(), firstDomainHttpClients.size()); firstGroupClientInfos.forEach(firstGroupClientInfo -> { Assert.assertTrue( @@ -341,7 +343,7 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { ); }); - List firstDomainHttpsClients = oAuth2Service.getOAuth2Clients("https", "first-domain", null); + List firstDomainHttpsClients = oAuth2Service.getOAuth2Clients("https", "first-domain", null, null); Assert.assertEquals(firstGroupClientInfos.size(), firstDomainHttpsClients.size()); firstGroupClientInfos.forEach(firstGroupClientInfo -> { Assert.assertTrue( @@ -381,13 +383,13 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { oAuth2Service.saveOAuth2Info(oAuth2Info); - List secondDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "second-domain", null); + List secondDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "second-domain", null, null); Assert.assertEquals(5, secondDomainHttpClients.size()); oAuth2Info.setEnabled(false); oAuth2Service.saveOAuth2Info(oAuth2Info); - List secondDomainHttpDisabledClients = oAuth2Service.getOAuth2Clients("http", "second-domain", null); + List secondDomainHttpDisabledClients = oAuth2Service.getOAuth2Clients("http", "second-domain", null, null); Assert.assertEquals(0, secondDomainHttpDisabledClients.size()); } @@ -520,7 +522,7 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { OAuth2Info foundOAuth2Info = oAuth2Service.findOAuth2Info(); Assert.assertEquals(oAuth2Info, foundOAuth2Info); - List firstDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "first-domain", "com.test.pkg1"); + List firstDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "first-domain", "com.test.pkg1", null); Assert.assertEquals(3, firstDomainHttpClients.size()); for (OAuth2ClientInfo clientInfo : firstDomainHttpClients) { String[] segments = clientInfo.getUrl().split("/"); @@ -536,6 +538,56 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { } } + @Test + public void testFindClientsByPackageAndPlatform() { + OAuth2Info oAuth2Info = new OAuth2Info(true, Lists.newArrayList( + OAuth2ParamsInfo.builder() + .domainInfos(Lists.newArrayList( + OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), + OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), + OAuth2DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() + )) + .mobileInfos(Lists.newArrayList( + OAuth2MobileInfo.builder().pkgName("com.test.pkg1").callbackUrlScheme("testPkg1Callback").build(), + OAuth2MobileInfo.builder().pkgName("com.test.pkg2").callbackUrlScheme("testPkg2Callback").build() + )) + .clientRegistrations(Lists.newArrayList( + validRegistrationInfo("Google", Arrays.asList(PlatformType.WEB, PlatformType.ANDROID)), + validRegistrationInfo("Facebook", Arrays.asList(PlatformType.IOS)), + validRegistrationInfo("GitHub", Collections.emptyList()) + )) + .build(), + OAuth2ParamsInfo.builder() + .domainInfos(Lists.newArrayList( + OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(), + OAuth2DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() + )) + .mobileInfos(Collections.emptyList()) + .clientRegistrations(Lists.newArrayList( + validRegistrationInfo(), + validRegistrationInfo() + )) + .build() + )); + oAuth2Service.saveOAuth2Info(oAuth2Info); + + OAuth2Info foundOAuth2Info = oAuth2Service.findOAuth2Info(); + Assert.assertEquals(oAuth2Info, foundOAuth2Info); + + List firstDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "first-domain", null, null); + Assert.assertEquals(3, firstDomainHttpClients.size()); + List pkg1Clients = oAuth2Service.getOAuth2Clients("http", "first-domain", "com.test.pkg1", null); + Assert.assertEquals(3, pkg1Clients.size()); + List pkg1AndroidClients = oAuth2Service.getOAuth2Clients("http", "first-domain", "com.test.pkg1", PlatformType.ANDROID); + Assert.assertEquals(2, pkg1AndroidClients.size()); + Assert.assertTrue(pkg1AndroidClients.stream().anyMatch(client -> client.getName().equals("Google"))); + Assert.assertTrue(pkg1AndroidClients.stream().anyMatch(client -> client.getName().equals("GitHub"))); + List pkg1IOSClients = oAuth2Service.getOAuth2Clients("http", "first-domain", "com.test.pkg1", PlatformType.IOS); + Assert.assertEquals(2, pkg1IOSClients.size()); + Assert.assertTrue(pkg1IOSClients.stream().anyMatch(client -> client.getName().equals("Facebook"))); + Assert.assertTrue(pkg1IOSClients.stream().anyMatch(client -> client.getName().equals("GitHub"))); + } + private OAuth2Info createDefaultOAuth2Info() { return new OAuth2Info(true, Lists.newArrayList( OAuth2ParamsInfo.builder() @@ -567,17 +619,22 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { } private OAuth2RegistrationInfo validRegistrationInfo() { + return validRegistrationInfo(null, Collections.emptyList()); + } + + private OAuth2RegistrationInfo validRegistrationInfo(String label, List platforms) { return OAuth2RegistrationInfo.builder() .clientId(UUID.randomUUID().toString()) .clientSecret(UUID.randomUUID().toString()) .authorizationUri(UUID.randomUUID().toString()) .accessTokenUri(UUID.randomUUID().toString()) .scope(Arrays.asList(UUID.randomUUID().toString(), UUID.randomUUID().toString())) + .platforms(platforms == null ? Collections.emptyList() : platforms) .userInfoUri(UUID.randomUUID().toString()) .userNameAttributeName(UUID.randomUUID().toString()) .jwkSetUri(UUID.randomUUID().toString()) .clientAuthenticationMethod(UUID.randomUUID().toString()) - .loginButtonLabel(UUID.randomUUID().toString()) + .loginButtonLabel(label != null ? label : UUID.randomUUID().toString()) .loginButtonIcon(UUID.randomUUID().toString()) .additionalInfo(mapper.createObjectNode().put(UUID.randomUUID().toString(), UUID.randomUUID().toString())) .mapperConfig( diff --git a/ui-ngx/src/app/core/auth/auth.service.ts b/ui-ngx/src/app/core/auth/auth.service.ts index 68724fa0dc..42b213f558 100644 --- a/ui-ngx/src/app/core/auth/auth.service.ts +++ b/ui-ngx/src/app/core/auth/auth.service.ts @@ -44,7 +44,7 @@ import { AdminService } from '@core/http/admin.service'; import { ActionNotificationShow } from '@core/notification/notification.actions'; import { MatDialog, MatDialogConfig } from '@angular/material/dialog'; import { AlertDialogComponent } from '@shared/components/dialog/alert-dialog.component'; -import { OAuth2ClientInfo } from '@shared/models/oauth2.models'; +import { OAuth2ClientInfo, PlatformType } from '@shared/models/oauth2.models'; import { isDefinedAndNotNull, isMobileApp } from '@core/utils'; @Injectable({ @@ -204,11 +204,8 @@ export class AuthService { } } - public loadOAuth2Clients(pkgName?: string): Observable> { - let url = '/api/noauth/oauth2Clients'; - if (isDefinedAndNotNull(pkgName)) { - url += `?pkgName=${pkgName}`; - } + public loadOAuth2Clients(): Observable> { + const url = '/api/noauth/oauth2Clients?platform=' + PlatformType.WEB; return this.http.post>(url, null, defaultHttpOptions()).pipe( catchError(err => of([])), diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html index 3ea9a1ca11..c56dc6a793 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html @@ -201,18 +201,27 @@
-
- - admin.oauth2.login-provider - - - {{ provider }} +
+
+ + admin.oauth2.login-provider + + + {{ provider }} + + + +
+
+ + admin.oauth2.allowed-platforms + + + {{ platformTypeTranslations.get(platform) | translate }} -
-
- +
admin.oauth2.client-id diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts index 39fb6ac6ff..3d4baaf59a 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts @@ -28,7 +28,8 @@ import { OAuth2DomainInfo, OAuth2Info, OAuth2MobileInfo, OAuth2ParamsInfo, - OAuth2RegistrationInfo, + OAuth2RegistrationInfo, PlatformType, + platformTypeTranslations, TenantNameStrategy } from '@shared/models/oauth2.models'; import { Store } from '@ngrx/store'; @@ -99,6 +100,8 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha tenantNameStrategies = Object.keys(TenantNameStrategy); protocols = Object.keys(DomainSchema); domainSchemaTranslations = domainSchemaTranslations; + platformTypes = Object.keys(PlatformType); + platformTypeTranslations = platformTypeTranslations; templateProvider = ['Custom']; @@ -293,6 +296,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha additionalInfo: this.fb.group({ providerName: [additionalInfo?.providerName ? additionalInfo?.providerName : defaultProviderName, Validators.required] }), + platforms: [registration?.platforms ? registration.platforms : []], loginButtonLabel: [registration?.loginButtonLabel ? registration.loginButtonLabel : null, Validators.required], loginButtonIcon: [registration?.loginButtonIcon ? registration.loginButtonIcon : null], clientId: [registration?.clientId ? registration.clientId : '', Validators.required], diff --git a/ui-ngx/src/app/shared/models/oauth2.models.ts b/ui-ngx/src/app/shared/models/oauth2.models.ts index 93b147c887..d334e89bdd 100644 --- a/ui-ngx/src/app/shared/models/oauth2.models.ts +++ b/ui-ngx/src/app/shared/models/oauth2.models.ts @@ -63,6 +63,20 @@ export enum TenantNameStrategy{ CUSTOM = 'CUSTOM' } +export enum PlatformType { + WEB = 'WEB', + ANDROID = 'ANDROID', + IOS = 'IOS' +} + +export const platformTypeTranslations = new Map( + [ + [PlatformType.WEB, 'admin.oauth2.platform-web'], + [PlatformType.ANDROID, 'admin.oauth2.platform-android'], + [PlatformType.IOS, 'admin.oauth2.platform-ios'] + ] +); + export interface OAuth2ClientRegistrationTemplate extends OAuth2RegistrationInfo{ comment: string; createdTime: number; @@ -80,6 +94,7 @@ export interface OAuth2RegistrationInfo { accessTokenUri: string; authorizationUri: string; scope: string[]; + platforms: PlatformType[]; jwkSetUri?: string; userInfoUri: string; clientAuthenticationMethod: ClientAuthenticationMethod; diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 5fbb6ed3fb..d602241fbe 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -228,7 +228,12 @@ "mobile-callback-url-scheme": "Callback URL scheme", "add-mobile-app": "Add application", "delete-mobile-app": "Delete application info", - "providers": "Providers" + "providers": "Providers", + "platform-web": "Web", + "platform-android": "Android", + "platform-ios": "iOS", + "all-platforms": "All platforms", + "allowed-platforms": "Allowed platforms" } }, "alarm": { From 1ed624d30bf67c7f893e21bb1d643e480b43e551 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Fri, 11 Jun 2021 13:37:23 +0300 Subject: [PATCH 09/11] Introduce mobile app oauth2 request authentication using application token signed by application secret. --- .../main/data/upgrade/3.2.2/schema_update.sql | 2 +- ...tomOAuth2AuthorizationRequestResolver.java | 26 +++++-- .../model/token/OAuth2AppTokenFactory.java | 69 +++++++++++++++++++ .../server/dao/oauth2/OAuth2Service.java | 2 +- .../common/data/oauth2/OAuth2Mobile.java | 4 +- .../common/data/oauth2/OAuth2MobileInfo.java | 2 +- .../server/dao/model/ModelConstants.java | 2 +- .../dao/model/sql/OAuth2MobileEntity.java | 8 +-- .../dao/oauth2/OAuth2RegistrationDao.java | 2 +- .../server/dao/oauth2/OAuth2ServiceImpl.java | 37 ++++++++-- .../server/dao/oauth2/OAuth2Utils.java | 4 +- .../sql/oauth2/JpaOAuth2RegistrationDao.java | 4 +- .../oauth2/OAuth2RegistrationRepository.java | 6 +- .../resources/sql/schema-entities-hsql.sql | 2 +- .../main/resources/sql/schema-entities.sql | 2 +- .../dao/service/BaseOAuth2ServiceTest.java | 34 +++++---- ui-ngx/src/app/core/utils.ts | 11 +++ .../admin/oauth2-settings.component.html | 55 ++++++++++----- .../pages/admin/oauth2-settings.component.ts | 7 +- .../button/copy-button.component.html | 9 ++- .../button/copy-button.component.ts | 14 +--- ui-ngx/src/app/shared/models/oauth2.models.ts | 2 +- .../assets/locale/locale.constant-en_US.json | 6 +- 23 files changed, 226 insertions(+), 84 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/security/model/token/OAuth2AppTokenFactory.java diff --git a/application/src/main/data/upgrade/3.2.2/schema_update.sql b/application/src/main/data/upgrade/3.2.2/schema_update.sql index 30bc80a458..d57668de0d 100644 --- a/application/src/main/data/upgrade/3.2.2/schema_update.sql +++ b/application/src/main/data/upgrade/3.2.2/schema_update.sql @@ -136,7 +136,7 @@ CREATE TABLE IF NOT EXISTS oauth2_mobile ( oauth2_params_id uuid NOT NULL, created_time bigint NOT NULL, pkg_name varchar(255), - callback_url_scheme varchar(255), + app_secret varchar(2048), CONSTRAINT fk_mobile_oauth2_params FOREIGN KEY (oauth2_params_id) REFERENCES oauth2_params(id) ON DELETE CASCADE, CONSTRAINT oauth2_mobile_unq_key UNIQUE (oauth2_params_id, pkg_name) ); diff --git a/application/src/main/java/org/thingsboard/server/config/CustomOAuth2AuthorizationRequestResolver.java b/application/src/main/java/org/thingsboard/server/config/CustomOAuth2AuthorizationRequestResolver.java index bc93253a73..26c230455b 100644 --- a/application/src/main/java/org/thingsboard/server/config/CustomOAuth2AuthorizationRequestResolver.java +++ b/application/src/main/java/org/thingsboard/server/config/CustomOAuth2AuthorizationRequestResolver.java @@ -39,6 +39,7 @@ import org.springframework.web.util.UriComponentsBuilder; import org.thingsboard.server.dao.oauth2.OAuth2Configuration; import org.thingsboard.server.dao.oauth2.OAuth2Service; import org.thingsboard.server.service.security.auth.oauth2.TbOAuth2ParameterNames; +import org.thingsboard.server.service.security.model.token.OAuth2AppTokenFactory; import org.thingsboard.server.utils.MiscUtils; import javax.servlet.http.HttpServletRequest; @@ -69,6 +70,9 @@ public class CustomOAuth2AuthorizationRequestResolver implements OAuth2Authoriza @Autowired private OAuth2Service oAuth2Service; + @Autowired + private OAuth2AppTokenFactory oAuth2AppTokenFactory; + @Autowired(required = false) private OAuth2Configuration oauth2Configuration; @@ -78,7 +82,8 @@ public class CustomOAuth2AuthorizationRequestResolver implements OAuth2Authoriza String registrationId = this.resolveRegistrationId(request); String redirectUriAction = getAction(request, "login"); String appPackage = getAppPackage(request); - return resolve(request, registrationId, redirectUriAction, appPackage); + String appToken = getAppToken(request); + return resolve(request, registrationId, redirectUriAction, appPackage, appToken); } @Override @@ -88,7 +93,8 @@ public class CustomOAuth2AuthorizationRequestResolver implements OAuth2Authoriza } String redirectUriAction = getAction(request, "authorize"); String appPackage = getAppPackage(request); - return resolve(request, registrationId, redirectUriAction, appPackage); + String appToken = getAppToken(request); + return resolve(request, registrationId, redirectUriAction, appPackage, appToken); } private String getAction(HttpServletRequest request, String defaultAction) { @@ -103,8 +109,12 @@ public class CustomOAuth2AuthorizationRequestResolver implements OAuth2Authoriza return request.getParameter("pkg"); } + private String getAppToken(HttpServletRequest request) { + return request.getParameter("appToken"); + } + @SuppressWarnings("deprecation") - private OAuth2AuthorizationRequest resolve(HttpServletRequest request, String registrationId, String redirectUriAction, String appPackage) { + private OAuth2AuthorizationRequest resolve(HttpServletRequest request, String registrationId, String redirectUriAction, String appPackage, String appToken) { if (registrationId == null) { return null; } @@ -117,10 +127,14 @@ public class CustomOAuth2AuthorizationRequestResolver implements OAuth2Authoriza Map attributes = new HashMap<>(); attributes.put(OAuth2ParameterNames.REGISTRATION_ID, clientRegistration.getRegistrationId()); if (!StringUtils.isEmpty(appPackage)) { - String callbackUrlScheme = this.oAuth2Service.findCallbackUrlScheme(UUID.fromString(registrationId), appPackage); - if (StringUtils.isEmpty(callbackUrlScheme)) { - throw new IllegalArgumentException("Invalid package: " + appPackage + ". No package info found for Client Registration."); + if (StringUtils.isEmpty(appToken)) { + throw new IllegalArgumentException("Invalid application token."); } else { + String appSecret = this.oAuth2Service.findAppSecret(UUID.fromString(registrationId), appPackage); + if (StringUtils.isEmpty(appSecret)) { + throw new IllegalArgumentException("Invalid package: " + appPackage + ". No application secret found for Client Registration with given application package."); + } + String callbackUrlScheme = this.oAuth2AppTokenFactory.validateTokenAndGetCallbackUrlScheme(appPackage, appToken, appSecret); attributes.put(TbOAuth2ParameterNames.CALLBACK_URL_SCHEME, callbackUrlScheme); } } diff --git a/application/src/main/java/org/thingsboard/server/service/security/model/token/OAuth2AppTokenFactory.java b/application/src/main/java/org/thingsboard/server/service/security/model/token/OAuth2AppTokenFactory.java new file mode 100644 index 0000000000..fba1598a7f --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/security/model/token/OAuth2AppTokenFactory.java @@ -0,0 +1,69 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.security.model.token; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.ExpiredJwtException; +import io.jsonwebtoken.Jws; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.MalformedJwtException; +import io.jsonwebtoken.SignatureException; +import io.jsonwebtoken.UnsupportedJwtException; +import io.micrometer.core.instrument.util.StringUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.util.Date; +import java.util.concurrent.TimeUnit; + +@Component +@Slf4j +public class OAuth2AppTokenFactory { + + private static final String CALLBACK_URL_SCHEME = "callbackUrlScheme"; + + private static final long MAX_EXPIRATION_TIME_DIFF_MS = TimeUnit.MINUTES.toMillis(5); + + public String validateTokenAndGetCallbackUrlScheme(String appPackage, String appToken, String appSecret) { + Jws jwsClaims; + try { + jwsClaims = Jwts.parser().setSigningKey(appSecret).parseClaimsJws(appToken); + } + catch (UnsupportedJwtException | MalformedJwtException | IllegalArgumentException | SignatureException ex) { + throw new IllegalArgumentException("Invalid Application token: ", ex); + } catch (ExpiredJwtException expiredEx) { + throw new IllegalArgumentException("Application token expired", expiredEx); + } + Claims claims = jwsClaims.getBody(); + Date expiration = claims.getExpiration(); + if (expiration == null) { + throw new IllegalArgumentException("Application token must have expiration date"); + } + long timeDiff = expiration.getTime() - System.currentTimeMillis(); + if (timeDiff > MAX_EXPIRATION_TIME_DIFF_MS) { + throw new IllegalArgumentException("Application token expiration time can't be longer than 5 minutes"); + } + if (!claims.getIssuer().equals(appPackage)) { + throw new IllegalArgumentException("Application token issuer doesn't match application package"); + } + String callbackUrlScheme = claims.get(CALLBACK_URL_SCHEME, String.class); + if (StringUtils.isEmpty(callbackUrlScheme)) { + throw new IllegalArgumentException("Application token doesn't have callbackUrlScheme"); + } + return callbackUrlScheme; + } + +} diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java index 0e9a36c05e..9764137cf2 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java @@ -42,5 +42,5 @@ public interface OAuth2Service { List findAllRegistrations(); - String findCallbackUrlScheme(UUID registrationId, String pkgName); + String findAppSecret(UUID registrationId, String pkgName); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Mobile.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Mobile.java index b3249a1ed5..53a6d41e5c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Mobile.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Mobile.java @@ -31,12 +31,12 @@ public class OAuth2Mobile extends BaseData { private OAuth2ParamsId oauth2ParamsId; private String pkgName; - private String callbackUrlScheme; + private String appSecret; public OAuth2Mobile(OAuth2Mobile mobile) { super(mobile); this.oauth2ParamsId = mobile.oauth2ParamsId; this.pkgName = mobile.pkgName; - this.callbackUrlScheme = mobile.callbackUrlScheme; + this.appSecret = mobile.appSecret; } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2MobileInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2MobileInfo.java index 18fbd49dcd..c04a1a9fd1 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2MobileInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2MobileInfo.java @@ -30,5 +30,5 @@ import lombok.ToString; @Builder public class OAuth2MobileInfo { private String pkgName; - private String callbackUrlScheme; + private String appSecret; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index 32b5afc9aa..87cee53991 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -418,7 +418,7 @@ public class ModelConstants { public static final String OAUTH2_MOBILE_COLUMN_FAMILY_NAME = "oauth2_mobile"; public static final String OAUTH2_PARAMS_ID_PROPERTY = "oauth2_params_id"; public static final String OAUTH2_PKG_NAME_PROPERTY = "pkg_name"; - public static final String OAUTH2_CALLBACK_URL_SCHEME_PROPERTY = "callback_url_scheme"; + public static final String OAUTH2_APP_SECRET_PROPERTY = "app_secret"; public static final String OAUTH2_CLIENT_REGISTRATION_INFO_COLUMN_FAMILY_NAME = "oauth2_client_registration_info"; public static final String OAUTH2_CLIENT_REGISTRATION_COLUMN_FAMILY_NAME = "oauth2_client_registration"; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2MobileEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2MobileEntity.java index 403f7af958..a6517011b0 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2MobileEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2MobileEntity.java @@ -40,8 +40,8 @@ public class OAuth2MobileEntity extends BaseSqlEntity { @Column(name = ModelConstants.OAUTH2_PKG_NAME_PROPERTY) private String pkgName; - @Column(name = ModelConstants.OAUTH2_CALLBACK_URL_SCHEME_PROPERTY) - private String callbackUrlScheme; + @Column(name = ModelConstants.OAUTH2_APP_SECRET_PROPERTY) + private String appSecret; public OAuth2MobileEntity() { super(); @@ -56,7 +56,7 @@ public class OAuth2MobileEntity extends BaseSqlEntity { this.oauth2ParamsId = mobile.getOauth2ParamsId().getId(); } this.pkgName = mobile.getPkgName(); - this.callbackUrlScheme = mobile.getCallbackUrlScheme(); + this.appSecret = mobile.getAppSecret(); } @Override @@ -66,7 +66,7 @@ public class OAuth2MobileEntity extends BaseSqlEntity { mobile.setCreatedTime(createdTime); mobile.setOauth2ParamsId(new OAuth2ParamsId(oauth2ParamsId)); mobile.setPkgName(pkgName); - mobile.setCallbackUrlScheme(callbackUrlScheme); + mobile.setAppSecret(appSecret); return mobile; } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2RegistrationDao.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2RegistrationDao.java index de80334577..87f0d56460 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2RegistrationDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2RegistrationDao.java @@ -29,6 +29,6 @@ public interface OAuth2RegistrationDao extends Dao { List findByOAuth2ParamsId(UUID oauth2ParamsId); - String findCallbackUrlScheme(UUID id, String pkgName); + String findAppSecret(UUID id, String pkgName); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index 6c2126a005..83a134ea36 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -21,7 +21,23 @@ import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.oauth2.*; +import org.thingsboard.server.common.data.oauth2.MapperType; +import org.thingsboard.server.common.data.oauth2.OAuth2BasicMapperConfig; +import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; +import org.thingsboard.server.common.data.oauth2.OAuth2CustomMapperConfig; +import org.thingsboard.server.common.data.oauth2.OAuth2Domain; +import org.thingsboard.server.common.data.oauth2.OAuth2DomainInfo; +import org.thingsboard.server.common.data.oauth2.OAuth2Info; +import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; +import org.thingsboard.server.common.data.oauth2.OAuth2Mobile; +import org.thingsboard.server.common.data.oauth2.OAuth2MobileInfo; +import org.thingsboard.server.common.data.oauth2.OAuth2Params; +import org.thingsboard.server.common.data.oauth2.OAuth2ParamsInfo; +import org.thingsboard.server.common.data.oauth2.OAuth2Registration; +import org.thingsboard.server.common.data.oauth2.OAuth2RegistrationInfo; +import org.thingsboard.server.common.data.oauth2.PlatformType; +import org.thingsboard.server.common.data.oauth2.SchemeType; +import org.thingsboard.server.common.data.oauth2.TenantNameStrategyType; import org.thingsboard.server.common.data.oauth2.deprecated.ClientRegistrationDto; import org.thingsboard.server.common.data.oauth2.deprecated.DomainInfo; import org.thingsboard.server.common.data.oauth2.deprecated.ExtendedOAuth2ClientRegistrationInfo; @@ -36,7 +52,11 @@ import org.thingsboard.server.dao.oauth2.deprecated.OAuth2ClientRegistrationDao; import org.thingsboard.server.dao.oauth2.deprecated.OAuth2ClientRegistrationInfoDao; import javax.transaction.Transactional; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.UUID; import java.util.function.Consumer; import java.util.stream.Collectors; @@ -164,11 +184,11 @@ public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Se } @Override - public String findCallbackUrlScheme(UUID id, String pkgName) { - log.trace("Executing findCallbackUrlScheme [{}][{}]", id, pkgName); + public String findAppSecret(UUID id, String pkgName) { + log.trace("Executing findAppSecret [{}][{}]", id, pkgName); validateId(id, INCORRECT_CLIENT_REGISTRATION_ID + id); validateString(pkgName, "Incorrect package name"); - return oauth2RegistrationDao.findCallbackUrlScheme(id, pkgName); + return oauth2RegistrationDao.findAppSecret(id, pkgName); } @@ -323,8 +343,11 @@ public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Se if (StringUtils.isEmpty(mobileInfo.getPkgName())) { throw new DataValidationException("Package should be specified!"); } - if (StringUtils.isEmpty(mobileInfo.getCallbackUrlScheme())) { - throw new DataValidationException("Callback URL scheme should be specified!"); + if (StringUtils.isEmpty(mobileInfo.getAppSecret())) { + throw new DataValidationException("Application secret should be specified!"); + } + if (mobileInfo.getAppSecret().length() < 16) { + throw new DataValidationException("Application secret should be at least 16 characters!"); } } oauth2Params.getMobileInfos().stream() diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java index 896c619c82..c34875d014 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java @@ -148,7 +148,7 @@ public class OAuth2Utils { public static OAuth2MobileInfo toOAuth2MobileInfo(OAuth2Mobile mobile) { return OAuth2MobileInfo.builder() .pkgName(mobile.getPkgName()) - .callbackUrlScheme(mobile.getCallbackUrlScheme()) + .appSecret(mobile.getAppSecret()) .build(); } @@ -191,7 +191,7 @@ public class OAuth2Utils { OAuth2Mobile mobile = new OAuth2Mobile(); mobile.setOauth2ParamsId(oauth2ParamsId); mobile.setPkgName(mobileInfo.getPkgName()); - mobile.setCallbackUrlScheme(mobileInfo.getCallbackUrlScheme()); + mobile.setAppSecret(mobileInfo.getAppSecret()); return mobile; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2RegistrationDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2RegistrationDao.java index 0b09f7ddea..ef35371b6e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2RegistrationDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2RegistrationDao.java @@ -57,8 +57,8 @@ public class JpaOAuth2RegistrationDao extends JpaAbstractDao findByOauth2ParamsId(UUID oauth2ParamsId); - @Query("SELECT mobile.callbackUrlScheme " + + @Query("SELECT mobile.appSecret " + "FROM OAuth2MobileEntity mobile " + "LEFT JOIN OAuth2RegistrationEntity reg on mobile.oauth2ParamsId = reg.oauth2ParamsId " + "WHERE reg.id = :registrationId " + "AND mobile.pkgName = :pkgName") - String findCallbackUrlScheme(@Param("registrationId") UUID id, - @Param("pkgName") String pkgName); + String findAppSecret(@Param("registrationId") UUID id, + @Param("pkgName") String pkgName); } diff --git a/dao/src/main/resources/sql/schema-entities-hsql.sql b/dao/src/main/resources/sql/schema-entities-hsql.sql index ad3a023a41..16522becde 100644 --- a/dao/src/main/resources/sql/schema-entities-hsql.sql +++ b/dao/src/main/resources/sql/schema-entities-hsql.sql @@ -431,7 +431,7 @@ CREATE TABLE IF NOT EXISTS oauth2_mobile ( oauth2_params_id uuid NOT NULL, created_time bigint NOT NULL, pkg_name varchar(255), - callback_url_scheme varchar(255), + app_secret varchar(2048), CONSTRAINT fk_mobile_oauth2_params FOREIGN KEY (oauth2_params_id) REFERENCES oauth2_params(id) ON DELETE CASCADE, CONSTRAINT oauth2_mobile_unq_key UNIQUE (oauth2_params_id, pkg_name) ); diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index 890d639a01..88386a11dd 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -468,7 +468,7 @@ CREATE TABLE IF NOT EXISTS oauth2_mobile ( oauth2_params_id uuid NOT NULL, created_time bigint NOT NULL, pkg_name varchar(255), - callback_url_scheme varchar(255), + app_secret varchar(2048), CONSTRAINT fk_mobile_oauth2_params FOREIGN KEY (oauth2_params_id) REFERENCES oauth2_params(id) ON DELETE CASCADE, CONSTRAINT oauth2_mobile_unq_key UNIQUE (oauth2_params_id, pkg_name) ); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java index ae2ff054ec..4077bb2176 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java @@ -15,8 +15,8 @@ */ package org.thingsboard.server.dao.service; -import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.collect.Lists; +import org.apache.commons.lang3.RandomStringUtils; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -487,7 +487,7 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { } @Test - public void testFindCallbackUrlScheme() { + public void testFindAppSecret() { OAuth2Info oAuth2Info = new OAuth2Info(true, Lists.newArrayList( OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( @@ -496,8 +496,8 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { OAuth2DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() )) .mobileInfos(Lists.newArrayList( - OAuth2MobileInfo.builder().pkgName("com.test.pkg1").callbackUrlScheme("testPkg1Callback").build(), - OAuth2MobileInfo.builder().pkgName("com.test.pkg2").callbackUrlScheme("testPkg2Callback").build() + validMobileInfo("com.test.pkg1", "testPkg1AppSecret"), + validMobileInfo("com.test.pkg2", "testPkg2AppSecret") )) .clientRegistrations(Lists.newArrayList( validRegistrationInfo(), @@ -527,14 +527,14 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { for (OAuth2ClientInfo clientInfo : firstDomainHttpClients) { String[] segments = clientInfo.getUrl().split("/"); String registrationId = segments[segments.length-1]; - String callbackUrlScheme = oAuth2Service.findCallbackUrlScheme(UUID.fromString(registrationId), "com.test.pkg1"); - Assert.assertNotNull(callbackUrlScheme); - Assert.assertEquals("testPkg1Callback", callbackUrlScheme); - callbackUrlScheme = oAuth2Service.findCallbackUrlScheme(UUID.fromString(registrationId), "com.test.pkg2"); - Assert.assertNotNull(callbackUrlScheme); - Assert.assertEquals("testPkg2Callback", callbackUrlScheme); - callbackUrlScheme = oAuth2Service.findCallbackUrlScheme(UUID.fromString(registrationId), "com.test.pkg3"); - Assert.assertNull(callbackUrlScheme); + String appSecret = oAuth2Service.findAppSecret(UUID.fromString(registrationId), "com.test.pkg1"); + Assert.assertNotNull(appSecret); + Assert.assertEquals("testPkg1AppSecret", appSecret); + appSecret = oAuth2Service.findAppSecret(UUID.fromString(registrationId), "com.test.pkg2"); + Assert.assertNotNull(appSecret); + Assert.assertEquals("testPkg2AppSecret", appSecret); + appSecret = oAuth2Service.findAppSecret(UUID.fromString(registrationId), "com.test.pkg3"); + Assert.assertNull(appSecret); } } @@ -548,8 +548,8 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { OAuth2DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() )) .mobileInfos(Lists.newArrayList( - OAuth2MobileInfo.builder().pkgName("com.test.pkg1").callbackUrlScheme("testPkg1Callback").build(), - OAuth2MobileInfo.builder().pkgName("com.test.pkg2").callbackUrlScheme("testPkg2Callback").build() + validMobileInfo("com.test.pkg1", "testPkg1Callback"), + validMobileInfo("com.test.pkg2", "testPkg2Callback") )) .clientRegistrations(Lists.newArrayList( validRegistrationInfo("Google", Arrays.asList(PlatformType.WEB, PlatformType.ANDROID)), @@ -651,4 +651,10 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { ) .build(); } + + private OAuth2MobileInfo validMobileInfo(String pkgName, String appSecret) { + return OAuth2MobileInfo.builder().pkgName(pkgName) + .appSecret(appSecret != null ? appSecret : RandomStringUtils.randomAlphanumeric(24)) + .build(); + } } diff --git a/ui-ngx/src/app/core/utils.ts b/ui-ngx/src/app/core/utils.ts index a287802ec2..aa52c55cbe 100644 --- a/ui-ngx/src/app/core/utils.ts +++ b/ui-ngx/src/app/core/utils.ts @@ -445,3 +445,14 @@ export function validateEntityId(entityId: EntityId | null): boolean { export function isMobileApp(): boolean { return isDefined((window as any).flutter_inappwebview); } + +const alphanumericCharacters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; +const alphanumericCharactersLength = alphanumericCharacters.length; + +export function randomAlphanumeric(length: number): string { + let result = ''; + for ( let i = 0; i < length; i++ ) { + result += alphanumericCharacters.charAt(Math.floor(Math.random() * alphanumericCharactersLength)); + } + return result; +} diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html index c56dc6a793..a983266434 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html @@ -90,23 +90,26 @@ admin.oauth2.redirect-uri-template - + + - - + +
@@ -144,18 +147,32 @@
- + admin.oauth2.mobile-package - + + admin.oauth2.mobile-package-hint {{ 'admin.oauth2.mobile-package-unique' | translate }}
- - admin.oauth2.mobile-callback-url-scheme - - +
+ + admin.oauth2.mobile-app-secret + + + + + {{ 'admin.oauth2.invalid-mobile-app-secret' | translate }} + + +
diff --git a/ui-ngx/src/app/shared/components/button/copy-button.component.ts b/ui-ngx/src/app/shared/components/button/copy-button.component.ts index 8d5d72efe3..60f5db12e0 100644 --- a/ui-ngx/src/app/shared/components/button/copy-button.component.ts +++ b/ui-ngx/src/app/shared/components/button/copy-button.component.ts @@ -26,7 +26,6 @@ import { TranslateService } from '@ngx-translate/core'; }) export class CopyButtonComponent { - private copedIcon = ''; private timer; copied = false; @@ -52,6 +51,9 @@ export class CopyButtonComponent { @Input() style: {[key: string]: any} = {}; + @Input() + color: string; + @Output() successCopied = new EventEmitter(); @@ -67,23 +69,13 @@ export class CopyButtonComponent { } this.clipboardService.copy(this.copyText); this.successCopied.emit(this.copyText); - this.copedIcon = 'done'; this.copied = true; this.timer = setTimeout(() => { - this.copedIcon = null; this.copied = false; this.cd.detectChanges(); }, 1500); } - get iconSymbol(): string { - return this.copedIcon || this.icon; - } - - get mdiIconSymbol(): string { - return this.copedIcon ? '' : this.mdiIcon; - } - get matTooltipText(): string { return this.copied ? this.translate.instant('ota-update.copied') : this.tooltipText; } diff --git a/ui-ngx/src/app/shared/models/oauth2.models.ts b/ui-ngx/src/app/shared/models/oauth2.models.ts index d334e89bdd..b434302a9a 100644 --- a/ui-ngx/src/app/shared/models/oauth2.models.ts +++ b/ui-ngx/src/app/shared/models/oauth2.models.ts @@ -34,7 +34,7 @@ export interface OAuth2DomainInfo { export interface OAuth2MobileInfo { pkgName: string; - callbackUrlScheme: string; + appSecret: string; } export enum DomainSchema{ diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index abe4cba802..f6112713f4 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -224,8 +224,12 @@ "mobile-apps": "Mobile applications", "no-mobile-apps": "No applications configured", "mobile-package": "Application package", + "mobile-package-placeholder": "Ex.: my.example.app", + "mobile-package-hint": "For Android: your own unique Application ID. For iOS: Product bundle identifier.", "mobile-package-unique": "Application package must be unique.", - "mobile-callback-url-scheme": "Callback URL scheme", + "mobile-app-secret": "Application secret", + "invalid-mobile-app-secret": "Application secret must contain only alphanumeric characters and must be between 16 and 2048 characters long.", + "copy-mobile-app-secret": "Copy application secret", "add-mobile-app": "Add application", "delete-mobile-app": "Delete application info", "providers": "Providers", From 225e0f1a1fec0c6a0ba30ef72fe5e03a956110ea Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 11 Jun 2021 15:46:10 +0300 Subject: [PATCH 10/11] Improvement RPC debug terminal widget: fixed parse command --- .../data/json/system/widget_bundles/control_widgets.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/application/src/main/data/json/system/widget_bundles/control_widgets.json b/application/src/main/data/json/system/widget_bundles/control_widgets.json index 4756b149c3..1de1bae6f1 100644 --- a/application/src/main/data/json/system/widget_bundles/control_widgets.json +++ b/application/src/main/data/json/system/widget_bundles/control_widgets.json @@ -18,8 +18,8 @@ "resources": [], "templateHtml": "
", "templateCss": ".cmd .cursor.blink {\n -webkit-animation-name: terminal-underline;\n -moz-animation-name: terminal-underline;\n -ms-animation-name: terminal-underline;\n animation-name: terminal-underline;\n}\n.terminal .inverted, .cmd .inverted {\n border-bottom-color: #aaa;\n}\n\n", - "controllerScript": "var requestTimeout = 500;\nvar multiParams = false;\nvar useRowStyleFunction = false;\nvar styleObj = {};\n\nself.onInit = function() {\n var subscription = self.ctx.defaultSubscription;\n var rpcEnabled = subscription.rpcEnabled;\n var deviceName = 'Simulated';\n var prompt;\n if (subscription.targetDeviceName && subscription.targetDeviceName.length) {\n deviceName = subscription.targetDeviceName;\n }\n if (self.ctx.settings.requestTimeout) {\n requestTimeout = self.ctx.settings.requestTimeout;\n }\n if (self.ctx.settings.multiParams) {\n multiParams = self.ctx.settings.multiParams;\n }\n if (self.ctx.settings.useRowStyleFunction && self.ctx.settings.rowStyleFunction) {\n try {\n var style = self.ctx.settings.rowStyleFunction;\n styleObj = JSON.parse(style);\n if ((typeof styleObj !== \"object\")) {\n styleObj = null;\n throw new URIError(`${style === null ? 'null' : typeof style} instead of style object`);\n }\n else if (typeof styleObj === \"object\" && (typeof styleObj.length) === \"number\") {\n styleObj = null;\n throw new URIError('Array instead of style object');\n }\n }\n catch (e) {\n console.log(`Row style function in widget ` +\n `returns '${e}'. Please check your row style function.`); \n }\n useRowStyleFunction = self.ctx.settings.useRowStyleFunction;\n \n }\n var greetings = 'Welcome to ThingsBoard RPC debug terminal.\\n\\n';\n if (!rpcEnabled) {\n greetings += 'Target device is not set!\\n\\n';\n prompt = '';\n } else {\n greetings += 'Current target device for RPC commands: [[b;#fff;]' + deviceName + ']\\n\\n';\n greetings += 'Please type [[b;#fff;]\\'help\\'] to see usage.\\n';\n prompt = '[[b;#8bc34a;]' + deviceName +']> ';\n }\n \n var terminal = $('#device-terminal', self.ctx.$container).terminal(\n function(command) {\n if (command !== '') {\n try {\n var localCommand = command.trim();\n var requestUUID = uuidv4();\n if (localCommand === 'help') {\n printUsage(this);\n } else {\n var cmdObj = $.terminal.parse_command(localCommand);\n if (cmdObj.args) {\n if (!multiParams && cmdObj.args.length > 1) {\n this.error(\"Wrong number of arguments!\");\n this.echo(' ');\n }\n else {\n if (cmdObj.args.length) {\n var params = getMultiParams(cmdObj.args);\n }\n performRpc(this, cmdObj.name, params, requestUUID);\n }\n }\n \n }\n } catch(e) {\n this.error(new String(e));\n }\n } else {\n this.echo('');\n }\n }, {\n greetings: greetings,\n prompt: prompt,\n enabled: rpcEnabled\n });\n \n if (styleObj && styleObj !== null) {\n terminal.css(styleObj);\n }\n \n if (!rpcEnabled) {\n terminal.error('No RPC target detected!').pause();\n }\n}\n\n\nfunction printUsage(terminal) {\n var commandsListText = '\\n[[b;#fff;]Usage:]\\n';\n commandsListText += ' [params body]]\\n\\n';\n commandsListText += '[[b;#fff;]Example 1 (multiParams===false):]\\n'; \n commandsListText += ' myRemoteMethod1 myText\\n\\n'; \n commandsListText += '[[b;#fff;]Example 2 (multiParams===false):]\\n'; \n commandsListText += ' myOtherRemoteMethod \"{\\\\\"key1\\\\\":2,\\\\\"key2\\\\\":\\\\\"myVal\\\\\"}\"\\n\\n'; \n commandsListText += '[[b;#fff;]Example 3 (multiParams===true)]\\n'; \n commandsListText += ' [params body] = \"all the string after the method, including spaces\"]\\n';\n commandsListText += ' myOtherRemoteMethod \"{\\\\\"key1\\\\\": \"battery level\", \\\\\"key2\\\\\": \\\\\"myVal\\\\\"}\"\\n'; \n terminal.echo(new String(commandsListText));\n}\n\nfunction performRpc(terminal, method, params, requestUUID) {\n terminal.pause();\n self.ctx.controlApi.sendTwoWayCommand(method, params, requestTimeout, requestUUID).subscribe(\n function success(responseBody) {\n terminal.echo(JSON.stringify(responseBody));\n terminal.echo(' ');\n terminal.resume();\n },\n function fail() {\n var errorText = self.ctx.defaultSubscription.rpcErrorText;\n terminal.error(errorText);\n terminal.echo(' ');\n terminal.resume();\n }\n );\n}\n\nfunction getMultiParams(cmdObj) {\n var params = \"\";\n cmdObj.forEach((element) => {\n try {\n params += \" \" + JSON.strigify(JSON.parse(element));\n } catch (e) {\n params += \" \" + element;\n }\n })\n return params.trim();\n}\n\n\nfunction uuidv4() {\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {\n var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);\n return v.toString(16);\n });\n}\n\n \nself.onDestroy = function() {\n}", - "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"requestTimeout\": {\n \"title\": \"RPC request timeout (ms)\",\n \"type\": \"number\",\n \"default\": 500\n },\n \"multiParams\": {\n \"title\": \"RPC params All line\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"useRowStyleFunction\": {\n \"title\": \"Use row style function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"rowStyleFunction\": {\n \"title\": \"Row style function: f(entity, ctx)\",\n \"type\": \"string\",\n \"default\": \"\"\n }\n },\n \"required\": [\"requestTimeout\"]\n },\n \"form\": [\n \"requestTimeout\",\n \"multiParams\",\n \"useRowStyleFunction\",\n {\n \"key\": \"rowStyleFunction\",\n \"type\": \"javascript\",\n \"condition\": \"model.useRowStyleFunction === true\"\n }\n ]\n}", + "controllerScript": "var requestTimeout = 500;\n\nself.onInit = function() {\n var subscription = self.ctx.defaultSubscription;\n var rpcEnabled = subscription.rpcEnabled;\n var deviceName = 'Simulated';\n var prompt;\n if (subscription.targetDeviceName && subscription.targetDeviceName.length) {\n deviceName = subscription.targetDeviceName;\n }\n if (self.ctx.settings.requestTimeout) {\n requestTimeout = self.ctx.settings.requestTimeout;\n }\n var greetings = 'Welcome to ThingsBoard RPC debug terminal.\\n\\n';\n if (!rpcEnabled) {\n greetings += 'Target device is not set!\\n\\n';\n prompt = '';\n } else {\n greetings += 'Current target device for RPC commands: [[b;#fff;]' + deviceName + ']\\n\\n';\n greetings += 'Please type [[b;#fff;]\\'help\\'] to see usage.\\n';\n prompt = '[[b;#8bc34a;]' + deviceName +']> ';\n }\n \n var terminal = $('#device-terminal', self.ctx.$container).terminal(\n function(command) {\n if (command !== '') {\n try {\n var localCommand = command.trim();\n var requestUUID = uuidv4();\n if (localCommand === 'help') {\n printUsage(this);\n } else {\n var spaceIndex = localCommand.indexOf(' ');\n if (spaceIndex === -1 && !localCommand.length) {\n this.error(\"Wrong number of arguments!\");\n this.echo(' ');\n } else {\n var params;\n if (spaceIndex === -1) {\n spaceIndex = localCommand.length;\n }\n var name = localCommand.substr(0, spaceIndex);\n var args = localCommand.substr(spaceIndex + 1);\n if (args.length) {\n try {\n params = JSON.parse(args);\n } catch (e) {\n params = args;\n }\n }\n performRpc(this, name, params, requestUUID);\n }\n }\n } catch(e) {\n this.error(new String(e));\n }\n } else {\n this.echo('');\n }\n }, {\n greetings: greetings,\n prompt: prompt,\n enabled: rpcEnabled\n });\n \n if (!rpcEnabled) {\n terminal.error('No RPC target detected!').pause();\n }\n}\n\n\nfunction printUsage(terminal) {\n var commandsListText = '\\n[[b;#fff;]Usage:]\\n';\n commandsListText += ' [params body]]\\n\\n';\n commandsListText += '[[b;#fff;]Example 1:]\\n'; \n commandsListText += ' myRemoteMethod1 myText\\n\\n'; \n commandsListText += '[[b;#fff;]Example 2:]\\n'; \n commandsListText += ' myOtherRemoteMethod \"{\\\\\"key1\\\\\": 2, \\\\\"key2\\\\\": \\\\\"myVal\\\\\"}\"\\n'; \n terminal.echo(new String(commandsListText));\n}\n\n\nfunction performRpc(terminal, method, params, requestUUID) {\n terminal.pause();\n self.ctx.controlApi.sendTwoWayCommand(method, params, requestTimeout, requestUUID).subscribe(\n function success(responseBody) {\n terminal.echo(JSON.stringify(responseBody));\n terminal.echo(' ');\n terminal.resume();\n },\n function fail() {\n var errorText = self.ctx.defaultSubscription.rpcErrorText;\n terminal.error(errorText);\n terminal.echo(' ');\n terminal.resume();\n }\n );\n}\n\n\nfunction uuidv4() {\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {\n var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);\n return v.toString(16);\n });\n}\n\n \nself.onDestroy = function() {\n}", + "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"requestTimeout\": {\n \"title\": \"RPC request timeout (ms)\",\n \"type\": \"number\",\n \"default\": 500\n }\n },\n \"required\": [\"requestTimeout\"]\n },\n \"form\": [\n \"requestTimeout\"\n ]\n}", "dataKeySettingsSchema": "{}\n", "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":true,\"backgroundColor\":\"#010101\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"parseGpioStatusFunction\":\"return body[pin] === true;\",\"gpioStatusChangeRequest\":{\"method\":\"setGpioStatus\",\"paramsBody\":\"{\\n \\\"pin\\\": \\\"{$pin}\\\",\\n \\\"enabled\\\": \\\"{$enabled}\\\"\\n}\"},\"requestTimeout\":500,\"switchPanelBackgroundColor\":\"#b71c1c\",\"gpioStatusRequest\":{\"method\":\"getGpioStatus\",\"paramsBody\":\"{}\"},\"gpioList\":[{\"pin\":1,\"label\":\"GPIO 1\",\"row\":0,\"col\":0,\"_uniqueKey\":0},{\"pin\":2,\"label\":\"GPIO 2\",\"row\":0,\"col\":1,\"_uniqueKey\":1},{\"pin\":3,\"label\":\"GPIO 3\",\"row\":1,\"col\":0,\"_uniqueKey\":2}]},\"title\":\"RPC debug terminal\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" } @@ -151,4 +151,4 @@ } } ] -} \ No newline at end of file +} From 51814f4017b5971e01e60b9204d57d59583e87dc Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 11 Jun 2021 17:28:54 +0300 Subject: [PATCH 11/11] UI: Refactoring lwm2m device profile: security server config and ota update strategy --- .../server/controller/Lwm2mController.java | 10 +- .../LwM2MServerSecurityInfoRepository.java | 37 +- .../data/lwm2m/ServerSecurityConfig.java | 2 + .../app/core/http/device-profile.service.ts | 24 +- .../lwm2m-device-config-server.component.html | 71 ++-- .../lwm2m-device-config-server.component.ts | 244 ++++++------- ...ile-transport-configuration.component.html | 335 ++++++++---------- ...ile-transport-configuration.component.scss | 27 ++ ...ofile-transport-configuration.component.ts | 87 +++-- .../lwm2m/lwm2m-profile-config.models.ts | 40 +-- .../assets/locale/locale.constant-en_US.json | 41 ++- 11 files changed, 419 insertions(+), 499 deletions(-) create mode 100644 ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.scss diff --git a/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java b/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java index 9e6d393b30..d94d26fc87 100644 --- a/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java +++ b/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java @@ -17,7 +17,6 @@ package org.thingsboard.server.controller; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; -import org.eclipse.leshan.core.SecurityMode; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; @@ -45,14 +44,11 @@ import java.util.Map; public class Lwm2mController extends BaseController { @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/lwm2m/deviceProfile/bootstrap/{securityMode}/{bootstrapServerIs}", method = RequestMethod.GET) + @RequestMapping(value = "/lwm2m/deviceProfile/bootstrap/{isBootstrapServer}", method = RequestMethod.GET) @ResponseBody - public ServerSecurityConfig getLwm2mBootstrapSecurityInfo(@PathVariable("securityMode") String strSecurityMode, - @PathVariable("bootstrapServerIs") boolean bootstrapServer) throws ThingsboardException { - checkNotNull(strSecurityMode); + public ServerSecurityConfig getLwm2mBootstrapSecurityInfo(@PathVariable("isBootstrapServer") boolean bootstrapServer) throws ThingsboardException { try { - SecurityMode securityMode = SecurityMode.valueOf(strSecurityMode); - return lwM2MServerSecurityInfoRepository.getServerSecurityInfo(securityMode, bootstrapServer); + return lwM2MServerSecurityInfoRepository.getServerSecurityInfo(bootstrapServer); } catch (Exception e) { throw handleException(e); } diff --git a/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServerSecurityInfoRepository.java b/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServerSecurityInfoRepository.java index 06190cdf70..78f849667f 100644 --- a/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServerSecurityInfoRepository.java +++ b/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServerSecurityInfoRepository.java @@ -18,7 +18,6 @@ package org.thingsboard.server.service.lwm2m; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.eclipse.leshan.core.SecurityMode; import org.eclipse.leshan.core.util.Hex; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Service; @@ -50,40 +49,20 @@ public class LwM2MServerSecurityInfoRepository { private final LwM2MTransportServerConfig serverConfig; private final LwM2MTransportBootstrapConfig bootstrapConfig; - /** - * @param securityMode - * @param bootstrapServer - * @return ServerSecurityConfig more value is default: Important - port, host, publicKey - */ - public ServerSecurityConfig getServerSecurityInfo(SecurityMode securityMode, boolean bootstrapServer) { - ServerSecurityConfig result = getServerSecurityConfig(bootstrapServer ? bootstrapConfig : serverConfig, securityMode); + public ServerSecurityConfig getServerSecurityInfo(boolean bootstrapServer) { + ServerSecurityConfig result = getServerSecurityConfig(bootstrapServer ? bootstrapConfig : serverConfig); result.setBootstrapServerIs(bootstrapServer); return result; } - private ServerSecurityConfig getServerSecurityConfig(LwM2MSecureServerConfig serverConfig, SecurityMode securityMode) { + private ServerSecurityConfig getServerSecurityConfig(LwM2MSecureServerConfig serverConfig) { ServerSecurityConfig bsServ = new ServerSecurityConfig(); bsServ.setServerId(serverConfig.getId()); - switch (securityMode) { - case NO_SEC: - bsServ.setHost(serverConfig.getHost()); - bsServ.setPort(serverConfig.getPort()); - bsServ.setServerPublicKey(""); - break; - case PSK: - bsServ.setHost(serverConfig.getSecureHost()); - bsServ.setPort(serverConfig.getSecurePort()); - bsServ.setServerPublicKey(""); - break; - case RPK: - case X509: - bsServ.setHost(serverConfig.getSecureHost()); - bsServ.setPort(serverConfig.getSecurePort()); - bsServ.setServerPublicKey(getPublicKey(serverConfig.getCertificateAlias(), this.serverConfig.getPublicX(), this.serverConfig.getPublicY())); - break; - default: - break; - } + bsServ.setHost(serverConfig.getHost()); + bsServ.setPort(serverConfig.getPort()); + bsServ.setSecurityHost(serverConfig.getSecureHost()); + bsServ.setSecurityPort(serverConfig.getSecurePort()); + bsServ.setServerPublicKey(getPublicKey(serverConfig.getCertificateAlias(), this.serverConfig.getPublicX(), this.serverConfig.getPublicY())); return bsServ; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/lwm2m/ServerSecurityConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/lwm2m/ServerSecurityConfig.java index 8777e08066..8220702ec8 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/lwm2m/ServerSecurityConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/lwm2m/ServerSecurityConfig.java @@ -20,7 +20,9 @@ import lombok.Data; @Data public class ServerSecurityConfig { String host; + String securityHost; Integer port; + Integer securityPort; String serverPublicKey; boolean bootstrapServerIs = true; Integer clientHoldOffTime = 1; diff --git a/ui-ngx/src/app/core/http/device-profile.service.ts b/ui-ngx/src/app/core/http/device-profile.service.ts index 556567e47b..6107a0853e 100644 --- a/ui-ngx/src/app/core/http/device-profile.service.ts +++ b/ui-ngx/src/app/core/http/device-profile.service.ts @@ -18,20 +18,22 @@ import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { PageLink } from '@shared/models/page/page-link'; import { defaultHttpOptionsFromConfig, RequestConfig } from './http-utils'; -import { Observable, throwError } from 'rxjs'; +import { Observable, of, throwError } from 'rxjs'; import { PageData } from '@shared/models/page/page-data'; import { DeviceProfile, DeviceProfileInfo, DeviceTransportType } from '@shared/models/device.models'; import { isDefinedAndNotNull, isEmptyStr } from '@core/utils'; import { ObjectLwM2M, ServerSecurityConfig } from '@home/components/profile/device/lwm2m/lwm2m-profile-config.models'; import { SortOrder } from '@shared/models/page/sort-order'; import { OtaPackageService } from '@core/http/ota-package.service'; -import { mergeMap } from 'rxjs/operators'; +import { mergeMap, tap } from 'rxjs/operators'; @Injectable({ providedIn: 'root' }) export class DeviceProfileService { + private lwm2mBootstrapSecurityInfoInMemoryCache = new Map(); + constructor( private http: HttpClient, private otaPackageService: OtaPackageService @@ -58,12 +60,18 @@ export class DeviceProfileService { return this.http.get>(url, defaultHttpOptionsFromConfig(config)); } - public getLwm2mBootstrapSecurityInfo(securityMode: string, bootstrapServerIs: boolean, - config?: RequestConfig): Observable { - return this.http.get( - `/api/lwm2m/deviceProfile/bootstrap/${securityMode}/${bootstrapServerIs}`, - defaultHttpOptionsFromConfig(config) - ); + public getLwm2mBootstrapSecurityInfo(isBootstrapServer: boolean, config?: RequestConfig): Observable { + const securityConfig = this.lwm2mBootstrapSecurityInfoInMemoryCache.get(isBootstrapServer); + if (securityConfig) { + return of(securityConfig); + } else { + return this.http.get( + `/api/lwm2m/deviceProfile/bootstrap/${isBootstrapServer}`, + defaultHttpOptionsFromConfig(config) + ).pipe( + tap(serverConfig => this.lwm2mBootstrapSecurityInfoInMemoryCache.set(isBootstrapServer, serverConfig)) + ); + } } public getLwm2mObjectsPage(pageLink: PageLink, config?: RequestConfig): Observable> { diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.html index 711b72d615..78f08ce31a 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.html @@ -28,60 +28,45 @@ {{ 'device-profile.lwm2m.server-host' | translate }} - + - {{ 'device-profile.lwm2m.server-host' | translate }} - {{ 'device-profile.lwm2m.required' | translate }} + {{ 'device-profile.lwm2m.server-host-required' | translate }} {{ 'device-profile.lwm2m.server-port' | translate }} - + - {{ 'device-profile.lwm2m.server-port' | translate }} - {{ 'device-profile.lwm2m.required' | translate }} + {{ 'device-profile.lwm2m.server-port-required' | translate }} +
+
{{ 'device-profile.lwm2m.short-id' | translate }} - + - {{ 'device-profile.lwm2m.short-id' | translate }} - {{ 'device-profile.lwm2m.required' | translate }} + {{ 'device-profile.lwm2m.short-id-required' | translate }} -
-
{{ 'device-profile.lwm2m.client-hold-off-time' | translate }} - - {{ 'device-profile.lwm2m.client-hold-off-time' | translate }} - {{ 'device-profile.lwm2m.required' | translate }} + {{ 'device-profile.lwm2m.client-hold-off-time-required' | translate }} - {{ 'device-profile.lwm2m.bootstrap-server-account-timeout' | translate }} - {{ 'device-profile.lwm2m.account-after-timeout' | translate }} + - {{ 'device-profile.lwm2m.bootstrap-server-account-timeout' | translate }} - {{ 'device-profile.lwm2m.required' | translate }} + {{ 'device-profile.lwm2m.account-after-timeout-required' | translate }} - - {{ 'device-profile.lwm2m.bootstrap-server' | translate }} - -
@@ -89,32 +74,22 @@ {{ 'device-profile.lwm2m.server-public-key' | translate }} - {{serverPublicKey.value?.length || 0}}/{{lenMaxServerPublicKey}} + {{serverPublicKey.value?.length || 0}}/{{maxLengthPublicKey}} - {{ 'device-profile.lwm2m.server-public-key' | translate }} - {{ 'device-profile.lwm2m.required' | translate }} + {{ 'device-profile.lwm2m.server-public-key-required' | translate }} - - {{ 'device-profile.lwm2m.server-public-key' | translate }} - {{ 'device-profile.lwm2m.pattern_hex_dec' | translate: { - count: 0} }} + + {{ 'device-profile.lwm2m.server-public-key-pattern' | translate }} - - {{ 'device-profile.lwm2m.server-public-key' | translate }} - {{ 'device-profile.lwm2m.pattern_hex_dec' | translate: { - count: lenMaxServerPublicKey } }} + + {{ 'device-profile.lwm2m.server-public-key-length' | translate: {count: maxLengthPublicKey } }}
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.ts index 55f8db8f84..cccaed895f 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.ts @@ -14,26 +14,24 @@ /// limitations under the License. /// -import { Component, forwardRef, Inject, Input } from '@angular/core'; +import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core'; import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import { - DEFAULT_CLIENT_HOLD_OFF_TIME, - DEFAULT_ID_SERVER, DEFAULT_PORT_BOOTSTRAP_NO_SEC, DEFAULT_PORT_SERVER_NO_SEC, KEY_REGEXP_HEX_DEC, LEN_MAX_PUBLIC_KEY_RPK, LEN_MAX_PUBLIC_KEY_X509, - SECURITY_CONFIG_MODE, - SECURITY_CONFIG_MODE_NAMES, + securityConfigMode, + securityConfigModeNames, ServerSecurityConfig } from './lwm2m-profile-config.models'; -import { coerceBooleanProperty } from '@angular/cdk/coercion'; -import { WINDOW } from '@core/services/window.service'; -import { pairwise, startWith } from 'rxjs/operators'; import { DeviceProfileService } from '@core/http/device-profile.service'; +import { of, Subject } from 'rxjs'; +import { map, mergeMap, takeUntil, tap } from 'rxjs/operators'; +import { Observable } from 'rxjs/internal/Observable'; +import { deepClone } from '@core/utils'; -// @dynamic @Component({ selector: 'tb-profile-lwm2m-device-config-server', templateUrl: './lwm2m-device-config-server.component.html', @@ -46,127 +44,78 @@ import { DeviceProfileService } from '@core/http/device-profile.service'; ] }) -export class Lwm2mDeviceConfigServerComponent implements ControlValueAccessor { +export class Lwm2mDeviceConfigServerComponent implements OnInit, ControlValueAccessor, OnDestroy { - private requiredValue: boolean; private disabled = false; + private destroy$ = new Subject(); + + private securityDefaultConfig: ServerSecurityConfig; - valuePrev = null; serverFormGroup: FormGroup; - securityConfigLwM2MType = SECURITY_CONFIG_MODE; - securityConfigLwM2MTypes = Object.keys(SECURITY_CONFIG_MODE); - credentialTypeLwM2MNamesMap = SECURITY_CONFIG_MODE_NAMES; - lenMinServerPublicKey = 0; - lenMaxServerPublicKey = LEN_MAX_PUBLIC_KEY_RPK; + securityConfigLwM2MType = securityConfigMode; + securityConfigLwM2MTypes = Object.keys(securityConfigMode); + credentialTypeLwM2MNamesMap = securityConfigModeNames; + maxLengthPublicKey = LEN_MAX_PUBLIC_KEY_RPK; currentSecurityMode = null; @Input() - bootstrapServerIs: boolean; + isBootstrapServer = false; - get required(): boolean { - return this.requiredValue; - } + private propagateChange = (v: any) => { }; - @Input() - set required(value: boolean) { - this.requiredValue = coerceBooleanProperty(value); + constructor(public fb: FormBuilder, + private deviceProfileService: DeviceProfileService) { } - constructor(public fb: FormBuilder, - private deviceProfileService: DeviceProfileService, - @Inject(WINDOW) private window: Window) { - this.serverFormGroup = this.initServerGroup(); + ngOnInit(): void { + this.serverFormGroup = this.fb.group({ + host: ['', Validators.required], + port: [this.isBootstrapServer ? DEFAULT_PORT_BOOTSTRAP_NO_SEC : DEFAULT_PORT_SERVER_NO_SEC, [Validators.required, Validators.min(0)]], + securityMode: [securityConfigMode.NO_SEC], + serverPublicKey: ['', Validators.required], + clientHoldOffTime: ['', [Validators.required, Validators.min(0)]], + serverId: ['', [Validators.required, Validators.min(0)]], + bootstrapServerAccountTimeout: ['', [Validators.required, Validators.min(0)]], + }); this.serverFormGroup.get('securityMode').valueChanges.pipe( - startWith(null), - pairwise() - ).subscribe(([previousValue, currentValue]) => { - if (previousValue === null || previousValue !== currentValue) { - this.getLwm2mBootstrapSecurityInfo(currentValue); - this.updateValidate(currentValue); - this.serverFormGroup.updateValueAndValidity(); - } - + tap(securityMode => this.updateValidate(securityMode)), + mergeMap(securityMode => this.getLwm2mBootstrapSecurityInfo(securityMode)), + takeUntil(this.destroy$) + ).subscribe(serverSecurityConfig => { + this.serverFormGroup.patchValue(serverSecurityConfig, {emitEvent: false}); }); - this.serverFormGroup.valueChanges.subscribe(value => { - if (this.disabled !== undefined && !this.disabled) { - this.propagateChangeState(value); - } + this.serverFormGroup.valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe(value => { + this.propagateChangeState(value); }); } - private updateValueFields = (serverData: ServerSecurityConfig): void => { - serverData.bootstrapServerIs = this.bootstrapServerIs; - this.serverFormGroup.patchValue(serverData, {emitEvent: false}); - this.serverFormGroup.get('bootstrapServerIs').disable(); - const securityMode = this.serverFormGroup.get('securityMode').value as SECURITY_CONFIG_MODE; - this.updateValidate(securityMode); + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); } - private updateValidate = (securityMode: SECURITY_CONFIG_MODE): void => { - switch (securityMode) { - case SECURITY_CONFIG_MODE.NO_SEC: - this.setValidatorsNoSecPsk(); - break; - case SECURITY_CONFIG_MODE.PSK: - this.setValidatorsNoSecPsk(); - break; - case SECURITY_CONFIG_MODE.RPK: - this.lenMinServerPublicKey = LEN_MAX_PUBLIC_KEY_RPK; - this.lenMaxServerPublicKey = LEN_MAX_PUBLIC_KEY_RPK; - this.setValidatorsRpkX509(); - break; - case SECURITY_CONFIG_MODE.X509: - this.lenMinServerPublicKey = 0; - this.lenMaxServerPublicKey = LEN_MAX_PUBLIC_KEY_X509; - this.setValidatorsRpkX509(); - break; + writeValue(serverData: ServerSecurityConfig): void { + if (serverData) { + this.serverFormGroup.patchValue(serverData, {emitEvent: false}); + this.updateValidate(serverData.securityMode); } - this.serverFormGroup.updateValueAndValidity(); - } - - private setValidatorsNoSecPsk = (): void => { - this.serverFormGroup.get('serverPublicKey').setValidators([]); - } - - private setValidatorsRpkX509 = (): void => { - this.serverFormGroup.get('serverPublicKey').setValidators([Validators.required, - Validators.pattern(KEY_REGEXP_HEX_DEC), - Validators.minLength(this.lenMinServerPublicKey), - Validators.maxLength(this.lenMaxServerPublicKey)]); - } - - writeValue(value: ServerSecurityConfig): void { - if (value) { - this.updateValueFields(value); + if (!this.securityDefaultConfig){ + this.getLwm2mBootstrapSecurityInfo().subscribe(value => { + if (!serverData) { + this.serverFormGroup.patchValue(value); + } + }); } } - private propagateChange = (v: any) => {}; - registerOnChange(fn: any): void { this.propagateChange = fn; } - private propagateChangeState = (value: any): void => { - if (value !== undefined) { - if (this.valuePrev === null) { - this.valuePrev = 'init'; - } else if (this.valuePrev === 'init') { - this.valuePrev = value; - } else if (JSON.stringify(value) !== JSON.stringify(this.valuePrev)) { - this.valuePrev = value; - if (this.serverFormGroup.valid) { - this.propagateChange(value); - } else { - this.propagateChange(null); - } - } - } - } - setDisabledState(isDisabled: boolean): void { this.disabled = isDisabled; - this.valuePrev = null; if (isDisabled) { this.serverFormGroup.disable({emitEvent: false}); } else { @@ -177,33 +126,76 @@ export class Lwm2mDeviceConfigServerComponent implements ControlValueAccessor { registerOnTouched(fn: any): void { } - private initServerGroup = (): FormGroup => { - const port = this.bootstrapServerIs ? DEFAULT_PORT_BOOTSTRAP_NO_SEC : DEFAULT_PORT_SERVER_NO_SEC; - return this.fb.group({ - host: [this.window.location.hostname, this.required ? Validators.required : ''], - port: [port, this.required ? Validators.required : ''], - bootstrapServerIs: [this.bootstrapServerIs, ''], - securityMode: [this.fb.control(SECURITY_CONFIG_MODE.NO_SEC)], - serverPublicKey: ['', this.required ? Validators.required : ''], - clientHoldOffTime: [DEFAULT_CLIENT_HOLD_OFF_TIME, this.required ? Validators.required : ''], - serverId: [DEFAULT_ID_SERVER, this.required ? Validators.required : ''], - bootstrapServerAccountTimeout: ['', this.required ? Validators.required : ''], - }); + private updateValidate(securityMode: securityConfigMode): void { + switch (securityMode) { + case securityConfigMode.NO_SEC: + case securityConfigMode.PSK: + this.clearValidators(); + break; + case securityConfigMode.RPK: + this.maxLengthPublicKey = LEN_MAX_PUBLIC_KEY_RPK; + this.setValidators(LEN_MAX_PUBLIC_KEY_RPK); + break; + case securityConfigMode.X509: + this.maxLengthPublicKey = LEN_MAX_PUBLIC_KEY_X509; + this.setValidators(0); + break; + } + this.serverFormGroup.get('serverPublicKey').updateValueAndValidity({emitEvent: false}); + } + + private clearValidators(): void { + this.serverFormGroup.get('serverPublicKey').clearValidators(); + } + + private setValidators(minLengthKey: number): void { + this.serverFormGroup.get('serverPublicKey').setValidators([ + Validators.required, + Validators.pattern(KEY_REGEXP_HEX_DEC), + Validators.minLength(minLengthKey), + Validators.maxLength(this.maxLengthPublicKey) + ]); } - private getLwm2mBootstrapSecurityInfo = (mode: string): void => { - this.deviceProfileService.getLwm2mBootstrapSecurityInfo(mode, this.serverFormGroup.get('bootstrapServerIs').value).subscribe( - (serverSecurityConfig) => { - this.serverFormGroup.patchValue({ - host: serverSecurityConfig.host, - port: serverSecurityConfig.port, - serverPublicKey: serverSecurityConfig.serverPublicKey, - clientHoldOffTime: serverSecurityConfig.clientHoldOffTime, - serverId: serverSecurityConfig.serverId, - bootstrapServerAccountTimeout: serverSecurityConfig.bootstrapServerAccountTimeout - }, - {emitEvent: true}); + private propagateChangeState = (value: ServerSecurityConfig): void => { + if (value !== undefined) { + if (this.serverFormGroup.valid) { + this.propagateChange(value); + } else { + this.propagateChange(null); } + } + } + + private getLwm2mBootstrapSecurityInfo(securityMode = securityConfigMode.NO_SEC): Observable { + if (this.securityDefaultConfig) { + return of(this.processingBootstrapSecurityInfo(this.securityDefaultConfig, securityMode)); + } + return this.deviceProfileService.getLwm2mBootstrapSecurityInfo(this.isBootstrapServer).pipe( + map(securityInfo => { + this.securityDefaultConfig = securityInfo; + return this.processingBootstrapSecurityInfo(securityInfo, securityMode); + }) ); } + + private processingBootstrapSecurityInfo(securityConfig: ServerSecurityConfig, securityMode: securityConfigMode): ServerSecurityConfig { + const config = deepClone(securityConfig); + switch (securityMode) { + case securityConfigMode.PSK: + config.port = config.securityPort; + config.host = config.securityHost; + config.serverPublicKey = ''; + break; + case securityConfigMode.RPK: + case securityConfigMode.X509: + config.port = config.securityPort; + config.host = config.securityHost; + break; + case securityConfigMode.NO_SEC: + config.serverPublicKey = ''; + break; + } + return config; + } } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html index e75aedd47c..30b78393d3 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html @@ -15,231 +15,172 @@ limitations under the License. --> -
+
-
- - -
-
- - -
-
-
-
- - -
-
- - - - -
{{ 'device-profile.lwm2m.servers' | translate | uppercase }}
-
-
- -
-
- - {{ 'device-profile.lwm2m.short-id' | translate }} - - - {{ 'device-profile.lwm2m.short-id' | translate }} - {{ 'device-profile.lwm2m.required' | translate }} - - - - {{ 'device-profile.lwm2m.lifetime' | translate }} - - - {{ 'device-profile.lwm2m.lifetime' | translate }} - {{ 'device-profile.lwm2m.required' | translate }} - - - - {{ 'device-profile.lwm2m.default-min-period' | translate }} - - - {{ 'device-profile.lwm2m.default-min-period' | translate }} - {{ 'device-profile.lwm2m.required' | translate }} - - -
-
- - {{ 'device-profile.lwm2m.binding' | translate }} - - - {{ bindingModeTypeNamesMap.get(bindingModeType[bindingMode]) }} - - - -
-
- - {{ 'device-profile.lwm2m.notif-if-disabled' | translate }} - -
-
-
-
-
- - - - -
{{ 'device-profile.lwm2m.bootstrap-server' | translate | uppercase }}
-
-
- -
- - -
-
-
-
- - - - -
{{ 'device-profile.lwm2m.lwm2m-server' | translate | uppercase }}
-
-
- -
- - -
-
-
-
-
+ + + +
- + -
- -
- - - -
{{ 'device-profile.lwm2m.client-strategy' | translate | uppercase }}
-
-
- -
- - {{ 'device-profile.lwm2m.client-strategy-label' | translate }} - - {{ 'device-profile.lwm2m.client-strategy-connect' | translate: - {count: 1} }} - {{ 'device-profile.lwm2m.client-strategy-connect' | translate: - {count: 2} }} - - -
-
-
-
+
+ - -
{{ 'device-profile.lwm2m.ota-update-strategy' | translate | uppercase }}
-
+ {{ 'device-profile.lwm2m.servers' | translate }}
-
- - {{ 'device-profile.lwm2m.fw-update-strategy-label' | translate }} - - {{ 'device-profile.lwm2m.fw-update-strategy' | translate: - {count: 1} }} - {{ 'device-profile.lwm2m.fw-update-strategy' | translate: - {count: 2} }} - {{ 'device-profile.lwm2m.fw-update-strategy' | translate: - {count: 3} }} - +
+ + {{ 'device-profile.lwm2m.short-id' | translate }} + + + {{ 'device-profile.lwm2m.short-id' | translate }} + {{ 'device-profile.lwm2m.required' | translate }} + - - {{ 'device-profile.lwm2m.sw-update-strategy-label' | translate }} - - {{ 'device-profile.lwm2m.sw-update-strategy' | translate: - {count: 1} }} - {{ 'device-profile.lwm2m.sw-update-strategy' | translate: - {count: 2} }} - + + {{ 'device-profile.lwm2m.lifetime' | translate }} + + + {{ 'device-profile.lwm2m.lifetime' | translate }} + {{ 'device-profile.lwm2m.required' | translate }} + + + + {{ 'device-profile.lwm2m.default-min-period' | translate }} + + + {{ 'device-profile.lwm2m.default-min-period' | translate }} + {{ 'device-profile.lwm2m.required' | translate }} +
+ + {{ 'device-profile.lwm2m.binding' | translate }} + + + {{ bindingModeTypeNamesMap.get(bindingModeType[bindingMode]) }} + + + + + {{ 'device-profile.lwm2m.notif-if-disabled' | translate }} + - + - -
{{ 'device-profile.lwm2m.ota-update-recourse' | translate | uppercase }}
-
+ {{ 'device-profile.lwm2m.bootstrap-server' | translate }}
-
-
- - {{ 'device-profile.lwm2m.fw-update-recourse' | translate }} - - - {{ 'device-profile.lwm2m.fw-update-recourse' | translate }} - {{ 'device-profile.lwm2m.required' | translate }} - - -
-
- - {{ 'device-profile.lwm2m.sw-update-recourse' | translate }} - - - {{ 'device-profile.lwm2m.sw-update-recourse' | translate }} - {{ 'device-profile.lwm2m.required' | translate }} - - -
-
+ + +
+
+ + + {{ 'device-profile.lwm2m.lwm2m-server' | translate }} + + + +
+ + +
+
+ device-profile.lwm2m.fw-update + + {{ 'device-profile.lwm2m.fw-update-strategy' | translate }} + + {{ 'device-profile.lwm2m.fw-update-strategy-package' | translate }} + {{ 'device-profile.lwm2m.fw-update-strategy-package-uri' | translate }} + {{ 'device-profile.lwm2m.fw-update-strategy-data' | translate }} + + + + {{ 'device-profile.lwm2m.fw-update-recourse' | translate }} + + + {{ 'device-profile.lwm2m.fw-update-recourse-required' | translate }} + + +
+
+ device-profile.lwm2m.sw-update + + {{ 'device-profile.lwm2m.sw-update-strategy' | translate }} + + {{ 'device-profile.lwm2m.sw-update-strategy-package' | translate }} + {{ 'device-profile.lwm2m.sw-update-strategy-package-uri' | translate }} + + + + {{ 'device-profile.lwm2m.sw-update-recourse' | translate }} + + + {{ 'device-profile.lwm2m.sw-update-recourse-required' | translate }} + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
-
+
Lwm2mDeviceProfileTransportConfigurationComponent), @@ -100,12 +100,38 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro clientStrategy: [1, []], fwUpdateStrategy: [1, []], swUpdateStrategy: [1, []], - fwUpdateRecourse: ['', []], - swUpdateRecourse: ['', []] + fwUpdateRecourse: [{value: '', disabled: true}, []], + swUpdateRecourse: [{value: '', disabled: true}, []] }); this.lwm2mDeviceConfigFormGroup = this.fb.group({ configurationJson: [null, Validators.required] }); + this.lwm2mDeviceProfileFormGroup.get('fwUpdateStrategy').valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe((fwStrategy) => { + if (fwStrategy === 2) { + this.lwm2mDeviceProfileFormGroup.get('fwUpdateRecourse').enable({emitEvent: false}); + this.lwm2mDeviceProfileFormGroup.get('fwUpdateRecourse').patchValue(DEFAULT_FW_UPDATE_RESOURCE, {emitEvent: false}); + this.isFwUpdateStrategy = true; + } else { + this.lwm2mDeviceProfileFormGroup.get('fwUpdateRecourse').disable({emitEvent: false}); + this.isFwUpdateStrategy = false; + } + this.otaUpdateFwStrategyValidate(true); + }); + this.lwm2mDeviceProfileFormGroup.get('swUpdateStrategy').valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe((swStrategy) => { + if (swStrategy === 2) { + this.lwm2mDeviceProfileFormGroup.get('swUpdateRecourse').enable({emitEvent: false}); + this.lwm2mDeviceProfileFormGroup.get('swUpdateRecourse').patchValue(DEFAULT_SW_UPDATE_RESOURCE, {emitEvent: false}); + this.isSwUpdateStrategy = true; + } else { + this.isSwUpdateStrategy = false; + this.lwm2mDeviceProfileFormGroup.get('swUpdateRecourse').disable({emitEvent: false}); + } + this.otaUpdateSwStrategyValidate(true); + }); this.lwm2mDeviceProfileFormGroup.valueChanges.pipe( takeUntil(this.destroy$) ).subscribe((value) => { @@ -176,11 +202,9 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } private updateWriteValue = (value: ModelValue): void => { - let fwResource = this.configurationValue.clientLwM2mSettings.fwUpdateStrategy === '2' && - isDefinedAndNotNull(this.configurationValue.clientLwM2mSettings.fwUpdateRecourse) ? + const fwResource = isDefinedAndNotNull(this.configurationValue.clientLwM2mSettings.fwUpdateRecourse) ? this.configurationValue.clientLwM2mSettings.fwUpdateRecourse : ''; - let swResource = this.configurationValue.clientLwM2mSettings.swUpdateStrategy === '2' && - isDefinedAndNotNull(this.configurationValue.clientLwM2mSettings.fwUpdateRecourse) ? + const swResource = isDefinedAndNotNull(this.configurationValue.clientLwM2mSettings.fwUpdateRecourse) ? this.configurationValue.clientLwM2mSettings.swUpdateRecourse : ''; this.lwm2mDeviceProfileFormGroup.patchValue({ objectIds: value, @@ -193,16 +217,16 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro bootstrapServer: this.configurationValue.bootstrap.bootstrapServer, lwm2mServer: this.configurationValue.bootstrap.lwm2mServer, clientStrategy: this.configurationValue.clientLwM2mSettings.clientStrategy, - fwUpdateStrategy: this.configurationValue.clientLwM2mSettings.fwUpdateStrategy, - swUpdateStrategy: this.configurationValue.clientLwM2mSettings.swUpdateStrategy, + fwUpdateStrategy: this.configurationValue.clientLwM2mSettings.fwUpdateStrategy || 1, + swUpdateStrategy: this.configurationValue.clientLwM2mSettings.swUpdateStrategy || 1, fwUpdateRecourse: fwResource, swUpdateRecourse: swResource }, {emitEvent: false}); this.configurationValue.clientLwM2mSettings.fwUpdateRecourse = fwResource; this.configurationValue.clientLwM2mSettings.swUpdateRecourse = swResource; - this.isFwUpdateStrategy = this.configurationValue.clientLwM2mSettings.fwUpdateStrategy === '2'; - this.isSwUpdateStrategy = this.configurationValue.clientLwM2mSettings.swUpdateStrategy === '2'; + this.isFwUpdateStrategy = this.configurationValue.clientLwM2mSettings.fwUpdateStrategy === 2; + this.isSwUpdateStrategy = this.configurationValue.clientLwM2mSettings.swUpdateStrategy === 2; this.otaUpdateSwStrategyValidate(); this.otaUpdateFwStrategyValidate(); } @@ -239,8 +263,8 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro this.configurationValue.clientLwM2mSettings.fwUpdateRecourse = config.fwUpdateRecourse; this.configurationValue.clientLwM2mSettings.swUpdateRecourse = config.swUpdateRecourse; this.upDateJsonAllConfig(); - this.updateModel(); } + this.updateModel(); } private getObserveAttrTelemetryObjects = (objectList: ObjectLwM2M[]): object => { @@ -513,53 +537,22 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro }); } - changeFwUpdateStrategy($event: MatSelectChange) { - if ($event.value === '2') { - this.isFwUpdateStrategy = true; - if (isEmpty(this.lwm2mDeviceProfileFormGroup.get('fwUpdateRecourse').value)) { - this.lwm2mDeviceProfileFormGroup.patchValue({ - fwUpdateRecourse: DEFAULT_FW_UPDATE_RESOURCE - }, - {emitEvent: false}); - } - } else { - this.isFwUpdateStrategy = false; - } - this.otaUpdateFwStrategyValidate(); - } - - changeSwUpdateStrategy($event: MatSelectChange) { - if ($event.value === '2') { - this.isSwUpdateStrategy = true; - if (isEmpty(this.lwm2mDeviceProfileFormGroup.get('swUpdateRecourse').value)) { - this.lwm2mDeviceProfileFormGroup.patchValue({ - swUpdateRecourse: DEFAULT_SW_UPDATE_RESOURCE - }, - {emitEvent: false}); - - } - } else { - this.isSwUpdateStrategy = false; - } - this.otaUpdateSwStrategyValidate(); - } - - private otaUpdateFwStrategyValidate(): void { + private otaUpdateFwStrategyValidate(updated = false): void { if (this.isFwUpdateStrategy) { this.lwm2mDeviceProfileFormGroup.get('fwUpdateRecourse').setValidators([Validators.required]); } else { this.lwm2mDeviceProfileFormGroup.get('fwUpdateRecourse').clearValidators(); } - this.lwm2mDeviceProfileFormGroup.get('fwUpdateRecourse').updateValueAndValidity(); + this.lwm2mDeviceProfileFormGroup.get('fwUpdateRecourse').updateValueAndValidity({emitEvent: updated}); } - private otaUpdateSwStrategyValidate(): void { + private otaUpdateSwStrategyValidate(updated = false): void { if (this.isSwUpdateStrategy) { this.lwm2mDeviceProfileFormGroup.get('swUpdateRecourse').setValidators([Validators.required]); } else { this.lwm2mDeviceProfileFormGroup.get('swUpdateRecourse').clearValidators(); } - this.lwm2mDeviceProfileFormGroup.get('swUpdateRecourse').updateValueAndValidity(); + this.lwm2mDeviceProfileFormGroup.get('swUpdateRecourse').updateValueAndValidity({emitEvent: updated}); } } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts index ff8225a27d..1f2d125d9f 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts @@ -20,7 +20,6 @@ export const INSTANCE = 'instance'; export const RESOURCES = 'resources'; export const ATTRIBUTE_LWM2M = 'attributeLwm2m'; export const CLIENT_LWM2M = 'clientLwM2M'; -export const CLIENT_LWM2M_SETTINGS = 'clientLwM2mSettings'; export const OBSERVE_ATTR_TELEMETRY = 'observeAttrTelemetry'; export const OBSERVE = 'observe'; export const ATTRIBUTE = 'attribute'; @@ -43,9 +42,9 @@ export const KEY_REGEXP_HEX_DEC = /^[-+]?[0-9A-Fa-f]+\.?[0-9A-Fa-f]*?$/; export const KEY_REGEXP_NUMBER = /^(\-?|\+?)\d*$/; export const INSTANCES_ID_VALUE_MIN = 0; export const INSTANCES_ID_VALUE_MAX = 65535; -export const DEFAULT_OTA_UPDATE_PROTOCOL = "coap://"; -export const DEFAULT_FW_UPDATE_RESOURCE = DEFAULT_OTA_UPDATE_PROTOCOL + DEFAULT_LOCAL_HOST_NAME + ":"+ DEFAULT_PORT_SERVER_NO_SEC; -export const DEFAULT_SW_UPDATE_RESOURCE = DEFAULT_OTA_UPDATE_PROTOCOL + DEFAULT_LOCAL_HOST_NAME + ":"+ DEFAULT_PORT_SERVER_NO_SEC; +export const DEFAULT_OTA_UPDATE_PROTOCOL = 'coap://'; +export const DEFAULT_FW_UPDATE_RESOURCE = DEFAULT_OTA_UPDATE_PROTOCOL + DEFAULT_LOCAL_HOST_NAME + ':' + DEFAULT_PORT_SERVER_NO_SEC; +export const DEFAULT_SW_UPDATE_RESOURCE = DEFAULT_OTA_UPDATE_PROTOCOL + DEFAULT_LOCAL_HOST_NAME + ':' + DEFAULT_PORT_SERVER_NO_SEC; export enum BINDING_MODE { @@ -113,19 +112,19 @@ export const ATTRIBUTE_LWM2M_MAP = new Map( export const ATTRIBUTE_KEYS = Object.keys(ATTRIBUTE_LWM2M_ENUM) as string[]; -export enum SECURITY_CONFIG_MODE { +export enum securityConfigMode { PSK = 'PSK', RPK = 'RPK', X509 = 'X509', NO_SEC = 'NO_SEC' } -export const SECURITY_CONFIG_MODE_NAMES = new Map( +export const securityConfigModeNames = new Map( [ - [SECURITY_CONFIG_MODE.PSK, 'Pre-Shared Key'], - [SECURITY_CONFIG_MODE.RPK, 'Raw Public Key'], - [SECURITY_CONFIG_MODE.X509, 'X.509 Certificate'], - [SECURITY_CONFIG_MODE.NO_SEC, 'No Security'] + [securityConfigMode.PSK, 'Pre-Shared Key'], + [securityConfigMode.RPK, 'Raw Public Key'], + [securityConfigMode.X509, 'X.509 Certificate'], + [securityConfigMode.NO_SEC, 'No Security'] ] ); @@ -144,9 +143,10 @@ export interface BootstrapServersSecurityConfig { export interface ServerSecurityConfig { host?: string; + securityHost?: string; port?: number; - bootstrapServerIs?: boolean; - securityMode: string; + securityPort?: number; + securityMode: securityConfigMode; clientPublicKeyOrId?: string; clientSecretKey?: string; serverPublicKey?: string; @@ -169,8 +169,8 @@ export interface Lwm2mProfileConfigModels { export interface ClientLwM2mSettings { clientStrategy: string; - fwUpdateStrategy: string; - swUpdateStrategy: string; + fwUpdateStrategy: number; + swUpdateStrategy: number; fwUpdateRecourse: string; swUpdateRecourse: string; } @@ -193,12 +193,11 @@ export function getDefaultBootstrapServersSecurityConfig(): BootstrapServersSecu }; } -export function getDefaultBootstrapServerSecurityConfig(hostname: any): ServerSecurityConfig { +export function getDefaultBootstrapServerSecurityConfig(hostname: string): ServerSecurityConfig { return { host: hostname, port: DEFAULT_PORT_BOOTSTRAP_NO_SEC, - bootstrapServerIs: true, - securityMode: SECURITY_CONFIG_MODE.NO_SEC.toString(), + securityMode: securityConfigMode.NO_SEC, serverPublicKey: '', clientHoldOffTime: DEFAULT_CLIENT_HOLD_OFF_TIME, serverId: DEFAULT_ID_BOOTSTRAP, @@ -208,7 +207,6 @@ export function getDefaultBootstrapServerSecurityConfig(hostname: any): ServerSe export function getDefaultLwM2MServerSecurityConfig(hostname): ServerSecurityConfig { const DefaultLwM2MServerSecurityConfig = getDefaultBootstrapServerSecurityConfig(hostname); - DefaultLwM2MServerSecurityConfig.bootstrapServerIs = false; DefaultLwM2MServerSecurityConfig.port = DEFAULT_PORT_SERVER_NO_SEC; DefaultLwM2MServerSecurityConfig.serverId = DEFAULT_ID_SERVER; return DefaultLwM2MServerSecurityConfig; @@ -242,9 +240,9 @@ export function getDefaultProfileConfig(hostname?: any): Lwm2mProfileConfigModel function getDefaultProfileClientLwM2mSettingsConfig(): ClientLwM2mSettings { return { - clientStrategy: "1", - fwUpdateStrategy: "1", - swUpdateStrategy: "1", + clientStrategy: '1', + fwUpdateStrategy: 1, + swUpdateStrategy: 1, fwUpdateRecourse: DEFAULT_FW_UPDATE_RESOURCE, swUpdateRecourse: DEFAULT_SW_UPDATE_RESOURCE }; diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 90a93c09e6..fdd0253d92 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -1243,7 +1243,7 @@ "pattern_hex_dec": "{ count, plural, 0 {must be hex decimal format} other {must be # characters} }", "servers": "Servers", "short-id": "Short ID", - "short-id-tip": "Short Server ID", + "short-id-required": "Short ID is required.", "lifetime": "Lifetime of the registration for this LwM2M client", "default-min-period": "Minimum Period between two notifications (sec)", "notif-if-disabled": "Notification Storing When Disabled or Offline", @@ -1252,28 +1252,37 @@ "bootstrap-server": "Bootstrap Server", "lwm2m-server": "LwM2M Server", "server-host": "Host", - "server-host-tip": "Server Host", + "server-host-required": "Host is required.", "server-port": "Port", - "server-port-tip": "Server Port", + "server-port-required": "Port is required.", "server-public-key": "Server Public Key", - "server-public-key-tip": "Server Public Key only for X509, RPK", + "server-public-key-required": "Server Public Key is required.", + "server-public-key-pattern": "Server Public Key must be hex decimal format.", + "server-public-key-length": "Server Public Key must be {{ count }} characters.", "client-hold-off-time": "Hold Off Time", - "client-hold-off-time-tip": "Client Hold Off Time for use with a Bootstrap-Server only", - "bootstrap-server-account-timeout": "Account after the timeout", - "bootstrap-server-account-timeout-tip": "Bootstrap-Server Account after the timeout value given by this resource.", - "others-tab": "Other settings...", + "client-hold-off-time-required": "Hold Off Time is required.", + "client-hold-off-time-tooltip": "Client Hold Off Time for use with a Bootstrap-Server only", + "account-after-timeout": "Account after the timeout", + "account-after-timeout-required": "Account after the timeout is required.", + "account-after-timeout-tooltip": "Bootstrap-Server Account after the timeout value given by this resource.", + "others-tab": "Other settings", "client-strategy": "Client strategy when connecting", "client-strategy-label": "Strategy", "client-strategy-connect": "{ count, plural, 1 {1: Only Observe Request to the client after the initial connection} other {2: Read All Resources & Observe Request to the client after registration} }", "client-strategy-tip": "{ count, plural, 1 {Strategy 1: After the initial connection of the LWM2M Client, the server sends Observe resources Request to the client, those resources that are marked as observation in the Device profile and which exist on the LWM2M client.} other {Strategy 2: After the registration, request the client to read all the resource values for all objects that the LWM2M client has,\n then execute: the server sends Observe resources Request to the client, those resources that are marked as observation in the Device profile and which exist on the LWM2M client.} }", - "ota-update-strategy": "Ota update strategy", - "fw-update-strategy-label": "Firmware update strategy", - "fw-update-strategy": "{ count, plural, 1 {Push firmware update as binary file using Object 5 and Resource 0 (Package).} 2 {Auto-generate unique CoAP URL to download the package and push firmware update as Object 5 and Resource 1 (Package URI).} other {Push firmware update as binary file using Object 19 and Resource 0 (Data).} }", - "sw-update-strategy-label": "Software update strategy", - "sw-update-strategy": "{ count, plural, 1 {Push binary file using Object 9 and Resource 2 (Package).} other {Auto-generate unique CoAP URL to download the package and push software update using Object 9 and Resource 3 (Package URI).} }", - "ota-update-recourse": "Ota update Coap recourse", - "fw-update-recourse": "Firmware update Coap recourse", - "sw-update-recourse": "Software update Coap recourse", + "fw-update": "Firmware update", + "fw-update-strategy": "Firmware update strategy", + "fw-update-strategy-data": "Push firmware update as binary file using Object 19 and Resource 0 (Data)", + "fw-update-strategy-package": "Push firmware update as binary file using Object 5 and Resource 0 (Package)", + "fw-update-strategy-package-uri": "Auto-generate unique CoAP URL to download the package and push firmware update as Object 5 and Resource 1 (Package URI)", + "sw-update": "Software update", + "sw-update-strategy": "Software update strategy", + "sw-update-strategy-package": "Push binary file using Object 9 and Resource 2 (Package)", + "sw-update-strategy-package-uri": "Auto-generate unique CoAP URL to download the package and push software update using Object 9 and Resource 3 (Package URI)", + "fw-update-recourse": "Firmware update CoAP recourse", + "fw-update-recourse-required": "Firmware update CoAP recourse is required.", + "sw-update-recourse": "Software update CoAP recourse", + "sw-update-recourse-required": "Software update CoAP recourse is required.", "config-json-tab": "Json Config Profile Device" } },