mirror of
https://codeberg.org/shroff/phylum.git
synced 2026-05-06 12:19:35 -05:00
[client] Generate API Keys from the client [#15]
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:http/http.dart';
|
||||
import 'package:offtheline/offtheline.dart';
|
||||
import 'package:phylum/util/time.dart';
|
||||
|
||||
class GenerateApiKeyRequest extends ApiRequest {
|
||||
final String description;
|
||||
final DateTime? expires;
|
||||
final List<String> scopes;
|
||||
|
||||
GenerateApiKeyRequest({required this.description, required this.expires, required this.scopes});
|
||||
|
||||
@override
|
||||
BaseRequest createRequest(ApiClient api, {Uint8List? data}) {
|
||||
final uri = api.createUriBuilder('/api/v1/user/keys/generate');
|
||||
final request = Request('post', uri.build());
|
||||
final fields = <String, String>{
|
||||
'description': description,
|
||||
'scopes': scopes.join(','),
|
||||
};
|
||||
if (expires != null) {
|
||||
fields['expires'] = expires!.formatServer();
|
||||
}
|
||||
request.bodyFields = fields;
|
||||
|
||||
return request;
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -3,8 +3,8 @@ import 'dart:typed_data';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:offtheline/offtheline.dart';
|
||||
|
||||
class ApiKeysRequest extends ApiRequest {
|
||||
const ApiKeysRequest();
|
||||
class ListApiKeysRequest extends ApiRequest {
|
||||
const ListApiKeysRequest();
|
||||
|
||||
@override
|
||||
BaseRequest createRequest(ApiClient api, {Uint8List? data}) {
|
||||
@@ -0,0 +1,20 @@
|
||||
part of 'responses.dart';
|
||||
|
||||
class GenerateApiKeyResponse extends PhylumApiSuccessResponse {
|
||||
final String keyId;
|
||||
final String key;
|
||||
final String accessToken;
|
||||
|
||||
GenerateApiKeyResponse._({required this.keyId, required this.key, required this.accessToken});
|
||||
|
||||
factory GenerateApiKeyResponse.fromResponse(Map<String, dynamic> data) {
|
||||
return GenerateApiKeyResponse._(
|
||||
keyId: data['id'],
|
||||
key: data['key'],
|
||||
accessToken: data['token'],
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> process(PhylumAccount account) => SynchronousFuture(null);
|
||||
}
|
||||
+4
-4
@@ -16,12 +16,12 @@ class ApiKey {
|
||||
});
|
||||
}
|
||||
|
||||
class ApiKeysResponse extends PhylumApiSuccessResponse {
|
||||
class ListApiKeysResponse extends PhylumApiSuccessResponse {
|
||||
final List<ApiKey> keys;
|
||||
|
||||
ApiKeysResponse._({required this.keys});
|
||||
ListApiKeysResponse._({required this.keys});
|
||||
|
||||
factory ApiKeysResponse.fromResponse(List data) {
|
||||
factory ListApiKeysResponse.fromResponse(List data) {
|
||||
final keys = data.cast<Map>().map((map) {
|
||||
final k = map.cast<String, dynamic>();
|
||||
return ApiKey(
|
||||
@@ -32,7 +32,7 @@ class ApiKeysResponse extends PhylumApiSuccessResponse {
|
||||
scopes: (k['scopes'] as List).cast<String>(),
|
||||
);
|
||||
}).toList(growable: false);
|
||||
return ApiKeysResponse._(keys: keys);
|
||||
return ListApiKeysResponse._(keys: keys);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -10,7 +10,8 @@ import 'package:phylum/libphylum/parsers/parsers.dart';
|
||||
import 'package:phylum/libphylum/phylum_account.dart';
|
||||
import 'package:phylum/util/permissions.dart';
|
||||
|
||||
part 'api_keys_response.dart';
|
||||
part 'api_key_list_response.dart';
|
||||
part 'api_key_generate_response.dart';
|
||||
part 'bookmark_response.dart';
|
||||
part 'bootstrap_login_response.dart';
|
||||
part 'empty_response.dart';
|
||||
|
||||
@@ -23,7 +23,7 @@ Future showReponsiveDialog(BuildContext context, String title, WidgetBuilder bui
|
||||
return AlertDialog(
|
||||
title: Text(title),
|
||||
scrollable: true,
|
||||
content: SizedBox(width: 360, child: builder(context)),
|
||||
content: SizedBox(width: 480, child: builder(context)),
|
||||
actions: [
|
||||
ElevatedButton(onPressed: Navigator.of(context).pop, child: Text('OK')),
|
||||
],
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:phylum/libphylum/phylum_account.dart';
|
||||
import 'package:phylum/libphylum/requests/api_keys_request.dart';
|
||||
import 'package:phylum/libphylum/requests/api_key_generate_request.dart';
|
||||
import 'package:phylum/libphylum/requests/api_keys_list_request.dart';
|
||||
import 'package:phylum/libphylum/responses/responses.dart';
|
||||
import 'package:phylum/ui/profile/generate_api_key_dialog.dart';
|
||||
import 'package:phylum/util/dialogs.dart';
|
||||
import 'package:phylum/util/time.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
@@ -68,11 +70,11 @@ class _ApiKeysViewState extends State<ApiKeysView> {
|
||||
void fetchKeys(BuildContext context) async {
|
||||
final account = context.read<PhylumAccount>();
|
||||
final response = await account.apiClient.sendRequest(
|
||||
const ApiKeysRequest(),
|
||||
(request, response) => parseJsonListResponse(response, ApiKeysResponse.fromResponse),
|
||||
const ListApiKeysRequest(),
|
||||
(request, response) => parseJsonListResponse(response, ListApiKeysResponse.fromResponse),
|
||||
);
|
||||
|
||||
if (response is ApiKeysResponse) {
|
||||
if (response is ListApiKeysResponse) {
|
||||
setState(() => keys = response.keys);
|
||||
} else {
|
||||
setState(() => error = response.description);
|
||||
@@ -80,6 +82,68 @@ class _ApiKeysViewState extends State<ApiKeysView> {
|
||||
}
|
||||
|
||||
void createKey(BuildContext context) async {
|
||||
GenerateApiKeyDialog.show(context);
|
||||
final account = context.read<PhylumAccount>();
|
||||
final k = await GenerateApiKeyDialog.show(context);
|
||||
if (k == null) return;
|
||||
final response = await account.apiClient.sendRequest(
|
||||
GenerateApiKeyRequest(
|
||||
description: k.description,
|
||||
expires: k.expires,
|
||||
scopes: k.scopes,
|
||||
),
|
||||
(_, response) => parseJsonMapResponse(response, GenerateApiKeyResponse.fromResponse));
|
||||
|
||||
if (response is GenerateApiKeyResponse) {
|
||||
if (!context.mounted) return;
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: Text('API Key Generated'),
|
||||
content: SizedBox(
|
||||
width: 480,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 6.0),
|
||||
child: Text('Key ID and Key can be used as the username and password in HTTP Basic auth'),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 6.0),
|
||||
child:
|
||||
Text('Access Token can be used as a Bearer token, or with HTTP Basic auth by leaving the '
|
||||
'username empty'),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 6.0),
|
||||
child: Text(
|
||||
'Please note down the details as it will not be possible to retreive once it is dismissed.'),
|
||||
),
|
||||
TextFormField(
|
||||
decoration: InputDecoration(label: Text('Key ID')),
|
||||
initialValue: response.keyId,
|
||||
readOnly: true,
|
||||
),
|
||||
TextFormField(
|
||||
decoration: InputDecoration(label: Text('Key')),
|
||||
initialValue: response.key,
|
||||
readOnly: true,
|
||||
),
|
||||
TextFormField(
|
||||
decoration: InputDecoration(label: Text('Access Token')),
|
||||
initialValue: response.accessToken,
|
||||
readOnly: true,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
ElevatedButton(onPressed: () => Navigator.of(context).pop(), child: Text('OK')),
|
||||
],
|
||||
));
|
||||
} else {
|
||||
if (!context.mounted) return;
|
||||
showAlertDialog(context, title: 'Error Generating API Key', message: response.description);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ final _formatYearMonthDate = DateFormat.yMMMd();
|
||||
final _formatFullNoYear = DateFormat.MMMd().add_jm();
|
||||
final _formatFull = DateFormat.yMMMd().add_jm();
|
||||
final _formatNumericDateTime = DateFormat('yyyy-MM-ddTHH:mm:ss');
|
||||
// final _formatServer = DateFormat('yyyy-MM-ddTHH:mm:ss\'Z\'00:00');
|
||||
|
||||
DateTime get _today {
|
||||
final now = DateTime.now();
|
||||
@@ -34,4 +35,8 @@ extension Formats on DateTime {
|
||||
String formatNumericDateTime() {
|
||||
return _formatNumericDateTime.format(this);
|
||||
}
|
||||
|
||||
String formatServer() {
|
||||
return toUtc().toIso8601String();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user