Add basic auth support for experimental networking features

This commit is contained in:
Elliot 2025-08-06 16:01:04 -04:00
parent bbfff64927
commit d0db195339
4 changed files with 114 additions and 8 deletions

View file

@ -92,8 +92,9 @@ class ApiService implements Authentication {
/// Takes a server URL and attempts to resolve the API endpoint.
///
/// Input: [schema://]host[:port][/path]
/// Input: [schema://][basicAuth:password@]host[:port][/path]
/// schema - optional (default: https)
/// userInfo - optional
/// host - required
/// port - optional (default: based on schema)
/// path - optional

View file

@ -16,12 +16,15 @@ String? getServerUrl() {
if (serverUri == null) {
return null;
}
return Uri.decodeFull(
serverUri.hasPort
? "${serverUri.scheme}://${serverUri.host}:${serverUri.port}"
: "${serverUri.scheme}://${serverUri.host}",
);
var URIToDecodeFull = "${serverUri.scheme}://";
if (serverUri.userInfo.isNotEmpty){
URIToDecodeFull += "${serverUri.userInfo}@";
}
URIToDecodeFull += "${serverUri.host}";
if(serverUri.hasPort){
URIToDecodeFull += ":${serverUri.port}";
}
return Uri.decodeFull(URIToDecodeFull);
}
/// Converts a Unicode URL to its ASCII-compatible encoding (Punycode).

View file

@ -59,7 +59,7 @@ void main() {
db.writeTxnSync(() => db.clearSync());
await StoreService.init(storeRepository: IsarStoreRepository(db));
});
// add test here
test('Should resolve HTTP endpoint', () async {
const testUrl = 'http://ip:2283';
const resolvedUrl = 'http://ip:2283/api';

View file

@ -0,0 +1,102 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/domain/services/store.service.dart';
import 'package:immich_mobile/entities/store.entity.dart';
import 'package:immich_mobile/infrastructure/repositories/store.repository.dart';
import 'package:immich_mobile/utils/url_helper.dart';
import 'package:isar/isar.dart';
import '../test_utils.dart';
import '../fixtures/user.stub.dart';
void main() {
late Isar db;
setUpAll(() async {
db = await TestUtils.initIsar();
await StoreService.init(storeRepository: IsarStoreRepository(db));
});
group('sanitizeUrl', () {
test('Should encode correctly', () {
var unEncodedURL = 'user:password@example.com/addSchemaAndRemovesSlashes////';
expect(sanitizeUrl(unEncodedURL), 'https://user:password@example.com/addSchemaAndRemovesSlashes');
});
test('does not switch http to https', () {
var unEncodedURL = 'http://user:password@example.com/addSchemaAndRemovesSlashes////';
expect(sanitizeUrl(unEncodedURL), 'http://user:password@example.com/addSchemaAndRemovesSlashes');
});
});
group('punycodeEncode', () {
test('malformed URL returns a blank string', () {
var badURL = 'example.com/missing a scheme';
expect(punycodeEncodeUrl(badURL), '');
});
test('Encodes IDNs correctly', () {
var idn = 'https://bücher.de';
expect(punycodeEncodeUrl(idn), 'https://xn--bcher-kva.de');
});
test('Keeps basic auth in encoding', () {
var basicAuthURL = 'https://user:password@example.com';
expect(punycodeEncodeUrl(basicAuthURL), 'https://user:password@example.com');
});
});
group('punycodeDecode', () {
test('malformed URL returns a null', () {
var badURL = 'example.com/missing%20a%20scheme';
expect(punycodeDecodeUrl(badURL), null);
});
test('Decodes IDNs correctly', () {
var idn = 'https://xn--bcher-kva.de';
expect(punycodeDecodeUrl(idn), 'https://bücher.de');
});
test('Keeps basic auth in encoding', () {
var basicAuthURL = 'https://user:password@example.com/%20with%20spaces';
expect(punycodeDecodeUrl(basicAuthURL), 'https://user:password@example.com/ with spaces');
});
});
group('getServerUrl', () {
test('Returns null if not set', () {
expect(getServerUrl(), null);
});
test('Returns null if what was set is not a correct url', () {
Store.put(StoreKey.serverEndpoint, 'example.com');
expect(getServerUrl(), null);
});
test('Returns decoded basic URL', () async {
var encodedURL = 'https://example.com/ clears extra paths';
await Store.put(StoreKey.serverEndpoint, encodedURL);
expect(getServerUrl(), 'https://example.com');
});
test('Returns decoded basic auth URL', () async {
var basicAuthURL = 'https://user:password@example.com';
await Store.put(StoreKey.serverEndpoint, basicAuthURL);
expect(getServerUrl(), basicAuthURL);
});
test('Returns decoded URL with a port', () async {
var portURL = 'https://example.com:1337';
await Store.put(StoreKey.serverEndpoint, portURL);
expect(getServerUrl(), portURL);
});
test('Returns decoded complex URL', () async {
var complexURL = 'https://user:password@example.com:1337/123/abc';
await Store.put(StoreKey.serverEndpoint, complexURL);
expect(getServerUrl(), 'https://user:password@example.com:1337');
});
});
}