From ec02aaa4066c166b92be2c45de70bf13647a60d2 Mon Sep 17 00:00:00 2001 From: Balazs Toldi Date: Mon, 9 May 2022 09:55:11 +0200 Subject: [PATCH] API authentication --- lib/main.dart | 112 ++++++++++++++--- lib/model/ApiAccess.dart | 6 + lib/model/user.dart | 2 + lib/service/AuthenticationChecker.dart | 21 ++++ lib/service/gitea_service.dart | 25 ++++ pubspec.lock | 163 ++++++++++++++++++++++++- pubspec.yaml | 10 +- 7 files changed, 320 insertions(+), 19 deletions(-) create mode 100644 lib/model/ApiAccess.dart create mode 100644 lib/service/AuthenticationChecker.dart create mode 100644 lib/service/gitea_service.dart diff --git a/lib/main.dart b/lib/main.dart index 615043e..4dd2f2c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,6 +1,10 @@ import 'package:flutter/material.dart'; +import 'package:gitea_client/model/user.dart'; +import 'package:gitea_client/service/AuthenticationChecker.dart'; import 'package:url_launcher/url_launcher.dart'; +import 'model/ApiAccess.dart'; + void main() { runApp(const MyApp()); } @@ -17,6 +21,22 @@ class MyApp extends StatelessWidget { primarySwatch: Colors.lightGreen, ), home: const LoginPage(title: 'Gitea client'), + routes: { + + }, + onGenerateRoute: (route) { + switch (route.name) { // <- here + case "/loginstatus": + return MaterialPageRoute( + settings: const RouteSettings(name: "/parameterpage"), + builder: (context) => StatefulLoginStatus( + apiAccess: route.arguments as ApiAccess, + ), + ); + } + return null; + }, + ); } } @@ -60,7 +80,8 @@ class StatefulLoginForm extends StatefulWidget { } class _LoginForm extends State { - final Uri getTokenUri = Uri.parse("https://www.jetbrains.com/help/youtrack/incloud/integration-with-gitea.html#enable-youtrack-integration-gitea"); + final Uri getTokenUri = Uri.parse( + "https://www.jetbrains.com/help/youtrack/incloud/integration-with-gitea.html#enable-youtrack-integration-gitea"); final tokenController = TextEditingController(); final instanceController = TextEditingController(); String instance = "gitea.com"; @@ -76,7 +97,7 @@ class _LoginForm extends State { children: [ DropdownButton( value: instance, - items: ['gitea.com', 'codeberg.com', 'Other'] + items: ['gitea.com', 'codeberg.org', 'Other'] .map>((String value) { return DropdownMenuItem( value: value, @@ -105,26 +126,89 @@ class _LoginForm extends State { obscureText: true, ), SizedBox( - width: (media.width > 600) ? media.width * 0.25 : media.width * 0.5, + width: + (media.width > 600) ? media.width * 0.25 : media.width * 0.5, child: Container( - padding: EdgeInsetsDirectional.all(15), - child: ElevatedButton( - child: const Text("Login"), - onPressed: () => { - - }, - ), - )), + padding: EdgeInsetsDirectional.all(15), + child: ElevatedButton( + child: const Text("Login"), + onPressed: () => { + Navigator.pushNamed(context, "/loginstatus", + arguments: ApiAccess( + (instance == "Other") + ? instanceController.text + : instance, + tokenController.text)) + }, + ), + )), // - TextButton(onPressed: () => { - _launchUrl(getTokenUri) - }, child: Text("Need a Token? Find out how to generate one!")) + TextButton( + onPressed: () => {_launchUrl(getTokenUri)}, + child: Text("Need a Token? Find out how to generate one!")) ], ), ); } } +class StatefulLoginStatus extends StatefulWidget { + final ApiAccess apiAccess; + const StatefulLoginStatus({Key? key, required this.apiAccess}) : super(key: key); + + @override + _StatefulLoginStatus createState() => _StatefulLoginStatus(); +} + +class _StatefulLoginStatus extends State { + + + Future? userRequest; + + @override + void initState() { + userRequest = AuthenticationChecker(widget.apiAccess).getAuthenticatedUserOrError(); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text("Login status"), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + FutureBuilder( + future: userRequest, + builder: (context, snapshot) { + if (snapshot.hasError){ + return Center( + child: Text( + "Hiba történt: ${snapshot.error}" + ), + ); + } else if (snapshot.hasData){ + var user = snapshot.data!; + final username = user.username; + return Text("Logged in as $username"); + + } else { + return Center( + child: CircularProgressIndicator(), + ); + } + } + ) + ], + ), + )); + } +} + void _launchUrl(Uri url) async { if (!await launchUrl(url)) throw 'Could not launch $url'; } diff --git a/lib/model/ApiAccess.dart b/lib/model/ApiAccess.dart new file mode 100644 index 0000000..93905f7 --- /dev/null +++ b/lib/model/ApiAccess.dart @@ -0,0 +1,6 @@ +class ApiAccess{ + final String instance; + final String token; + + const ApiAccess(this.instance, this.token); +} \ No newline at end of file diff --git a/lib/model/user.dart b/lib/model/user.dart index e65ed53..a591830 100644 --- a/lib/model/user.dart +++ b/lib/model/user.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + class AuthenticatedUser { int? id; String? login; diff --git a/lib/service/AuthenticationChecker.dart b/lib/service/AuthenticationChecker.dart new file mode 100644 index 0000000..bb8b6b8 --- /dev/null +++ b/lib/service/AuthenticationChecker.dart @@ -0,0 +1,21 @@ +import 'package:gitea_client/model/ApiAccess.dart'; +import 'package:gitea_client/service/gitea_service.dart'; + +import '../model/user.dart'; + +class AuthenticationChecker { + final ApiAccess apiAccess; + late GiteaServie service; + AuthenticationChecker(this.apiAccess) { + service = GiteaServie(apiAccess.instance, apiAccess.token); + } + + Future getAuthenticatedUserOrError() async{ + final user = await service.getAuthenticatedUser(); + if(user.username != null) { + return user; + } + throw Exception("Authentication failed"); + } + +} \ No newline at end of file diff --git a/lib/service/gitea_service.dart b/lib/service/gitea_service.dart new file mode 100644 index 0000000..11905a1 --- /dev/null +++ b/lib/service/gitea_service.dart @@ -0,0 +1,25 @@ +import 'dart:convert'; + +import 'package:flutter/foundation.dart'; +import 'package:gitea_client/model/user.dart'; +import 'package:http/http.dart' as http; + +AuthenticatedUser _parseAuthenticatedUserResponse(String message){ + return AuthenticatedUser.fromJson(jsonDecode(message)); +} + +class GiteaServie { + final String apiUrl; + final String _token; + + const GiteaServie(this.apiUrl, this._token); + + Future getAuthenticatedUser() async { + var response = await http.get( + Uri.https(apiUrl, "api/v1/user", { + "token": _token, + }), + ); + return compute(_parseAuthenticatedUserResponse, response.body); + } +} \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index 7a95d19..aec2822 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,6 +1,27 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + url: "https://pub.dartlang.org" + source: hosted + version: "39.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.0" + args: + dependency: transitive + description: + name: args + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.0" async: dependency: transitive description: @@ -15,6 +36,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.0" + build: + dependency: transitive + description: + name: build + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.0" + build_config: + dependency: transitive + description: + name: build_config + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" characters: dependency: transitive description: @@ -29,6 +64,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.3.1" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" clock: dependency: transitive description: @@ -43,6 +85,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.15.0" + convert: + dependency: transitive + description: + name: convert + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" + crypto: + dependency: transitive + description: + name: crypto + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.2" cupertino_icons: dependency: "direct main" description: @@ -50,6 +106,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.4" + dart_style: + dependency: transitive + description: + name: dart_style + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.3" fake_async: dependency: transitive description: @@ -57,6 +120,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.2.0" + file: + dependency: transitive + description: + name: file + url: "https://pub.dartlang.org" + source: hosted + version: "6.1.2" flutter: dependency: "direct main" description: flutter @@ -84,6 +154,27 @@ packages: description: flutter source: sdk version: "0.0.0" + glob: + dependency: transitive + description: + name: glob + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.2" + http: + dependency: "direct main" + description: + name: http + url: "https://pub.dartlang.org" + source: hosted + version: "0.13.4" + http_parser: + dependency: transitive + description: + name: http_parser + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.0" intl: dependency: "direct main" description: @@ -98,6 +189,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.6.3" + json_annotation: + dependency: transitive + description: + name: json_annotation + url: "https://pub.dartlang.org" + source: hosted + version: "4.5.0" + json_serializable: + dependency: "direct dev" + description: + name: json_serializable + url: "https://pub.dartlang.org" + source: hosted + version: "6.2.0" lints: dependency: transitive description: @@ -105,6 +210,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.1" + logging: + dependency: transitive + description: + name: logging + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" matcher: dependency: transitive description: @@ -126,6 +238,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.7.0" + package_config: + dependency: transitive + description: + name: package_config + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.2" path: dependency: transitive description: @@ -140,11 +259,39 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.2" + pub_semver: + dependency: transitive + description: + name: pub_semver + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + pubspec_parse: + dependency: transitive + description: + name: pubspec_parse + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" sky_engine: dependency: transitive description: flutter source: sdk version: "0.0.99" + source_gen: + dependency: transitive + description: + name: source_gen + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.2" + source_helper: + dependency: transitive + description: + name: source_helper + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.2" source_span: dependency: transitive description: @@ -195,7 +342,7 @@ packages: source: hosted version: "1.3.0" url_launcher: - dependency: "direct dev" + dependency: "direct main" description: name: url_launcher url: "https://pub.dartlang.org" @@ -257,6 +404,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.1" + watcher: + dependency: transitive + description: + name: watcher + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + yaml: + dependency: transitive + description: + name: yaml + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.0" sdks: dart: ">=2.16.2 <3.0.0" flutter: ">=2.10.0" diff --git a/pubspec.yaml b/pubspec.yaml index 7f5554e..f8cbd22 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -32,8 +32,10 @@ dependencies: flutter_localizations: sdk: flutter intl: ^0.17.0 - - + # For opening links in the browser + url_launcher: ^6.1.0 + # For http communication + http: ^0.13.4 # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.2 @@ -41,14 +43,14 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - + json_serializable: ^6.2.0 # The "flutter_lints" package below contains a set of recommended lints to # encourage good coding practices. The lint set provided by the package is # activated in the `analysis_options.yaml` file located at the root of your # package. See that file for information about deactivating specific lint # rules and activating additional ones. flutter_lints: ^1.0.0 - url_launcher: ^6.1.0 + # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec