feat_account: mid commit
Signed-off-by: fufesou <shuanglongchen@yeah.net>
This commit is contained in:
parent
3454454bd5
commit
87e53501e3
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_hbb/common/widgets/peer_card.dart';
|
import 'package:flutter_hbb/common/widgets/peer_card.dart';
|
||||||
import 'package:flutter_hbb/common/widgets/peers_view.dart';
|
import 'package:flutter_hbb/common/widgets/peers_view.dart';
|
||||||
import 'package:flutter_hbb/desktop/widgets/popup_menu.dart';
|
import 'package:flutter_hbb/desktop/widgets/popup_menu.dart';
|
||||||
|
import 'package:flutter_hbb/desktop/widgets/login.dart';
|
||||||
import '../../consts.dart';
|
import '../../consts.dart';
|
||||||
import '../../desktop/widgets/material_mod_popup_menu.dart' as mod_menu;
|
import '../../desktop/widgets/material_mod_popup_menu.dart' as mod_menu;
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
@ -472,7 +472,7 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
|||||||
|
|
||||||
/// common login dialog for desktop
|
/// common login dialog for desktop
|
||||||
/// call this directly
|
/// call this directly
|
||||||
Future<bool> loginDialog() async {
|
Future<bool> loginDialog2() async {
|
||||||
String userName = "";
|
String userName = "";
|
||||||
var userNameMsg = "";
|
var userNameMsg = "";
|
||||||
String pass = "";
|
String pass = "";
|
||||||
|
@ -7,6 +7,7 @@ import 'package:flutter/services.dart';
|
|||||||
import 'package:flutter_hbb/common.dart';
|
import 'package:flutter_hbb/common.dart';
|
||||||
import 'package:flutter_hbb/desktop/pages/desktop_home_page.dart';
|
import 'package:flutter_hbb/desktop/pages/desktop_home_page.dart';
|
||||||
import 'package:flutter_hbb/desktop/pages/desktop_tab_page.dart';
|
import 'package:flutter_hbb/desktop/pages/desktop_tab_page.dart';
|
||||||
|
import 'package:flutter_hbb/desktop/widgets/login.dart';
|
||||||
import 'package:flutter_hbb/models/platform_model.dart';
|
import 'package:flutter_hbb/models/platform_model.dart';
|
||||||
import 'package:flutter_hbb/models/server_model.dart';
|
import 'package:flutter_hbb/models/server_model.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
469
flutter/lib/desktop/widgets/login.dart
Normal file
469
flutter/lib/desktop/widgets/login.dart
Normal file
@ -0,0 +1,469 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hbb/consts.dart';
|
||||||
|
import 'package:flutter_hbb/models/platform_model.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
import 'package:url_launcher/url_launcher_string.dart';
|
||||||
|
|
||||||
|
import '../../common.dart';
|
||||||
|
import '../widgets/button.dart';
|
||||||
|
|
||||||
|
class _IconOP extends StatelessWidget {
|
||||||
|
final String icon;
|
||||||
|
final double iconWidth;
|
||||||
|
const _IconOP({Key? key, required this.icon, required this.iconWidth})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
margin: const EdgeInsets.symmetric(horizontal: 4.0),
|
||||||
|
child: SvgPicture.asset(
|
||||||
|
'assets/$icon.svg',
|
||||||
|
width: iconWidth,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ButtonOP extends StatelessWidget {
|
||||||
|
final String op;
|
||||||
|
final RxString curOP;
|
||||||
|
final double iconWidth;
|
||||||
|
final Color primaryColor;
|
||||||
|
final double height;
|
||||||
|
final Function() onTap;
|
||||||
|
|
||||||
|
const ButtonOP({
|
||||||
|
Key? key,
|
||||||
|
required this.op,
|
||||||
|
required this.curOP,
|
||||||
|
required this.iconWidth,
|
||||||
|
required this.primaryColor,
|
||||||
|
required this.height,
|
||||||
|
required this.onTap,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Row(children: [
|
||||||
|
Expanded(
|
||||||
|
child: Container(
|
||||||
|
height: height,
|
||||||
|
padding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
|
||||||
|
child: Obx(() => ElevatedButton(
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
primary: curOP.value.isEmpty || curOP.value == op
|
||||||
|
? primaryColor
|
||||||
|
: Colors.grey,
|
||||||
|
).copyWith(elevation: ButtonStyleButton.allOrNull(0.0)),
|
||||||
|
onPressed:
|
||||||
|
curOP.value.isEmpty || curOP.value == op ? onTap : null,
|
||||||
|
child: Stack(children: [
|
||||||
|
// to-do: translate
|
||||||
|
Center(child: Text('Continue with $op')),
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: SizedBox(
|
||||||
|
width: 120,
|
||||||
|
child: _IconOP(
|
||||||
|
icon: op,
|
||||||
|
iconWidth: iconWidth,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConfigOP {
|
||||||
|
final String op;
|
||||||
|
final double iconWidth;
|
||||||
|
ConfigOP({required this.op, required this.iconWidth});
|
||||||
|
}
|
||||||
|
|
||||||
|
class WidgetOP extends StatefulWidget {
|
||||||
|
final ConfigOP config;
|
||||||
|
final RxString curOP;
|
||||||
|
const WidgetOP({
|
||||||
|
Key? key,
|
||||||
|
required this.config,
|
||||||
|
required this.curOP,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<StatefulWidget> createState() {
|
||||||
|
return _WidgetOPState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _WidgetOPState extends State<WidgetOP> {
|
||||||
|
Timer? _updateTimer;
|
||||||
|
String _stateMsg = '';
|
||||||
|
String _stateFailedMsg = '';
|
||||||
|
String _url = '';
|
||||||
|
String _username = '';
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
_updateTimer?.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
_beginQueryState() {
|
||||||
|
_updateTimer = Timer.periodic(Duration(seconds: 1), (timer) {
|
||||||
|
_updateState();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateState() {
|
||||||
|
bind.mainAccountAuthResult().then((result) {
|
||||||
|
if (result.isEmpty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final resultMap = jsonDecode(result);
|
||||||
|
if (resultMap == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final String stateMsg = resultMap['state_msg'];
|
||||||
|
final String failedMsg = resultMap['failed_msg'];
|
||||||
|
// to-do: test null url
|
||||||
|
final String url = resultMap['url'];
|
||||||
|
if (_stateMsg != stateMsg) {
|
||||||
|
if (_url.isEmpty && url.isNotEmpty) {
|
||||||
|
launchUrl(Uri.parse(url));
|
||||||
|
_url = url;
|
||||||
|
}
|
||||||
|
setState(() {
|
||||||
|
_stateMsg = stateMsg;
|
||||||
|
_stateFailedMsg = failedMsg;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_resetState() {
|
||||||
|
_stateMsg = '';
|
||||||
|
_stateFailedMsg = '';
|
||||||
|
_url = '';
|
||||||
|
_username = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(minWidth: 500),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
ButtonOP(
|
||||||
|
op: widget.config.op,
|
||||||
|
curOP: widget.curOP,
|
||||||
|
iconWidth: widget.config.iconWidth,
|
||||||
|
primaryColor: str2color(widget.config.op, 0x7f),
|
||||||
|
height: 40,
|
||||||
|
onTap: () {
|
||||||
|
widget.curOP.value = widget.config.op;
|
||||||
|
bind.mainAccountAuth(op: widget.config.op);
|
||||||
|
_beginQueryState();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Obx(() => Offstage(
|
||||||
|
offstage: widget.curOP.value != widget.config.op,
|
||||||
|
child: Text(
|
||||||
|
_stateMsg,
|
||||||
|
style: TextStyle(fontSize: 12),
|
||||||
|
))),
|
||||||
|
Obx(
|
||||||
|
() => Offstage(
|
||||||
|
offstage: widget.curOP.value != widget.config.op,
|
||||||
|
child: const SizedBox(
|
||||||
|
height: 5.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Obx(
|
||||||
|
() => Offstage(
|
||||||
|
offstage: widget.curOP.value != widget.config.op,
|
||||||
|
child: ConstrainedBox(
|
||||||
|
constraints: BoxConstraints(maxHeight: 20),
|
||||||
|
child: ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
widget.curOP.value = '';
|
||||||
|
_updateTimer?.cancel();
|
||||||
|
_resetState();
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
translate('Cancel'),
|
||||||
|
style: TextStyle(fontSize: 15),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LoginWidgetOP extends StatelessWidget {
|
||||||
|
final List<ConfigOP> ops;
|
||||||
|
final RxString curOP = ''.obs;
|
||||||
|
|
||||||
|
LoginWidgetOP({
|
||||||
|
Key? key,
|
||||||
|
required this.ops,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var children = ops
|
||||||
|
.map((op) => [
|
||||||
|
WidgetOP(
|
||||||
|
config: op,
|
||||||
|
curOP: curOP,
|
||||||
|
),
|
||||||
|
const Divider()
|
||||||
|
])
|
||||||
|
.expand((i) => i)
|
||||||
|
.toList();
|
||||||
|
if (children.isNotEmpty) {
|
||||||
|
children.removeLast();
|
||||||
|
}
|
||||||
|
return SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
|
children: children,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LoginWidgetUserPass extends StatelessWidget {
|
||||||
|
final String username;
|
||||||
|
final String pass;
|
||||||
|
final String usernameMsg;
|
||||||
|
final String passMsg;
|
||||||
|
final bool isInProgress;
|
||||||
|
final Function(String, String) onLogin;
|
||||||
|
const LoginWidgetUserPass({
|
||||||
|
Key? key,
|
||||||
|
required this.username,
|
||||||
|
required this.pass,
|
||||||
|
required this.usernameMsg,
|
||||||
|
required this.passMsg,
|
||||||
|
required this.isInProgress,
|
||||||
|
required this.onLogin,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var userController = TextEditingController(text: username);
|
||||||
|
var pwdController = TextEditingController(text: pass);
|
||||||
|
return ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(minWidth: 500),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const SizedBox(
|
||||||
|
height: 8.0,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(minWidth: 100),
|
||||||
|
child: Text(
|
||||||
|
'${translate("Username")}:',
|
||||||
|
textAlign: TextAlign.start,
|
||||||
|
).marginOnly(bottom: 16.0)),
|
||||||
|
const SizedBox(
|
||||||
|
width: 24.0,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: TextField(
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: const OutlineInputBorder(),
|
||||||
|
errorText: usernameMsg.isNotEmpty ? usernameMsg : null),
|
||||||
|
controller: userController,
|
||||||
|
focusNode: FocusNode()..requestFocus(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 8.0,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(minWidth: 100),
|
||||||
|
child: Text('${translate("Password")}:')
|
||||||
|
.marginOnly(bottom: 16.0)),
|
||||||
|
const SizedBox(
|
||||||
|
width: 24.0,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: TextField(
|
||||||
|
obscureText: true,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: const OutlineInputBorder(),
|
||||||
|
errorText: passMsg.isNotEmpty ? passMsg : null),
|
||||||
|
controller: pwdController,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 4.0,
|
||||||
|
),
|
||||||
|
Offstage(
|
||||||
|
offstage: !isInProgress, child: const LinearProgressIndicator()),
|
||||||
|
const SizedBox(
|
||||||
|
height: 12.0,
|
||||||
|
),
|
||||||
|
Row(children: [
|
||||||
|
Expanded(
|
||||||
|
child: Container(
|
||||||
|
height: 50,
|
||||||
|
padding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
|
||||||
|
child: ElevatedButton(
|
||||||
|
child: const Text(
|
||||||
|
'Login',
|
||||||
|
style: TextStyle(fontSize: 18),
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
onLogin(userController.text, pwdController.text);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// common login dialog for desktop
|
||||||
|
/// call this directly
|
||||||
|
Future<bool> loginDialog() async {
|
||||||
|
String username = '';
|
||||||
|
var usernameMsg = '';
|
||||||
|
String pass = '';
|
||||||
|
var passMsg = '';
|
||||||
|
var isInProgress = false;
|
||||||
|
var completer = Completer<bool>();
|
||||||
|
|
||||||
|
gFFI.dialogManager.show((setState, close) {
|
||||||
|
cancel() {
|
||||||
|
isInProgress = false;
|
||||||
|
completer.complete(false);
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
onLogin(String username0, String pass0) async {
|
||||||
|
setState(() {
|
||||||
|
usernameMsg = '';
|
||||||
|
passMsg = '';
|
||||||
|
isInProgress = true;
|
||||||
|
});
|
||||||
|
cancel() {
|
||||||
|
if (isInProgress) {
|
||||||
|
setState(() {
|
||||||
|
isInProgress = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
username = username0;
|
||||||
|
pass = pass0;
|
||||||
|
if (username.isEmpty) {
|
||||||
|
usernameMsg = translate('Username missed');
|
||||||
|
debugPrint('REMOVE ME ====================== username empty');
|
||||||
|
cancel();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (pass.isEmpty) {
|
||||||
|
passMsg = translate('Password missed');
|
||||||
|
debugPrint('REMOVE ME ====================== password empty');
|
||||||
|
cancel();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
final resp = await gFFI.userModel.login(username, pass);
|
||||||
|
if (resp.containsKey('error')) {
|
||||||
|
passMsg = resp['error'];
|
||||||
|
debugPrint('REMOVE ME ====================== password error');
|
||||||
|
cancel();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// {access_token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJndWlkIjoiMDFkZjQ2ZjgtZjg3OS00MDE0LTk5Y2QtMGMwYzM2MmViZGJlIiwiZXhwIjoxNjYxNDg2NzYwfQ.GZpe1oI8TfM5yTYNrpcwbI599P4Z_-b2GmnwNl2Lr-w,
|
||||||
|
// token_type: Bearer, user: {id: , name: admin, email: null, note: null, status: null, grp: null, is_admin: true}}
|
||||||
|
debugPrint('$resp');
|
||||||
|
completer.complete(true);
|
||||||
|
} catch (err) {
|
||||||
|
debugPrint(err.toString());
|
||||||
|
debugPrint(
|
||||||
|
'REMOVE ME ====================== login error ${err.toString()}');
|
||||||
|
cancel();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return CustomAlertDialog(
|
||||||
|
title: Text(translate('Login')),
|
||||||
|
content: ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(minWidth: 500),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const SizedBox(
|
||||||
|
height: 8.0,
|
||||||
|
),
|
||||||
|
LoginWidgetUserPass(
|
||||||
|
username: username,
|
||||||
|
pass: pass,
|
||||||
|
usernameMsg: usernameMsg,
|
||||||
|
passMsg: passMsg,
|
||||||
|
isInProgress: isInProgress,
|
||||||
|
onLogin: onLogin,
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 8.0,
|
||||||
|
),
|
||||||
|
const Center(
|
||||||
|
child: Text(
|
||||||
|
'or',
|
||||||
|
style: TextStyle(fontSize: 16),
|
||||||
|
)),
|
||||||
|
const SizedBox(
|
||||||
|
height: 8.0,
|
||||||
|
),
|
||||||
|
LoginWidgetOP(ops: [
|
||||||
|
ConfigOP(op: 'Github', iconWidth: 24),
|
||||||
|
ConfigOP(op: 'Google', iconWidth: 24),
|
||||||
|
ConfigOP(op: 'Okta', iconWidth: 46),
|
||||||
|
]),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(onPressed: cancel, child: Text(translate('Cancel'))),
|
||||||
|
],
|
||||||
|
onCancel: cancel,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
return completer.future;
|
||||||
|
}
|
@ -77,7 +77,9 @@ class UserModel {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
final m = jsonDecode(userInfo);
|
final m = jsonDecode(userInfo);
|
||||||
if (m != null) {
|
if (m == null) {
|
||||||
|
userName.value = '';
|
||||||
|
} else {
|
||||||
userName.value = m['name'] ?? '';
|
userName.value = m['name'] ?? '';
|
||||||
}
|
}
|
||||||
return userName.value;
|
return userName.value;
|
||||||
|
@ -13,6 +13,8 @@ use hbb_common::{
|
|||||||
fs, log,
|
fs, log,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// use crate::hbbs_http::account::AuthResult;
|
||||||
|
|
||||||
use crate::flutter::{self, SESSIONS};
|
use crate::flutter::{self, SESSIONS};
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
use crate::start_server;
|
use crate::start_server;
|
||||||
@ -1082,6 +1084,14 @@ pub fn install_install_path() -> SyncReturn<String> {
|
|||||||
SyncReturn(install_path())
|
SyncReturn(install_path())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn main_account_auth(op: String) {
|
||||||
|
account_auth(op);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main_account_auth_result() -> String {
|
||||||
|
account_auth_result()
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
pub mod server_side {
|
pub mod server_side {
|
||||||
use jni::{
|
use jni::{
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use super::HbbHttpResponse;
|
use super::HbbHttpResponse;
|
||||||
use hbb_common::{config::Config, log, sleep, tokio, tokio::sync::RwLock, ResultType};
|
use hbb_common::{config::Config, log, sleep, tokio, tokio::sync::RwLock, ResultType};
|
||||||
use serde_derive::Deserialize;
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
@ -16,6 +16,9 @@ lazy_static::lazy_static! {
|
|||||||
|
|
||||||
const QUERY_INTERVAL_SECS: f32 = 1.0;
|
const QUERY_INTERVAL_SECS: f32 = 1.0;
|
||||||
const QUERY_TIMEOUT_SECS: u64 = 60;
|
const QUERY_TIMEOUT_SECS: u64 = 60;
|
||||||
|
const REQUESTING_ACCOUNT_AUTH: &str = "Requesting account auth";
|
||||||
|
const WAITING_ACCOUNT_AUTH: &str = "Waiting account auth";
|
||||||
|
const LOGIN_ACCOUNT_AUTH: &str = "Login account auth";
|
||||||
|
|
||||||
#[derive(Deserialize, Clone)]
|
#[derive(Deserialize, Clone)]
|
||||||
pub struct OidcAuthUrl {
|
pub struct OidcAuthUrl {
|
||||||
@ -23,7 +26,7 @@ pub struct OidcAuthUrl {
|
|||||||
url: Url,
|
url: Url,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Default, Clone)]
|
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct UserPayload {
|
pub struct UserPayload {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
@ -34,34 +37,16 @@ pub struct UserPayload {
|
|||||||
pub is_admin: Option<bool>,
|
pub is_admin: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct AuthBody {
|
pub struct AuthBody {
|
||||||
access_token: String,
|
pub access_token: String,
|
||||||
token_type: String,
|
pub token_type: String,
|
||||||
user: UserPayload,
|
pub user: UserPayload,
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub enum OidcState {
|
|
||||||
// initial request
|
|
||||||
OidcRequest = 1,
|
|
||||||
// initial request failed
|
|
||||||
OidcRequestFailed = 2,
|
|
||||||
// request succeeded, loop querying
|
|
||||||
OidcQuerying = 11,
|
|
||||||
// loop querying failed
|
|
||||||
OidcQueryFailed = 12,
|
|
||||||
// query sucess before
|
|
||||||
OidcNotExists = 13,
|
|
||||||
// query timeout
|
|
||||||
OidcQueryTimeout = 14,
|
|
||||||
// already login
|
|
||||||
OidcLogin = 21,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct OidcSession {
|
pub struct OidcSession {
|
||||||
client: reqwest::Client,
|
client: reqwest::Client,
|
||||||
state: OidcState,
|
state_msg: &'static str,
|
||||||
failed_msg: String,
|
failed_msg: String,
|
||||||
code_url: Option<OidcAuthUrl>,
|
code_url: Option<OidcAuthUrl>,
|
||||||
auth_body: Option<AuthBody>,
|
auth_body: Option<AuthBody>,
|
||||||
@ -70,11 +55,19 @@ pub struct OidcSession {
|
|||||||
query_timeout: Duration,
|
query_timeout: Duration,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct AuthResult {
|
||||||
|
pub state_msg: String,
|
||||||
|
pub failed_msg: String,
|
||||||
|
pub url: Option<String>,
|
||||||
|
pub auth_body: Option<AuthBody>,
|
||||||
|
}
|
||||||
|
|
||||||
impl OidcSession {
|
impl OidcSession {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
client: reqwest::Client::new(),
|
client: reqwest::Client::new(),
|
||||||
state: OidcState::OidcRequest,
|
state_msg: REQUESTING_ACCOUNT_AUTH,
|
||||||
failed_msg: "".to_owned(),
|
failed_msg: "".to_owned(),
|
||||||
code_url: None,
|
code_url: None,
|
||||||
auth_body: None,
|
auth_body: None,
|
||||||
@ -112,7 +105,7 @@ impl OidcSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn reset(&mut self) {
|
fn reset(&mut self) {
|
||||||
self.state = OidcState::OidcRequest;
|
self.state_msg = REQUESTING_ACCOUNT_AUTH;
|
||||||
self.failed_msg = "".to_owned();
|
self.failed_msg = "".to_owned();
|
||||||
self.keep_querying = true;
|
self.keep_querying = true;
|
||||||
self.running = false;
|
self.running = false;
|
||||||
@ -136,21 +129,21 @@ impl OidcSession {
|
|||||||
OIDC_SESSION
|
OIDC_SESSION
|
||||||
.write()
|
.write()
|
||||||
.await
|
.await
|
||||||
.set_state(OidcState::OidcRequestFailed, err);
|
.set_state(REQUESTING_ACCOUNT_AUTH, err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
OIDC_SESSION.write().await.set_state(
|
OIDC_SESSION
|
||||||
OidcState::OidcRequestFailed,
|
.write()
|
||||||
"Invalid auth response".to_owned(),
|
.await
|
||||||
);
|
.set_state(REQUESTING_ACCOUNT_AUTH, "Invalid auth response".to_owned());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
OIDC_SESSION
|
OIDC_SESSION
|
||||||
.write()
|
.write()
|
||||||
.await
|
.await
|
||||||
.set_state(OidcState::OidcRequestFailed, err.to_string());
|
.set_state(REQUESTING_ACCOUNT_AUTH, err.to_string());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -158,7 +151,7 @@ impl OidcSession {
|
|||||||
OIDC_SESSION
|
OIDC_SESSION
|
||||||
.write()
|
.write()
|
||||||
.await
|
.await
|
||||||
.set_state(OidcState::OidcQuerying, "".to_owned());
|
.set_state(WAITING_ACCOUNT_AUTH, "".to_owned());
|
||||||
OIDC_SESSION.write().await.code_url = Some(code_url.clone());
|
OIDC_SESSION.write().await.code_url = Some(code_url.clone());
|
||||||
|
|
||||||
let begin = Instant::now();
|
let begin = Instant::now();
|
||||||
@ -169,7 +162,7 @@ impl OidcSession {
|
|||||||
OIDC_SESSION
|
OIDC_SESSION
|
||||||
.write()
|
.write()
|
||||||
.await
|
.await
|
||||||
.set_state(OidcState::OidcLogin, "".to_owned());
|
.set_state(LOGIN_ACCOUNT_AUTH, "".to_owned());
|
||||||
OIDC_SESSION.write().await.auth_body = Some(auth_body);
|
OIDC_SESSION.write().await.auth_body = Some(auth_body);
|
||||||
return;
|
return;
|
||||||
// to-do, set access-token
|
// to-do, set access-token
|
||||||
@ -181,7 +174,7 @@ impl OidcSession {
|
|||||||
OIDC_SESSION
|
OIDC_SESSION
|
||||||
.write()
|
.write()
|
||||||
.await
|
.await
|
||||||
.set_state(OidcState::OidcQueryFailed, err);
|
.set_state(WAITING_ACCOUNT_AUTH, err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -200,14 +193,14 @@ impl OidcSession {
|
|||||||
OIDC_SESSION
|
OIDC_SESSION
|
||||||
.write()
|
.write()
|
||||||
.await
|
.await
|
||||||
.set_state(OidcState::OidcQueryTimeout, "timeout".to_owned());
|
.set_state(WAITING_ACCOUNT_AUTH, "timeout".to_owned());
|
||||||
}
|
}
|
||||||
|
|
||||||
// no need to handle "keep_querying == false"
|
// no need to handle "keep_querying == false"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_state(&mut self, state: OidcState, failed_msg: String) {
|
fn set_state(&mut self, state_msg: &'static str, failed_msg: String) {
|
||||||
self.state = state;
|
self.state_msg = state_msg;
|
||||||
self.failed_msg = failed_msg;
|
self.failed_msg = failed_msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,15 +221,16 @@ impl OidcSession {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_result_(&self) -> (u8, String, Option<AuthBody>) {
|
fn get_result_(&self) -> AuthResult {
|
||||||
(
|
AuthResult {
|
||||||
self.state as u8,
|
state_msg: self.state_msg.to_string(),
|
||||||
self.failed_msg.clone(),
|
failed_msg: self.failed_msg.clone(),
|
||||||
self.auth_body.clone(),
|
url: self.code_url.as_ref().map(|x| x.url.to_string()),
|
||||||
)
|
auth_body: self.auth_body.clone(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_result() -> (u8, String, Option<AuthBody>) {
|
pub async fn get_result() -> AuthResult {
|
||||||
OIDC_SESSION.read().await.get_result_()
|
OIDC_SESSION.read().await.get_result_()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,8 +20,7 @@ use hbb_common::{
|
|||||||
tokio::{self, sync::mpsc, time},
|
tokio::{self, sync::mpsc, time},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::ipc;
|
use crate::{common::SOFTWARE_UPDATE_URL, hbbs_http::account, ipc, platform};
|
||||||
use crate::{common::SOFTWARE_UPDATE_URL, platform};
|
|
||||||
|
|
||||||
type Message = RendezvousMessage;
|
type Message = RendezvousMessage;
|
||||||
|
|
||||||
@ -843,6 +842,16 @@ pub(crate) fn check_connect_status(reconnect: bool) -> mpsc::UnboundedSender<ipc
|
|||||||
tx
|
tx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::main(flavor = "current_thread")]
|
||||||
|
pub async fn account_auth(op: String) {
|
||||||
|
account::OidcSession::account_auth(op, get_id(), get_uuid()).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main(flavor = "current_thread")]
|
||||||
|
pub async fn account_auth_result() -> String {
|
||||||
|
serde_json::to_string(&account::OidcSession::get_result().await).unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
// notice: avoiding create ipc connecton repeatly,
|
// notice: avoiding create ipc connecton repeatly,
|
||||||
// because windows named pipe has serious memory leak issue.
|
// because windows named pipe has serious memory leak issue.
|
||||||
#[tokio::main(flavor = "current_thread")]
|
#[tokio::main(flavor = "current_thread")]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user