mirror of
				https://github.com/inventree/inventree-app.git
				synced 2025-10-31 05:15:42 +00:00 
			
		
		
		
	Token auth (#434)
* Embed device platform information into token request * Remove username and password from userProfile * Display icon to show if profile has associated user token * Remove username / password from login settings screen * Refactor login procedure around token auth * Refactoring * Add profile login screen - Username / password values are not stored - Just to fetch api token * Login with basic auth * Pass profile to API when connecting * Remove _BASE_URL accessor - Fixes URL caching bug * Add more context to login screen * Add helper functions for unit tests - Change default port to 8000 (makes testing easier with local inventree instance) * api.dart handles basic auth now * fix api_test.dart * Further test improvements * linting fixes * Provide feedback when login fails * More linting * Record user details on login, and display in "about" widget * Fix string lookup * Add extra debug * Fix auth values * Fix user profile test
This commit is contained in:
		| @@ -17,37 +17,11 @@ void main() { | ||||
|  | ||||
|   setUp(() async { | ||||
|  | ||||
|     if (! await UserProfileDBManager().profileNameExists("Test Profile")) { | ||||
|       // Create and select a profile to user | ||||
|  | ||||
|       print("TEST: Creating profile for user 'testuser'"); | ||||
|  | ||||
|       await UserProfileDBManager().addProfile(UserProfile( | ||||
|         name: "Test Profile", | ||||
|         server: "http://localhost:12345", | ||||
|         username: "testuser", | ||||
|         password: "testpassword", | ||||
|         selected: true, | ||||
|       )); | ||||
|     } | ||||
|  | ||||
|     var prf = await UserProfileDBManager().getSelectedProfile(); | ||||
|  | ||||
|     // Ensure that the server settings are correct by default, | ||||
|     // as they can get overwritten by subsequent tests | ||||
|  | ||||
|     if (prf != null) { | ||||
|       prf.name = "Test Profile"; | ||||
|       prf.server = "http://localhost:12345"; | ||||
|       prf.username = "testuser"; | ||||
|       prf.password = "testpassword"; | ||||
|  | ||||
|       await UserProfileDBManager().updateProfile(prf); | ||||
|     } | ||||
|     await setupServerProfile(select: true); | ||||
|  | ||||
|     // Ensure the profile is selected | ||||
|     assert(! await UserProfileDBManager().selectProfileByName("Missing Profile")); | ||||
|     assert(await UserProfileDBManager().selectProfileByName("Test Profile")); | ||||
|     assert(await UserProfileDBManager().selectProfileByName(testServerName)); | ||||
|  | ||||
|   }); | ||||
|  | ||||
| @@ -71,53 +45,57 @@ void main() { | ||||
|       var api = InvenTreeAPI(); | ||||
|  | ||||
|       // Incorrect server address | ||||
|       var profile = await UserProfileDBManager().getSelectedProfile(); | ||||
|       var profile = await setupServerProfile(); | ||||
|  | ||||
|       assert(profile != null); | ||||
|       profile.server = "http://localhost:5555"; | ||||
|  | ||||
|       if (profile != null) { | ||||
|         profile.server = "http://localhost:5555"; | ||||
|         await UserProfileDBManager().updateProfile(profile); | ||||
|       bool result = await api.connectToServer(profile); | ||||
|       assert(!result); | ||||
|  | ||||
|         bool result = await api.connectToServer(); | ||||
|         assert(!result); | ||||
|       debugContains("SocketException at"); | ||||
|  | ||||
|         debugContains("SocketException at"); | ||||
|       // Test incorrect login details | ||||
|       profile.server = testServerAddress; | ||||
|  | ||||
|         // Test incorrect login details | ||||
|         profile.server = "http://localhost:12345"; | ||||
|         profile.username = "invalidusername"; | ||||
|       final response = await api.fetchToken(profile, "baduser", "badpassword"); | ||||
|       assert(!response.successful()); | ||||
|  | ||||
|         await UserProfileDBManager().updateProfile(profile); | ||||
|       debugContains("Token request failed"); | ||||
|  | ||||
|         await api.connectToServer(); | ||||
|         assert(!result); | ||||
|       assert(!api.checkConnection()); | ||||
|  | ||||
|         debugContains("Token request failed"); | ||||
|       debugContains("Token request failed: STATUS 401"); | ||||
|       debugContains("showSnackIcon: 'Not Connected'"); | ||||
|  | ||||
|         assert(!api.checkConnection()); | ||||
|     }); | ||||
|  | ||||
|         debugContains("Token request failed: STATUS 401"); | ||||
|         debugContains("showSnackIcon: 'Not Connected'"); | ||||
|     test("Bad Token", () async { | ||||
|       // Test that login fails with a bad token | ||||
|       var profile = await setupServerProfile(); | ||||
|  | ||||
|       } else { | ||||
|         assert(false); | ||||
|       } | ||||
|       profile.token = "bad-token"; | ||||
|  | ||||
|       bool result = await InvenTreeAPI().connectToServer(profile); | ||||
|       assert(!result); | ||||
|     }); | ||||
|  | ||||
|     test("Login Success", () async { | ||||
|       // Test that we can login to the server successfully | ||||
|       var api = InvenTreeAPI(); | ||||
|  | ||||
|       // Attempt to connect | ||||
|       final bool result = await api.connectToServer(); | ||||
|       final profile = await setupServerProfile(select: true, fetchToken: true); | ||||
|       assert(profile.hasToken); | ||||
|  | ||||
|       // Now, connect to the server | ||||
|       bool result = await api.connectToServer(profile); | ||||
|  | ||||
|       // Check expected values | ||||
|       assert(result); | ||||
|       assert(api.hasToken); | ||||
|       expect(api.baseUrl, equals("http://localhost:12345/")); | ||||
|  | ||||
|       expect(api.baseUrl, equals(testServerAddress)); | ||||
|  | ||||
|       assert(api.hasToken); | ||||
|       assert(api.isConnected()); | ||||
|       assert(!api.isConnecting()); | ||||
|       assert(api.checkConnection()); | ||||
| @@ -127,7 +105,8 @@ void main() { | ||||
|       // Test server version information | ||||
|       var api = InvenTreeAPI(); | ||||
|  | ||||
|       assert(await api.connectToServer()); | ||||
|       final profile = await setupServerProfile(fetchToken: true); | ||||
|       assert(await api.connectToServer(profile)); | ||||
|  | ||||
|       // Check supported functions | ||||
|       assert(api.apiVersion >= 50); | ||||
| @@ -135,12 +114,15 @@ void main() { | ||||
|       assert(api.supportsNotifications); | ||||
|       assert(api.supportsPoReceive); | ||||
|  | ||||
|       // Ensure we can request (and receive) user roles | ||||
|       assert(await api.getUserRoles()); | ||||
|       assert(api.serverInstance.isNotEmpty); | ||||
|       assert(api.serverVersion.isNotEmpty); | ||||
|  | ||||
|       // Ensure we can have user role data | ||||
|       assert(api.roles.isNotEmpty); | ||||
|  | ||||
|       // Check available permissions | ||||
|       assert(api.checkPermission("part", "change")); | ||||
|       assert(api.checkPermission("stocklocation", "delete")); | ||||
|       assert(api.checkPermission("stock_location", "delete")); | ||||
|       assert(!api.checkPermission("part", "weirdpermission")); | ||||
|       assert(api.checkPermission("blah", "bloo")); | ||||
|  | ||||
|   | ||||
| @@ -10,7 +10,6 @@ import "package:flutter_test/flutter_test.dart"; | ||||
| import "package:inventree/api.dart"; | ||||
| import "package:inventree/barcode/barcode.dart"; | ||||
| import "package:inventree/helpers.dart"; | ||||
| import "package:inventree/user_profile.dart"; | ||||
|  | ||||
| import "package:inventree/inventree/part.dart"; | ||||
| import "package:inventree/inventree/stock.dart"; | ||||
| @@ -23,26 +22,7 @@ void main() { | ||||
|  | ||||
|   // Connect to the server | ||||
|   setUpAll(() async { | ||||
|     final prf = await UserProfileDBManager().getProfileByName("Test Profile"); | ||||
|  | ||||
|     if (prf != null) { | ||||
|       await UserProfileDBManager().deleteProfile(prf); | ||||
|     } | ||||
|  | ||||
|     bool result = await UserProfileDBManager().addProfile( | ||||
|       UserProfile( | ||||
|         name: "Test Profile", | ||||
|         server: "http://localhost:12345", | ||||
|         username: "testuser", | ||||
|         password: "testpassword", | ||||
|         selected: true, | ||||
|       ), | ||||
|     ); | ||||
|  | ||||
|     assert(result); | ||||
|  | ||||
|     assert(await UserProfileDBManager().selectProfileByName("Test Profile")); | ||||
|     assert(await InvenTreeAPI().connectToServer()); | ||||
|     await connectToTestServer(); | ||||
|   }); | ||||
|  | ||||
|   setUp(() async { | ||||
| @@ -91,8 +71,8 @@ void main() { | ||||
|     test("Scan Into Location", () async { | ||||
|  | ||||
|       final item = await InvenTreeStockItem().get(1) as InvenTreeStockItem?; | ||||
|  | ||||
|       assert(item != null); | ||||
|  | ||||
|       assert(item!.pk == 1); | ||||
|  | ||||
|       var handler = StockItemScanIntoLocationHandler(item!); | ||||
|   | ||||
| @@ -5,7 +5,6 @@ | ||||
| import "package:test/test.dart"; | ||||
|  | ||||
| import "package:inventree/api.dart"; | ||||
| import "package:inventree/user_profile.dart"; | ||||
| import "package:inventree/inventree/model.dart"; | ||||
| import "package:inventree/inventree/part.dart"; | ||||
|  | ||||
| @@ -16,16 +15,7 @@ void main() { | ||||
|   setupTestEnv(); | ||||
|  | ||||
|   setUp(() async { | ||||
|     await UserProfileDBManager().addProfile(UserProfile( | ||||
|       name: "Test Profile", | ||||
|       server: "http://localhost:12345", | ||||
|       username: "testuser", | ||||
|       password: "testpassword", | ||||
|       selected: true, | ||||
|     )); | ||||
|  | ||||
|     assert(await UserProfileDBManager().selectProfileByName("Test Profile")); | ||||
|     assert(await InvenTreeAPI().connectToServer()); | ||||
|     await connectToTestServer(); | ||||
|   }); | ||||
|  | ||||
|   group("Category Tests:", () { | ||||
|   | ||||
| @@ -1,6 +1,8 @@ | ||||
|  | ||||
| import "package:flutter/services.dart"; | ||||
| import "package:flutter_test/flutter_test.dart"; | ||||
| import "package:inventree/api.dart"; | ||||
| import "package:inventree/user_profile.dart"; | ||||
|  | ||||
| // This is the same as the following issue except it keeps the http client | ||||
| // TestWidgetsFlutterBinding.ensureInitialized(); | ||||
| @@ -19,4 +21,78 @@ void setupTestEnv() { | ||||
|       .setMockMethodCallHandler(channel, (MethodCall methodCall) async { | ||||
|     return "."; | ||||
|   }); | ||||
| } | ||||
|  | ||||
| // Accessors for default testing values | ||||
| const String testServerAddress = "http://localhost:8000/"; | ||||
| const String testServerName = "Test Server"; | ||||
| const String testUsername = "testuser"; | ||||
| const String testPassword = "testpassword"; | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * Request an API token for the given profile | ||||
|  */ | ||||
| Future<bool> fetchProfileToken({ | ||||
|   UserProfile? profile, | ||||
|   String username = testUsername, | ||||
|   String password = testPassword | ||||
| }) async { | ||||
|  | ||||
|   profile ??= await UserProfileDBManager().getProfileByName(testServerName); | ||||
|  | ||||
|   assert(profile != null); | ||||
|  | ||||
|   final response = await InvenTreeAPI().fetchToken(profile!, username, password); | ||||
|   return response.successful(); | ||||
| } | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * Setup a valid profile, and return it | ||||
|  */ | ||||
| Future<UserProfile> setupServerProfile({bool select = true, bool fetchToken = false}) async { | ||||
|   // Setup a valid server profile | ||||
|  | ||||
|   UserProfile? profile = await UserProfileDBManager().getProfileByName(testServerName); | ||||
|  | ||||
|   if (profile == null) { | ||||
|     // Profile does not already exist - create it! | ||||
|     bool result = await UserProfileDBManager().addProfile( | ||||
|         UserProfile( | ||||
|           server: testServerAddress, | ||||
|           name: testServerName | ||||
|         ) | ||||
|     ); | ||||
|  | ||||
|     assert(result); | ||||
|   } | ||||
|  | ||||
|   profile = await UserProfileDBManager().getProfileByName(testServerName); | ||||
|   assert(profile != null); | ||||
|  | ||||
|   if (select) { | ||||
|     assert(await UserProfileDBManager().selectProfileByName(testServerName)); | ||||
|   } | ||||
|  | ||||
|   if (fetchToken && !profile!.hasToken) { | ||||
|     final bool result = await fetchProfileToken(profile: profile); | ||||
|     assert(result); | ||||
|     assert(profile.hasToken); | ||||
|   } | ||||
|  | ||||
|   return profile!; | ||||
| } | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * Complete all steps necessary to login to the server | ||||
|  */ | ||||
| Future<void> connectToTestServer() async { | ||||
|  | ||||
|   // Setup profile, and fetch user token as necessary | ||||
|   final profile = await setupServerProfile(fetchToken: true); | ||||
|  | ||||
|   // Connect to the server | ||||
|   assert(await InvenTreeAPI().connectToServer(profile)); | ||||
| } | ||||
| @@ -27,10 +27,8 @@ void main() { | ||||
|  | ||||
|     // Now, create one! | ||||
|     bool result = await UserProfileDBManager().addProfile(UserProfile( | ||||
|       name: "Test Profile", | ||||
|       username: "testuser", | ||||
|       password: "testpassword""", | ||||
|       server: "http://localhost:12345", | ||||
|       name: testServerName, | ||||
|       server: testServerAddress, | ||||
|       selected: true, | ||||
|     )); | ||||
|  | ||||
| @@ -62,20 +60,15 @@ void main() { | ||||
|     test("Add Invalid Profiles", () async { | ||||
|       // Add a profile with missing data | ||||
|       bool result = await UserProfileDBManager().addProfile( | ||||
|         UserProfile( | ||||
|           username: "what", | ||||
|           password: "why", | ||||
|         ) | ||||
|         UserProfile() | ||||
|       ); | ||||
|  | ||||
|       expect(result, equals(false)); | ||||
|  | ||||
|       // Add a profile with a name that already exists | ||||
|       // Add a profile with a new name | ||||
|       result = await UserProfileDBManager().addProfile( | ||||
|         UserProfile( | ||||
|           name: "Test Profile", | ||||
|           username: "xyz", | ||||
|           password: "hunter42", | ||||
|           name: "Another Test Profile", | ||||
|         ) | ||||
|       ); | ||||
|  | ||||
| @@ -84,14 +77,14 @@ void main() { | ||||
|       // Check that the number of protocols available is still the same | ||||
|       var profiles = await UserProfileDBManager().getAllProfiles(); | ||||
|  | ||||
|       expect(profiles.length, equals(1)); | ||||
|       expect(profiles.length, equals(2)); | ||||
|     }); | ||||
|  | ||||
|     test("Profile Name Check", () async { | ||||
|       bool result = await UserProfileDBManager().profileNameExists("doesnotexist"); | ||||
|       expect(result, equals(false)); | ||||
|  | ||||
|       result = await UserProfileDBManager().profileNameExists("Test Profile"); | ||||
|       result = await UserProfileDBManager().profileNameExists("Test Server"); | ||||
|       expect(result, equals(true)); | ||||
|     }); | ||||
|  | ||||
| @@ -104,23 +97,16 @@ void main() { | ||||
|       if (prf != null) { | ||||
|         UserProfile p = prf; | ||||
|  | ||||
|         expect(p.name, equals("Test Profile")); | ||||
|         expect(p.username, equals("testuser")); | ||||
|         expect(p.password, equals("testpassword")); | ||||
|         expect(p.server, equals("http://localhost:12345")); | ||||
|         expect(p.name, equals(testServerName)); | ||||
|         expect(p.server, equals(testServerAddress)); | ||||
|  | ||||
|         expect(p.toString(), equals("<${p.key}> Test Profile : http://localhost:12345 - testuser:testpassword")); | ||||
|         expect(p.toString(), equals("<${p.key}> Test Server : http://localhost:8000/")); | ||||
|  | ||||
|         // Test that we can update the profile | ||||
|         p.name = "different name"; | ||||
|  | ||||
|         bool result = await UserProfileDBManager().updateProfile(p); | ||||
|         expect(result, equals(true)); | ||||
|  | ||||
|         // Trying to update with an invalid value will fail! | ||||
|         p.password = ""; | ||||
|         result = await UserProfileDBManager().updateProfile(p); | ||||
|         expect(result, equals(false)); | ||||
|       } | ||||
|     }); | ||||
|   }); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user