Browse Source

nextcloud,neon: Fix login and user name mess

pull/149/head
jld3103 2 years ago
parent
commit
0b0181fd06
No known key found for this signature in database
GPG Key ID: 9062417B9E8EB7B3
  1. 12
      packages/neon/integration_test/screenshot_test.dart
  2. 1
      packages/neon/lib/main.dart
  3. 7
      packages/neon/lib/src/blocs/accounts.dart
  4. 2
      packages/neon/lib/src/blocs/push_notifications.dart
  5. 54
      packages/neon/lib/src/models/account.dart
  6. 6
      packages/neon/lib/src/models/account.g.dart
  7. 25
      packages/neon/lib/src/pages/login.dart
  8. 15
      packages/nextcloud/lib/src/client.dart
  9. 2
      packages/nextcloud/test/helper.dart

12
packages/neon/integration_test/screenshot_test.dart

@ -119,7 +119,6 @@ Future pumpAppPage(
AppStorage('accounts', sharedPreferences), AppStorage('accounts', sharedPreferences),
sharedPreferences, sharedPreferences,
globalOptions, globalOptions,
packageInfo,
allAppImplementations, allAppImplementations,
); );
if (account != null) { if (account != null) {
@ -227,9 +226,10 @@ Future main() async {
packageInfo = await PackageInfo.fromPlatform(); packageInfo = await PackageInfo.fromPlatform();
account = Account( account = Account(
serverURL: 'http://10.0.2.2', serverURL: 'http://10.0.2.2',
loginName: 'user1',
username: 'user1', username: 'user1',
password: 'user1', password: 'user1',
)..setupClient(packageInfo); );
await SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []); await SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []);
}); });
@ -462,14 +462,12 @@ Future main() async {
}); });
testWidgets('notifications', (final tester) async { testWidgets('notifications', (final tester) async {
await (Account( await Account(
serverURL: 'http://10.0.2.2', serverURL: 'http://10.0.2.2',
loginName: 'admin',
username: 'admin', username: 'admin',
password: 'admin', password: 'admin',
)..setupClient(packageInfo)) ).client.notifications.sendAdminNotification(
.client
.notifications
.sendAdminNotification(
userId: account.username, userId: account.username,
shortMessage: 'Notifications demo', shortMessage: 'Notifications demo',
longMessage: 'This is a notifications demo of the Neon app', longMessage: 'This is a notifications demo of the Neon app',

1
packages/neon/lib/main.dart

@ -46,7 +46,6 @@ Future main() async {
AppStorage('accounts', sharedPreferences), AppStorage('accounts', sharedPreferences),
sharedPreferences, sharedPreferences,
globalOptions, globalOptions,
packageInfo,
allAppImplementations, allAppImplementations,
); );
final pushNotificationsBloc = PushNotificationsBloc( final pushNotificationsBloc = PushNotificationsBloc(

7
packages/neon/lib/src/blocs/accounts.dart

@ -7,7 +7,6 @@ import 'package:neon/src/blocs/user_details.dart';
import 'package:neon/src/blocs/user_status.dart'; import 'package:neon/src/blocs/user_status.dart';
import 'package:neon/src/models/account.dart'; import 'package:neon/src/models/account.dart';
import 'package:neon/src/neon.dart'; import 'package:neon/src/neon.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:rxdart/rxdart.dart'; import 'package:rxdart/rxdart.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
@ -30,7 +29,6 @@ class AccountsBloc extends Bloc implements AccountsBlocEvents, AccountsBlocState
this._storage, this._storage,
this._sharedPreferences, this._sharedPreferences,
this._globalOptions, this._globalOptions,
this._packageInfo,
this._allAppImplementations, this._allAppImplementations,
) { ) {
accounts.listen((final as) async { accounts.listen((final as) async {
@ -42,7 +40,7 @@ class AccountsBloc extends Bloc implements AccountsBlocEvents, AccountsBlocState
accounts.add( accounts.add(
_storage _storage
.getStringList(_keyAccounts)! .getStringList(_keyAccounts)!
.map((final a) => (Account.fromJson(json.decode(a) as Map<String, dynamic>))..setupClient(_packageInfo)) .map((final a) => Account.fromJson(json.decode(a) as Map<String, dynamic>))
.toList(), .toList(),
); );
} }
@ -69,7 +67,6 @@ class AccountsBloc extends Bloc implements AccountsBlocEvents, AccountsBlocState
final SharedPreferences _sharedPreferences; final SharedPreferences _sharedPreferences;
final GlobalOptions _globalOptions; final GlobalOptions _globalOptions;
final List<AppImplementation> _allAppImplementations; final List<AppImplementation> _allAppImplementations;
final PackageInfo _packageInfo;
final _keyAccounts = 'accounts'; final _keyAccounts = 'accounts';
final _keyLastUsedAccount = 'last-used-account'; final _keyLastUsedAccount = 'last-used-account';
@ -102,7 +99,6 @@ class AccountsBloc extends Bloc implements AccountsBlocEvents, AccountsBlocState
@override @override
void addAccount(final Account account) { void addAccount(final Account account) {
account.setupClient(_packageInfo);
if (activeAccount.valueOrNull == null) { if (activeAccount.valueOrNull == null) {
setActiveAccount(account); setActiveAccount(account);
} }
@ -140,7 +136,6 @@ class AccountsBloc extends Bloc implements AccountsBlocEvents, AccountsBlocState
@override @override
void updateAccount(final Account account) { void updateAccount(final Account account) {
account.setupClient(_packageInfo);
final as = accounts.value; final as = accounts.value;
final index = as.indexWhere((final a) => a.id == account.id); final index = as.indexWhere((final a) => a.id == account.id);
if (index == -1) { if (index == -1) {

2
packages/neon/lib/src/blocs/push_notifications.dart

@ -134,7 +134,7 @@ class PushNotificationsBloc extends Bloc implements PushNotificationsBlocEvents,
Future _registerUnifiedPushInstances(final List<Account> accounts) async { Future _registerUnifiedPushInstances(final List<Account> accounts) async {
// Notifications will only work on accounts with app password // Notifications will only work on accounts with app password
for (final account in accounts.where((final a) => a.appPassword != null)) { for (final account in accounts.where((final a) => a.password != null)) {
await UnifiedPush.registerApp(account.client.id); await UnifiedPush.registerApp(account.client.id);
} }
} }

54
packages/neon/lib/src/models/account.dart

@ -7,6 +7,29 @@ import 'package:package_info_plus/package_info_plus.dart';
part 'account.g.dart'; part 'account.g.dart';
Future<Account> getAccount(
final PackageInfo packageInfo,
final String serverURL,
final String loginName,
final String password,
) async {
final username = (await NextcloudClient(
serverURL,
loginName: loginName,
password: password,
).provisioningApi.getCurrentUser())
.ocs
.data
.id;
return Account(
serverURL: serverURL,
loginName: loginName,
username: username,
password: password,
userAgent: userAgent(packageInfo),
);
}
String userAgent(final PackageInfo packageInfo) { String userAgent(final PackageInfo packageInfo) {
var buildNumber = packageInfo.buildNumber; var buildNumber = packageInfo.buildNumber;
if (buildNumber == '') { if (buildNumber == '') {
@ -19,30 +42,30 @@ String userAgent(final PackageInfo packageInfo) {
class Account { class Account {
Account({ Account({
required this.serverURL, required this.serverURL,
required this.loginName,
required this.username, required this.username,
this.password, this.password,
this.appPassword, this.userAgent,
}) : assert( });
(password != null && appPassword == null) || (password == null && appPassword != null),
'Either password or appPassword has to be set',
);
factory Account.fromJson(final Map<String, dynamic> json) => _$AccountFromJson(json); factory Account.fromJson(final Map<String, dynamic> json) => _$AccountFromJson(json);
Map<String, dynamic> toJson() => _$AccountToJson(this); Map<String, dynamic> toJson() => _$AccountToJson(this);
final String serverURL; final String serverURL;
final String loginName;
final String username; final String username;
final String? password; final String? password;
final String? appPassword; final String? userAgent;
@override @override
// ignore: avoid_equals_and_hash_code_on_mutable_classes // ignore: avoid_equals_and_hash_code_on_mutable_classes
bool operator ==(final Object other) => bool operator ==(final Object other) =>
other is Account && other is Account &&
other.serverURL == serverURL && other.serverURL == serverURL &&
other.loginName == loginName &&
other.username == username && other.username == username &&
other.password == password && other.password == password &&
other.appPassword == appPassword; other.userAgent == userAgent;
@override @override
// ignore: avoid_equals_and_hash_code_on_mutable_classes // ignore: avoid_equals_and_hash_code_on_mutable_classes
@ -52,24 +75,15 @@ class Account {
NextcloudClient? _client; NextcloudClient? _client;
void setupClient(final PackageInfo packageInfo) { NextcloudClient get client => _client ??= NextcloudClient(
_client ??= NextcloudClient(
serverURL, serverURL,
loginName: loginName,
username: username, username: username,
password: appPassword ?? password, password: password,
userAgentOverride: userAgent(packageInfo), userAgentOverride: userAgent,
); );
} }
NextcloudClient get client {
if (_client == null) {
throw Exception('You need to call setupClient() first');
}
return _client!;
}
}
Map<String, String> _idCache = {}; Map<String, String> _idCache = {};
extension NextcloudClientHelpers on NextcloudClient { extension NextcloudClientHelpers on NextcloudClient {

6
packages/neon/lib/src/models/account.g.dart

@ -8,14 +8,16 @@ part of 'account.dart';
Account _$AccountFromJson(Map<String, dynamic> json) => Account( Account _$AccountFromJson(Map<String, dynamic> json) => Account(
serverURL: json['serverURL'] as String, serverURL: json['serverURL'] as String,
loginName: json['loginName'] as String,
username: json['username'] as String, username: json['username'] as String,
password: json['password'] as String?, password: json['password'] as String?,
appPassword: json['appPassword'] as String?, userAgent: json['userAgent'] as String?,
); );
Map<String, dynamic> _$AccountToJson(Account instance) => <String, dynamic>{ Map<String, dynamic> _$AccountToJson(Account instance) => <String, dynamic>{
'serverURL': instance.serverURL, 'serverURL': instance.serverURL,
'loginName': instance.loginName,
'username': instance.username, 'username': instance.username,
'password': instance.password, 'password': instance.password,
'appPassword': instance.appPassword, 'userAgent': instance.userAgent,
}; };

25
packages/neon/lib/src/pages/login.dart

@ -48,32 +48,39 @@ class _LoginPageState extends State<LoginPage> {
_loginBloc.loginFlowResult.listen((final result) async { _loginBloc.loginFlowResult.listen((final result) async {
if (result != null) { if (result != null) {
final account = Account( try {
serverURL: result.server, final account = await getAccount(
username: result.loginName, _packageInfo,
appPassword: result.appPassword, result.server,
)..setupClient(await PackageInfo.fromPlatform()); result.loginName,
result.appPassword,
);
if (!mounted) { if (!mounted) {
return; return;
} }
final accountsBloc = Provider.of<AccountsBloc>(context, listen: false);
if (widget.serverURL != null) { if (widget.serverURL != null) {
accountsBloc.updateAccount(account); _accountsBloc.updateAccount(account);
Navigator.of(context).pop(); Navigator.of(context).pop();
} else { } else {
for (final a in accountsBloc.accounts.value) { for (final a in _accountsBloc.accounts.value) {
if (a.id == account.id) { if (a.id == account.id) {
ExceptionWidget.showSnackbar(context, AppLocalizations.of(context).errorAccountAlreadyExists); ExceptionWidget.showSnackbar(context, AppLocalizations.of(context).errorAccountAlreadyExists);
await _loginBloc.refresh(); await _loginBloc.refresh();
return; return;
} }
} }
accountsBloc _accountsBloc
..addAccount(account) ..addAccount(account)
..setActiveAccount(account); ..setActiveAccount(account);
} }
} catch (e, s) {
debugPrint(e.toString());
debugPrint(s.toString());
ExceptionWidget.showSnackbar(context, e);
await _loginBloc.refresh();
}
} }
}); });
} }

15
packages/nextcloud/lib/src/client.dart

@ -5,6 +5,7 @@ class NextcloudClient extends Client {
// ignore: public_member_api_docs // ignore: public_member_api_docs
NextcloudClient( NextcloudClient(
super.baseURL, { super.baseURL, {
this.loginName,
this.username, this.username,
final String? password, final String? password,
final String? language, final String? language,
@ -19,20 +20,24 @@ class NextcloudClient extends Client {
}..removeWhere((final _, final value) => value == null)) }..removeWhere((final _, final value) => value == null))
.cast<String, String>(), .cast<String, String>(),
userAgent: userAgentOverride ?? appType.userAgent, userAgent: userAgentOverride ?? appType.userAgent,
authentication: username != null && password != null authentication: loginName != null && password != null
? HttpBasicAuthentication( ? HttpBasicAuthentication(
username: username, username: loginName,
password: password, password: password,
) )
: null, : null,
); );
/// Username used for all operations. Necessary for accessing WebDAV resources /// Identifier used for authentication. This can be the username or email or something else.
final String? loginName;
/// Username of the user on the server, it needs to be set for using WebDAV.
/// It can be obtained via the provisioning_api.
final String? username; final String? username;
WebDavClient? _webdav; WebDavClient? _webdav;
/// Client for WebDAV. Will be null if no username is set for the client /// Client for WebDAV. Username needs to be set in order to use it
WebDavClient get webdav { WebDavClient get webdav {
if (_webdav != null) { if (_webdav != null) {
return _webdav!; return _webdav!;
@ -43,7 +48,7 @@ class NextcloudClient extends Client {
return _webdav = WebDavClient( return _webdav = WebDavClient(
this, this,
'/remote.php/dav/files/${(super.authentication! as HttpBasicAuthentication).username}', '/remote.php/dav/files/$username',
); );
} }
} }

2
packages/nextcloud/test/helper.dart

@ -86,6 +86,7 @@ class DockerContainer {
class TestNextcloudClient extends NextcloudClient { class TestNextcloudClient extends NextcloudClient {
TestNextcloudClient( TestNextcloudClient(
super.baseURL, { super.baseURL, {
super.loginName,
super.username, super.username,
super.password, super.password,
super.language, super.language,
@ -135,6 +136,7 @@ Future<TestNextcloudClient> getTestClient(
final client = TestNextcloudClient( final client = TestNextcloudClient(
'http://localhost:${container.port}', 'http://localhost:${container.port}',
loginName: username,
username: username, username: username,
password: clientPassword, password: clientPassword,
appType: appType, appType: appType,

Loading…
Cancel
Save