Skip to content

Commit

Permalink
added basic auth support & fixed storage bug; fix #SNRGY-3211
Browse files Browse the repository at this point in the history
  • Loading branch information
hahahannes committed Mar 19, 2024
1 parent cda34d1 commit 375bc64
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 30 deletions.
4 changes: 2 additions & 2 deletions lib/services/device_manager_old.dart
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,11 @@ class DeviceManagerOld {
final devicesUrl = basePath + "/devices";
final Response<Map<String, dynamic>> resp;
try {
_logger.d(LOG_PREFIX + ": Try to load devices");
_logger.d(LOG_PREFIX + ": Try to load devices from: " + devicesUrl);
resp = await dio.get<Map<String, dynamic>>(devicesUrl);
return resp;
} on DioError catch (e) {
_logger.d(LOG_PREFIX + ": Could not load devices");
_logger.d(LOG_PREFIX + ": Could not load devices: " + e.message);
if (e.response?.statusCode == null || e.response!.statusCode! > 304) {
throw UnexpectedStatusCodeException(e.response?.statusCode, "$devicesUrl ${e.message}");
}
Expand Down
17 changes: 16 additions & 1 deletion lib/services/mgw/restricted.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
* limitations under the License.
*/

import 'dart:convert';
import 'package:dio/dio.dart';
import 'package:logger/logger.dart';
import 'package:mobile_app/services/mgw/auth.dart';
Expand Down Expand Up @@ -46,6 +47,14 @@ class MgwService {
return loginResponse.token;
}

Future<String> GetBasicAuthValue() async {
_logger.d(LOG_PREFIX + ": Load basic auth credentials from storage");
var password = await MgwStorage.LoadBasicAuthCredentials();
String basicAuth =
'Basic ' + base64.encode(utf8.encode('admin:$password'));
return basicAuth;
}

LoadCredentialsFromStorage() async {
_logger.d(LOG_PREFIX + ": Load device credentials from storage");
deviceCredentials = await MgwStorage.LoadCredentials();
Expand All @@ -58,7 +67,13 @@ class MgwService {
if(authenticate) {
dio.interceptors.add(InterceptorsWrapper(onRequest: (options, handler) async {
_logger.d(LOG_PREFIX + ": Set auth headers");
options.headers?['X-Session-Token'] = await GetSessionToken();
try {
_logger.d("Try to get session token");
options.headers?['X-Session-Token'] = await GetSessionToken();
} catch (e) {
_logger.d("Try to get basic auth");
options.headers?['Authorization'] = await GetBasicAuthValue();
}
_logger.d(LOG_PREFIX + ": End interceptor");
return handler.next(options);
}));
Expand Down
43 changes: 35 additions & 8 deletions lib/services/mgw/storage.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const LOG_PREFIX = "MGW-STORAGE-SERVICE";

class MgwStorage {
static const _mgwCredentialsKeyPrefix = "credentials_";
static const _mgwBasicAuthCredentialsKeyPrefix = "basic_auth_credentials_";
static const _mgwConnectedKeyPrefix = "connected_mgws_";

static const _boxName = "mgw.box";
Expand All @@ -23,19 +24,21 @@ class MgwStorage {
if(isInitialized) return;
Hive.init((await getApplicationDocumentsDirectory()).path);
_box = await Hive.openBox<String>(_boxName);
_logger.d("TEST");
_logger.d(_box?.get(_mgwConnectedKeyPrefix));
isInitialized = true;
}

static Future<void> StoreCredentials(DeviceUserCredentials user) async {
init();
await init();
_logger.d(LOG_PREFIX + ": Store mgw device credentials for: " + user.login);
var credentials = json.encode(user);
return await _box?.put(_mgwCredentialsKeyPrefix, credentials).then((
value) => _box?.flush());
}

static Future<DeviceUserCredentials> LoadCredentials() async {
init();
await init();
_logger.d(LOG_PREFIX + ": Load mgw device credentials");
var credentials = await _box?.get(_mgwCredentialsKeyPrefix);
if(credentials != null) {
Expand All @@ -47,7 +50,7 @@ class MgwStorage {
}

static Future<void> StorePairedMGW(MGW mgw) async {
init();
await init();
_logger.d(LOG_PREFIX + ": Store paired mgw: " + mgw.mDNSServiceName);
var storedMGWs = await LoadPairedMGWs();
storedMGWs.add(mgw);
Expand All @@ -56,14 +59,38 @@ class MgwStorage {
}

static Future<List<MGW>> LoadPairedMGWs() async {
init();
_logger.d(LOG_PREFIX + ": Load mgw device credentials");
await init();
_logger.d(LOG_PREFIX + ": Load paired mgws");
var encodedMgws = await _box?.get(_mgwConnectedKeyPrefix);
List<MGW> mgws = [];
if(encodedMgws != null) {
List<MGW> mgws = json.decode(encodedMgws);
_logger.d(LOG_PREFIX + ": Loaded mgws");
for(final mgw in jsonDecode(encodedMgws)) {
mgws.add(MGW.fromJson(mgw));
}
return mgws;
}
return [];
_logger.d(LOG_PREFIX + ": Loaded mgws: " + mgws.toString());
return mgws;
}



// TODO: remove loading and saving of basic auth credentials later
static Future<void> StoreBasicAuthCredentials(String password) async {
await init();
_logger.d(LOG_PREFIX + ": Store mgw device basic auth credentials: " + password);
return await _box?.put(_mgwBasicAuthCredentialsKeyPrefix, password).then((
value) => _box?.flush());
}

static Future<String> LoadBasicAuthCredentials() async {
await init();
_logger.d(LOG_PREFIX + ": Load mgw device basic auth credentials");
var password = await _box?.get(_mgwBasicAuthCredentialsKeyPrefix);
if(password != null) {
_logger.d(LOG_PREFIX + ": Loaded mgw device basic auth credentials");
return password;
}
throw("Credentials not stored");
}
}
93 changes: 75 additions & 18 deletions lib/widgets/tabs/gateways/mgw_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,47 @@ import 'package:mobile_app/widgets/shared/toast.dart';
import 'package:nsd/nsd.dart';
import 'package:provider/provider.dart';

const double TOP_PADDING = 100;
const textStyle = TextStyle(color: Colors.white, fontSize: 35);

final _logger = Logger(
printer: SimplePrinter(),
);

TextEditingController _textFieldController = TextEditingController();

Future<void> pairWithBasicAuth(BuildContext context, MGW mgw) async {
// TODO remove pairing with basic auth credentials
await showDialog<void>(
context: context,
builder: (context) {
return AlertDialog(
title: Text('Password'),
content: TextField(
controller: _textFieldController,
decoration: InputDecoration(hintText: "Password"),
),
actions: <Widget>[
TextButton(
child: Text('CANCEL'),
onPressed: () {
Navigator.pop(context);
},
),
TextButton(
child: Text('OK'),
onPressed: () async {
var password = _textFieldController.text;
await MgwStorage.StoreBasicAuthCredentials(password);
Navigator.pop(context);
},
),
],
);
},
);
}

Future<List<MGW>> DiscoverLocalGatewayHosts() async {
_logger.d("Discover local gateways...");
Discovery discovery = await startDiscovery('_snrgy._tcp', ipLookupType: IpLookupType.any);
Expand All @@ -46,7 +83,7 @@ Future<List<MGW>> DiscoverLocalGatewayHosts() async {
var serviceName = service.name??"";
var coreId = utf8.decode(service.txt?["core-id"]??[]);

var ip = service.addresses?[0].host??"";
var ip = service.addresses?[0].address??"";
if(!foundHostnames.contains(hostname)) {
var gateway = MGW(hostname, serviceName, coreId, ip);
gateways.add(gateway);
Expand All @@ -60,7 +97,8 @@ Future<List<MGW>> DiscoverLocalGatewayHosts() async {
return gateways;
}

Future<void> PairWithGateway(String host) async {
Future<void> PairWithGateway(MGW mgw) async {
var host = mgw.ip;
MgwAuthService authService = MgwAuthService(host);

_logger.d("Pair with gateway: "+ host);
Expand All @@ -72,13 +110,23 @@ Future<void> PairWithGateway(String host) async {
_logger.d("Stored credentials");
}

const double TOP_PADDING = 100;
const textStyle = TextStyle(color: Colors.white, fontSize: 35);
Future<void> StoreGateway(MGW mgw, AppState appState) async {
_logger.d("Store paired mgw");
await MgwStorage.StorePairedMGW(mgw);
_logger.d("Stored mgw");

appState.gateways.add(mgw);
}

class AddLocalNetwork extends StatelessWidget {
class AddLocalNetwork extends StatefulWidget {
const AddLocalNetwork({Key? key}) : super(key: key);

handleData(List<MGW> mgws, AppState appState) {
@override
_AddLocalNetworkState createState() => _AddLocalNetworkState();
}

class _AddLocalNetworkState extends State<AddLocalNetwork> {
handleData(List<MGW> mgws, AppState appState, widgetBuildContext) {
if (mgws.length == 0) {
return Column(
children: [
Expand Down Expand Up @@ -110,18 +158,26 @@ class AddLocalNetwork extends StatelessWidget {
Icons.add
),
onPressed: () async {
var ip = mgw.ip;
try {
await PairWithGateway(ip);
appState.gateways.add(mgw);
_logger.d("Try to pair token based");
await PairWithGateway(mgw);
await StoreGateway(mgw, appState);
} on Failure catch (e) {
if (e.errorCode == ErrorCode.SERVER_ERROR) {
// TODO dont use 500 to inidcate that pairing is closed
Toast.showToastNoContext(
"Pairing was not possible. Check if pairing mode is enabled!");
_logger.e("Pairing is not possible: " + e.detailedMessage);
if (e.errorCode == ErrorCode.UNAUTHORIZED) {
// MGW is still using basic auth protection -> ask user for password
try {
_logger.d("Try to pair basic auth based");
await pairWithBasicAuth(widgetBuildContext, mgw);
await StoreGateway(mgw, appState);
} catch (e) {
_logger.e("Pairing is not possible: " + e.toString());
Toast.showToastNoContext(
"Pairing was not possible");
}
} else {
Toast.showToastNoContext(
"Pairing was not possible!");
"Pairing was not possible. Check if pairing mode is enabled!");
}
}
Navigator.pop(context);
Expand Down Expand Up @@ -154,7 +210,8 @@ class AddLocalNetwork extends StatelessWidget {

handleLoading() {
return Column(
children: [
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.only(top: TOP_PADDING),
child: SizedBox(
Expand All @@ -170,9 +227,9 @@ class AddLocalNetwork extends StatelessWidget {
]);
}

handleResponse(servicesWrapper, AppState appState) {
handleResponse(servicesWrapper, AppState appState, context) {
if (servicesWrapper.hasData) {
return handleData(servicesWrapper.data!, appState);
return handleData(servicesWrapper.data!, appState, context);
}
if (servicesWrapper.hasError) {
return handleError(servicesWrapper.error);
Expand All @@ -191,7 +248,7 @@ class AddLocalNetwork extends StatelessWidget {
future: DiscoverLocalGatewayHosts(),
builder: (BuildContext context,
AsyncSnapshot<List<MGW>> servicesWrapper) {
return handleResponse(servicesWrapper, state);
return handleResponse(servicesWrapper, state, context);
})
);
});
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 0.0.289+289
version: 0.0.290+290

environment:
sdk: ">=2.17.0 <3.0.0"
Expand Down

0 comments on commit 375bc64

Please sign in to comment.