feat(bitbucket): app password login

This commit is contained in:
Rongjian Zhang 2020-02-02 14:08:58 +08:00
parent 79fe174b10
commit 53ecb3242f
6 changed files with 122 additions and 19 deletions

View File

@ -10,19 +10,20 @@ class Account {
String token;
String login;
String avatarUrl;
int gitlabId;
int gitlabId; // For GitLab
String appPassword; // For Bitbucket
equals(Account a) {
final uri = Uri.parse(domain);
final uriA = Uri.parse(a.domain);
// equals(Account a) {
// final uri = Uri.parse(domain);
// final uriA = Uri.parse(a.domain);
// Treat domains as the same if they have the same hosts and ports
return a.platform == platform &&
a.login == login &&
a.gitlabId == gitlabId &&
uri.host == uriA.host &&
uri.port == uriA.port;
}
// // Treat domains as the same if they have the same hosts and ports
// return a.platform == platform &&
// a.login == login &&
// a.gitlabId == gitlabId &&
// uri.host == uriA.host &&
// uri.port == uriA.port;
// }
Account({
@required this.platform,
@ -31,6 +32,7 @@ class Account {
@required this.login,
@required this.avatarUrl,
this.gitlabId,
this.appPassword,
});
factory Account.fromJson(Map<String, dynamic> json) =>

View File

@ -14,6 +14,7 @@ Account _$AccountFromJson(Map<String, dynamic> json) {
login: json['login'] as String,
avatarUrl: json['avatarUrl'] as String,
gitlabId: json['gitlabId'] as int,
appPassword: json['appPassword'] as String,
);
}
@ -32,5 +33,6 @@ Map<String, dynamic> _$AccountToJson(Account instance) {
writeNotNull('login', instance.login);
writeNotNull('avatarUrl', instance.avatarUrl);
writeNotNull('gitlabId', instance.gitlabId);
writeNotNull('appPassword', instance.appPassword);
return val;
}

View File

@ -1,6 +1,7 @@
import 'dart:io';
import 'dart:convert';
import 'dart:async';
import 'package:git_touch/models/bitbucket.dart';
import 'package:git_touch/models/gitea.dart';
import 'package:git_touch/utils/request_serilizer.dart';
import 'package:gql_http_link/gql_http_link.dart';
@ -22,6 +23,7 @@ const clientId = 'df930d7d2e219f26142a';
class PlatformType {
static const github = 'github';
static const gitlab = 'gitlab';
static const bitbucket = 'bitbucket';
static const gitea = 'gitea';
}
@ -214,6 +216,32 @@ class AuthModel with ChangeNotifier {
);
}
Future loginToBb(String domain, String username, String appPassword) async {
try {
loading = true;
notifyListeners();
final uri = Uri.parse('$domain/api/2.0/user')
.replace(userInfo: '$username:$appPassword');
final res = await http.get(uri);
if (res.statusCode >= 400) {
throw 'status ${res.statusCode}';
}
final info = json.decode(res.body);
final user = BbUser.fromJson(info);
await _addAccount(Account(
platform: PlatformType.bitbucket,
domain: domain,
token: user.username,
login: username,
avatarUrl: null,
appPassword: appPassword,
));
} finally {
loading = false;
notifyListeners();
}
}
Future<void> init() async {
// Listen scheme
_sub = getUriLinksStream().listen(_onSchemeDetected, onError: (err) {

12
lib/models/bitbucket.dart Normal file
View File

@ -0,0 +1,12 @@
import 'package:json_annotation/json_annotation.dart';
part 'bitbucket.g.dart';
@JsonSerializable(fieldRename: FieldRename.snake)
class BbUser {
String username;
String displayName;
bool isStaff;
DateTime createdOn;
BbUser();
factory BbUser.fromJson(Map<String, dynamic> json) => _$BbUserFromJson(json);
}

View File

@ -0,0 +1,24 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'bitbucket.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
BbUser _$BbUserFromJson(Map<String, dynamic> json) {
return BbUser()
..username = json['username'] as String
..displayName = json['display_name'] as String
..isStaff = json['is_staff'] as bool
..createdOn = json['created_on'] == null
? null
: DateTime.parse(json['created_on'] as String);
}
Map<String, dynamic> _$BbUserToJson(BbUser instance) => <String, dynamic>{
'username': instance.username,
'display_name': instance.displayName,
'is_staff': instance.isStaff,
'created_on': instance.createdOn?.toIso8601String(),
};

View File

@ -22,6 +22,10 @@ class _LoginScreenState extends State<LoginScreen> {
final _tokenController = TextEditingController();
final _domainController = TextEditingController();
// For Bitbucket
final _usernameController = TextEditingController();
final _passwordController = TextEditingController();
// @override
// initState() {
// super.initState();
@ -114,15 +118,9 @@ class _LoginScreenState extends State<LoginScreen> {
return Column(
children: <Widget>[
if (showDomain)
MyTextField(
controller: _domainController,
placeholder: 'Domain',
),
MyTextField(controller: _domainController, placeholder: 'Domain'),
SizedBox(height: 8),
MyTextField(
placeholder: 'Access token',
controller: _tokenController,
),
MyTextField(placeholder: 'Access token', controller: _tokenController),
SizedBox(height: 8),
if (notes != null) ...notes,
],
@ -228,6 +226,43 @@ class _LoginScreenState extends State<LoginScreen> {
}
},
),
_buildAddItem(
text: 'Bitbucket Account',
brand: FontAwesome5Brands.bitbucket,
onTap: () async {
_domainController.text = 'https://bitbucket.org';
final result = await theme.showConfirm(
context,
Column(
children: <Widget>[
MyTextField(
controller: _domainController,
placeholder: 'Domain'),
SizedBox(height: 8),
MyTextField(
placeholder: 'Username',
controller: _usernameController),
SizedBox(height: 8),
MyTextField(
placeholder: 'App password',
controller: _passwordController),
SizedBox(height: 8),
// TODO: permissions
],
),
);
if (result == true) {
try {
await auth.loginToBb(
_domainController.text,
_usernameController.text,
_passwordController.text);
} catch (err) {
showError(err);
}
}
},
),
_buildAddItem(
text: 'Gitea Account',
brand: Octicons.git_branch, // TODO: brand icon