mirror of
https://github.com/foss42/apidash.git
synced 2025-12-02 10:49:49 +08:00
feat: add environment variable substitution support for auth models
This commit is contained in:
@@ -317,7 +317,7 @@ class CollectionStateNotifier
|
|||||||
var responseRec = await sendHttpRequest(
|
var responseRec = await sendHttpRequest(
|
||||||
requestId,
|
requestId,
|
||||||
apiType,
|
apiType,
|
||||||
requestModel.httpRequestModel?.authModel,
|
substitutedHttpRequestModel.authModel,
|
||||||
substitutedHttpRequestModel,
|
substitutedHttpRequestModel,
|
||||||
defaultUriScheme: defaultUriScheme,
|
defaultUriScheme: defaultUriScheme,
|
||||||
noSSL: noSSL,
|
noSSL: noSSL,
|
||||||
|
|||||||
@@ -20,17 +20,16 @@ class ApiKeyAuthFields extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _ApiKeyAuthFieldsState extends State<ApiKeyAuthFields> {
|
class _ApiKeyAuthFieldsState extends State<ApiKeyAuthFields> {
|
||||||
late TextEditingController _keyController;
|
late String _key;
|
||||||
late TextEditingController _nameController;
|
late String _name;
|
||||||
late String _addKeyTo;
|
late String _addKeyTo;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
final apiAuth = widget.authData?.apikey;
|
final apiAuth = widget.authData?.apikey;
|
||||||
_keyController = TextEditingController(text: apiAuth?.key ?? '');
|
_key = apiAuth?.key ?? '';
|
||||||
_nameController =
|
_name = apiAuth?.name ?? kApiKeyHeaderName;
|
||||||
TextEditingController(text: apiAuth?.name ?? kApiKeyHeaderName);
|
|
||||||
_addKeyTo = apiAuth?.location ?? kAddToDefaultLocation;
|
_addKeyTo = apiAuth?.location ?? kAddToDefaultLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,20 +65,26 @@ class _ApiKeyAuthFieldsState extends State<ApiKeyAuthFields> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
AuthTextField(
|
EnvAuthField(
|
||||||
readOnly: widget.readOnly,
|
readOnly: widget.readOnly,
|
||||||
controller: _nameController,
|
|
||||||
hintText: kHintTextFieldName,
|
hintText: kHintTextFieldName,
|
||||||
onChanged: (value) => _updateApiKeyAuth(),
|
initialValue: widget.authData?.apikey?.name,
|
||||||
|
onChanged: (value) {
|
||||||
|
_name = value;
|
||||||
|
_updateApiKeyAuth();
|
||||||
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
AuthTextField(
|
EnvAuthField(
|
||||||
readOnly: widget.readOnly,
|
readOnly: widget.readOnly,
|
||||||
controller: _keyController,
|
|
||||||
title: kLabelApiKey,
|
title: kLabelApiKey,
|
||||||
hintText: kHintTextKey,
|
hintText: kHintTextKey,
|
||||||
isObscureText: true,
|
isObscureText: true,
|
||||||
onChanged: (value) => _updateApiKeyAuth(),
|
initialValue: widget.authData?.apikey?.key,
|
||||||
|
onChanged: (value) {
|
||||||
|
_key = value;
|
||||||
|
_updateApiKeyAuth();
|
||||||
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -87,8 +92,8 @@ class _ApiKeyAuthFieldsState extends State<ApiKeyAuthFields> {
|
|||||||
|
|
||||||
void _updateApiKeyAuth() {
|
void _updateApiKeyAuth() {
|
||||||
final apiKey = AuthApiKeyModel(
|
final apiKey = AuthApiKeyModel(
|
||||||
key: _keyController.text.trim(),
|
key: _key.trim(),
|
||||||
name: _nameController.text.trim(),
|
name: _name.trim(),
|
||||||
location: _addKeyTo,
|
location: _addKeyTo,
|
||||||
);
|
);
|
||||||
widget.updateAuth?.call(widget.authData?.copyWith(
|
widget.updateAuth?.call(widget.authData?.copyWith(
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import 'package:apidash_core/apidash_core.dart';
|
|||||||
import 'package:apidash/widgets/widgets.dart';
|
import 'package:apidash/widgets/widgets.dart';
|
||||||
import 'consts.dart';
|
import 'consts.dart';
|
||||||
|
|
||||||
class BasicAuthFields extends StatelessWidget {
|
class BasicAuthFields extends StatefulWidget {
|
||||||
final AuthModel? authData;
|
final AuthModel? authData;
|
||||||
final Function(AuthModel?)? updateAuth;
|
final Function(AuthModel?)? updateAuth;
|
||||||
final bool readOnly;
|
final bool readOnly;
|
||||||
@@ -16,50 +16,55 @@ class BasicAuthFields extends StatelessWidget {
|
|||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
State<BasicAuthFields> createState() => _BasicAuthFieldsState();
|
||||||
final usernameController = TextEditingController(
|
}
|
||||||
text: authData?.basic?.username ?? '',
|
|
||||||
);
|
|
||||||
final passwordController = TextEditingController(
|
|
||||||
text: authData?.basic?.password ?? '',
|
|
||||||
);
|
|
||||||
|
|
||||||
|
class _BasicAuthFieldsState extends State<BasicAuthFields> {
|
||||||
|
late String _username;
|
||||||
|
late String _password;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_username = widget.authData?.basic?.username ?? '';
|
||||||
|
_password = widget.authData?.basic?.password ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
AuthTextField(
|
EnvAuthField(
|
||||||
readOnly: readOnly,
|
readOnly: widget.readOnly,
|
||||||
hintText: kHintUsername,
|
hintText: kHintUsername,
|
||||||
controller: usernameController,
|
initialValue: widget.authData?.basic?.username,
|
||||||
onChanged: (_) => _updateBasicAuth(
|
onChanged: (value) {
|
||||||
usernameController,
|
_username = value;
|
||||||
passwordController,
|
_updateBasicAuth();
|
||||||
),
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
AuthTextField(
|
EnvAuthField(
|
||||||
readOnly: readOnly,
|
readOnly: widget.readOnly,
|
||||||
hintText: kHintPassword,
|
hintText: kHintPassword,
|
||||||
isObscureText: true,
|
isObscureText: true,
|
||||||
controller: passwordController,
|
initialValue: widget.authData?.basic?.password,
|
||||||
onChanged: (_) => _updateBasicAuth(
|
onChanged: (value) {
|
||||||
usernameController,
|
_password = value;
|
||||||
passwordController,
|
_updateBasicAuth();
|
||||||
),
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _updateBasicAuth(
|
void _updateBasicAuth() {
|
||||||
TextEditingController usernameController,
|
|
||||||
TextEditingController passwordController,
|
|
||||||
) {
|
|
||||||
final basicAuth = AuthBasicAuthModel(
|
final basicAuth = AuthBasicAuthModel(
|
||||||
username: usernameController.text.trim(),
|
username: _username.trim(),
|
||||||
password: passwordController.text.trim(),
|
password: _password.trim(),
|
||||||
);
|
);
|
||||||
updateAuth?.call(authData?.copyWith(
|
widget.updateAuth?.call(widget.authData?.copyWith(
|
||||||
type: APIAuthType.basic,
|
type: APIAuthType.basic,
|
||||||
basic: basicAuth,
|
basic: basicAuth,
|
||||||
) ??
|
) ??
|
||||||
|
|||||||
@@ -20,29 +20,31 @@ class BearerAuthFields extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _BearerAuthFieldsState extends State<BearerAuthFields> {
|
class _BearerAuthFieldsState extends State<BearerAuthFields> {
|
||||||
late TextEditingController _tokenController;
|
late String _token;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
final bearerAuth = widget.authData?.bearer;
|
_token = widget.authData?.bearer?.token ?? '';
|
||||||
_tokenController = TextEditingController(text: bearerAuth?.token ?? '');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return AuthTextField(
|
return EnvAuthField(
|
||||||
readOnly: widget.readOnly,
|
readOnly: widget.readOnly,
|
||||||
controller: _tokenController,
|
|
||||||
hintText: kHintToken,
|
hintText: kHintToken,
|
||||||
isObscureText: true,
|
isObscureText: true,
|
||||||
onChanged: (value) => _updateBearerAuth(),
|
initialValue: widget.authData?.bearer?.token,
|
||||||
|
onChanged: (value) {
|
||||||
|
_token = value;
|
||||||
|
_updateBearerAuth();
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _updateBearerAuth() {
|
void _updateBearerAuth() {
|
||||||
final bearer = AuthBearerModel(
|
final bearer = AuthBearerModel(
|
||||||
token: _tokenController.text.trim(),
|
token: _token.trim(),
|
||||||
);
|
);
|
||||||
widget.updateAuth?.call(widget.authData?.copyWith(
|
widget.updateAuth?.call(widget.authData?.copyWith(
|
||||||
type: APIAuthType.bearer,
|
type: APIAuthType.bearer,
|
||||||
|
|||||||
@@ -21,25 +21,25 @@ class DigestAuthFields extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _DigestAuthFieldsState extends State<DigestAuthFields> {
|
class _DigestAuthFieldsState extends State<DigestAuthFields> {
|
||||||
late TextEditingController _usernameController;
|
late String _username;
|
||||||
late TextEditingController _passwordController;
|
late String _password;
|
||||||
late TextEditingController _realmController;
|
late String _realm;
|
||||||
late TextEditingController _nonceController;
|
late String _nonce;
|
||||||
late String _algorithmController;
|
late String _algorithmController;
|
||||||
late TextEditingController _qopController;
|
late String _qop;
|
||||||
late TextEditingController _opaqueController;
|
late String _opaque;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
final digest = widget.authData?.digest;
|
final digest = widget.authData?.digest;
|
||||||
_usernameController = TextEditingController(text: digest?.username ?? '');
|
_username = digest?.username ?? '';
|
||||||
_passwordController = TextEditingController(text: digest?.password ?? '');
|
_password = digest?.password ?? '';
|
||||||
_realmController = TextEditingController(text: digest?.realm ?? '');
|
_realm = digest?.realm ?? '';
|
||||||
_nonceController = TextEditingController(text: digest?.nonce ?? '');
|
_nonce = digest?.nonce ?? '';
|
||||||
_algorithmController = digest?.algorithm ?? kDigestAlgos[0];
|
_algorithmController = digest?.algorithm ?? kDigestAlgos[0];
|
||||||
_qopController = TextEditingController(text: digest?.qop ?? kQop[0]);
|
_qop = digest?.qop ?? kQop[0];
|
||||||
_opaqueController = TextEditingController(text: digest?.opaque ?? '');
|
_opaque = digest?.opaque ?? '';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -48,37 +48,49 @@ class _DigestAuthFieldsState extends State<DigestAuthFields> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
AuthTextField(
|
EnvAuthField(
|
||||||
readOnly: widget.readOnly,
|
readOnly: widget.readOnly,
|
||||||
controller: _usernameController,
|
|
||||||
hintText: kHintUsername,
|
hintText: kHintUsername,
|
||||||
infoText: kInfoDigestUsername,
|
infoText: kInfoDigestUsername,
|
||||||
onChanged: (_) => _updateDigestAuth(),
|
initialValue: widget.authData?.digest?.username,
|
||||||
|
onChanged: (value) {
|
||||||
|
_username = value;
|
||||||
|
_updateDigestAuth();
|
||||||
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
AuthTextField(
|
EnvAuthField(
|
||||||
readOnly: widget.readOnly,
|
readOnly: widget.readOnly,
|
||||||
controller: _passwordController,
|
|
||||||
hintText: kHintPassword,
|
hintText: kHintPassword,
|
||||||
isObscureText: true,
|
isObscureText: true,
|
||||||
infoText: kInfoDigestPassword,
|
infoText: kInfoDigestPassword,
|
||||||
onChanged: (_) => _updateDigestAuth(),
|
initialValue: widget.authData?.digest?.password,
|
||||||
|
onChanged: (value) {
|
||||||
|
_password = value;
|
||||||
|
_updateDigestAuth();
|
||||||
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
AuthTextField(
|
EnvAuthField(
|
||||||
readOnly: widget.readOnly,
|
readOnly: widget.readOnly,
|
||||||
controller: _realmController,
|
|
||||||
hintText: kHintRealm,
|
hintText: kHintRealm,
|
||||||
infoText: kInfoDigestRealm,
|
infoText: kInfoDigestRealm,
|
||||||
onChanged: (_) => _updateDigestAuth(),
|
initialValue: widget.authData?.digest?.realm,
|
||||||
|
onChanged: (value) {
|
||||||
|
_realm = value;
|
||||||
|
_updateDigestAuth();
|
||||||
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
AuthTextField(
|
EnvAuthField(
|
||||||
readOnly: widget.readOnly,
|
readOnly: widget.readOnly,
|
||||||
controller: _nonceController,
|
|
||||||
hintText: kHintNonce,
|
hintText: kHintNonce,
|
||||||
infoText: kInfoDigestNonce,
|
infoText: kInfoDigestNonce,
|
||||||
onChanged: (_) => _updateDigestAuth(),
|
initialValue: widget.authData?.digest?.nonce,
|
||||||
|
onChanged: (value) {
|
||||||
|
_nonce = value;
|
||||||
|
_updateDigestAuth();
|
||||||
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
Text(
|
Text(
|
||||||
@@ -106,20 +118,26 @@ class _DigestAuthFieldsState extends State<DigestAuthFields> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
AuthTextField(
|
EnvAuthField(
|
||||||
readOnly: widget.readOnly,
|
readOnly: widget.readOnly,
|
||||||
controller: _qopController,
|
|
||||||
hintText: kHintQop,
|
hintText: kHintQop,
|
||||||
infoText: kInfoDigestQop,
|
infoText: kInfoDigestQop,
|
||||||
onChanged: (_) => _updateDigestAuth(),
|
initialValue: widget.authData?.digest?.qop,
|
||||||
|
onChanged: (value) {
|
||||||
|
_qop = value;
|
||||||
|
_updateDigestAuth();
|
||||||
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
AuthTextField(
|
EnvAuthField(
|
||||||
readOnly: widget.readOnly,
|
readOnly: widget.readOnly,
|
||||||
controller: _opaqueController,
|
|
||||||
hintText: kHintDataString,
|
hintText: kHintDataString,
|
||||||
infoText: kInfoDigestDataString,
|
infoText: kInfoDigestDataString,
|
||||||
onChanged: (_) => _updateDigestAuth(),
|
initialValue: widget.authData?.digest?.opaque,
|
||||||
|
onChanged: (value) {
|
||||||
|
_opaque = value;
|
||||||
|
_updateDigestAuth();
|
||||||
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -128,13 +146,13 @@ class _DigestAuthFieldsState extends State<DigestAuthFields> {
|
|||||||
|
|
||||||
void _updateDigestAuth() {
|
void _updateDigestAuth() {
|
||||||
final digest = AuthDigestModel(
|
final digest = AuthDigestModel(
|
||||||
username: _usernameController.text.trim(),
|
username: _username.trim(),
|
||||||
password: _passwordController.text.trim(),
|
password: _password.trim(),
|
||||||
realm: _realmController.text.trim(),
|
realm: _realm.trim(),
|
||||||
nonce: _nonceController.text.trim(),
|
nonce: _nonce.trim(),
|
||||||
algorithm: _algorithmController.trim(),
|
algorithm: _algorithmController.trim(),
|
||||||
qop: _qopController.text.trim(),
|
qop: _qop.trim(),
|
||||||
opaque: _opaqueController.text.trim(),
|
opaque: _opaque.trim(),
|
||||||
);
|
);
|
||||||
widget.updateAuth?.call(widget.authData?.copyWith(
|
widget.updateAuth?.call(widget.authData?.copyWith(
|
||||||
type: APIAuthType.digest,
|
type: APIAuthType.digest,
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ class JwtAuthFields extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _JwtAuthFieldsState extends State<JwtAuthFields> {
|
class _JwtAuthFieldsState extends State<JwtAuthFields> {
|
||||||
late TextEditingController _secretController;
|
late String _secret;
|
||||||
late TextEditingController _privateKeyController;
|
late TextEditingController _privateKeyController;
|
||||||
late TextEditingController _payloadController;
|
late TextEditingController _payloadController;
|
||||||
late String _addTokenTo;
|
late String _addTokenTo;
|
||||||
@@ -32,7 +32,7 @@ class _JwtAuthFieldsState extends State<JwtAuthFields> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
final jwt = widget.authData?.jwt;
|
final jwt = widget.authData?.jwt;
|
||||||
_secretController = TextEditingController(text: jwt?.secret ?? '');
|
_secret = jwt?.secret ?? '';
|
||||||
_privateKeyController = TextEditingController(text: jwt?.privateKey ?? '');
|
_privateKeyController = TextEditingController(text: jwt?.privateKey ?? '');
|
||||||
_payloadController = TextEditingController(text: jwt?.payload ?? '');
|
_payloadController = TextEditingController(text: jwt?.payload ?? '');
|
||||||
_addTokenTo = jwt?.addTokenTo ?? kAddToDefaultLocation;
|
_addTokenTo = jwt?.addTokenTo ?? kAddToDefaultLocation;
|
||||||
@@ -96,13 +96,16 @@ class _JwtAuthFieldsState extends State<JwtAuthFields> {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
if (_algorithm.startsWith(kStartAlgo)) ...[
|
if (_algorithm.startsWith(kStartAlgo)) ...[
|
||||||
AuthTextField(
|
EnvAuthField(
|
||||||
readOnly: widget.readOnly,
|
readOnly: widget.readOnly,
|
||||||
controller: _secretController,
|
|
||||||
isObscureText: true,
|
isObscureText: true,
|
||||||
hintText: kHintSecret,
|
hintText: kHintSecret,
|
||||||
infoText: kInfoSecret,
|
infoText: kInfoSecret,
|
||||||
onChanged: (value) => _updateJwtAuth(),
|
initialValue: widget.authData?.jwt?.secret,
|
||||||
|
onChanged: (value) {
|
||||||
|
_secret = value;
|
||||||
|
_updateJwtAuth();
|
||||||
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
CheckboxListTile(
|
CheckboxListTile(
|
||||||
@@ -207,7 +210,7 @@ class _JwtAuthFieldsState extends State<JwtAuthFields> {
|
|||||||
|
|
||||||
void _updateJwtAuth() {
|
void _updateJwtAuth() {
|
||||||
final jwt = AuthJwtModel(
|
final jwt = AuthJwtModel(
|
||||||
secret: _secretController.text.trim(),
|
secret: _secret.trim(),
|
||||||
privateKey: _privateKeyController.text.trim(),
|
privateKey: _privateKeyController.text.trim(),
|
||||||
payload: _payloadController.text.trim(),
|
payload: _payloadController.text.trim(),
|
||||||
addTokenTo: _addTokenTo,
|
addTokenTo: _addTokenTo,
|
||||||
|
|||||||
@@ -91,10 +91,91 @@ HttpRequestModel substituteHttpRequestModel(
|
|||||||
);
|
);
|
||||||
}).toList(),
|
}).toList(),
|
||||||
body: substituteVariables(httpRequestModel.body, combinedEnvVarMap),
|
body: substituteVariables(httpRequestModel.body, combinedEnvVarMap),
|
||||||
|
authModel:
|
||||||
|
_substituteAuthModel(httpRequestModel.authModel, combinedEnvVarMap),
|
||||||
);
|
);
|
||||||
return newRequestModel;
|
return newRequestModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AuthModel? _substituteAuthModel(
|
||||||
|
AuthModel? authModel, Map<String, String> envVarMap) {
|
||||||
|
if (authModel == null) return null;
|
||||||
|
|
||||||
|
switch (authModel.type) {
|
||||||
|
case APIAuthType.basic:
|
||||||
|
if (authModel.basic != null) {
|
||||||
|
final basic = authModel.basic!;
|
||||||
|
return authModel.copyWith(
|
||||||
|
basic: basic.copyWith(
|
||||||
|
username: substituteVariables(basic.username, envVarMap) ??
|
||||||
|
basic.username,
|
||||||
|
password: substituteVariables(basic.password, envVarMap) ??
|
||||||
|
basic.password,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case APIAuthType.bearer:
|
||||||
|
if (authModel.bearer != null) {
|
||||||
|
final bearer = authModel.bearer!;
|
||||||
|
return authModel.copyWith(
|
||||||
|
bearer: bearer.copyWith(
|
||||||
|
token: substituteVariables(bearer.token, envVarMap) ?? bearer.token,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case APIAuthType.apiKey:
|
||||||
|
if (authModel.apikey != null) {
|
||||||
|
final apiKey = authModel.apikey!;
|
||||||
|
return authModel.copyWith(
|
||||||
|
apikey: apiKey.copyWith(
|
||||||
|
key: substituteVariables(apiKey.key, envVarMap) ?? apiKey.key,
|
||||||
|
name: substituteVariables(apiKey.name, envVarMap) ?? apiKey.name,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case APIAuthType.jwt:
|
||||||
|
if (authModel.jwt != null) {
|
||||||
|
final jwt = authModel.jwt!;
|
||||||
|
return authModel.copyWith(
|
||||||
|
jwt: jwt.copyWith(
|
||||||
|
secret: substituteVariables(jwt.secret, envVarMap) ?? jwt.secret,
|
||||||
|
privateKey: substituteVariables(jwt.privateKey, envVarMap) ??
|
||||||
|
jwt.privateKey,
|
||||||
|
payload: substituteVariables(jwt.payload, envVarMap) ?? jwt.payload,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case APIAuthType.digest:
|
||||||
|
if (authModel.digest != null) {
|
||||||
|
final digest = authModel.digest!;
|
||||||
|
return authModel.copyWith(
|
||||||
|
digest: digest.copyWith(
|
||||||
|
username: substituteVariables(digest.username, envVarMap) ??
|
||||||
|
digest.username,
|
||||||
|
password: substituteVariables(digest.password, envVarMap) ??
|
||||||
|
digest.password,
|
||||||
|
realm: substituteVariables(digest.realm, envVarMap) ?? digest.realm,
|
||||||
|
nonce: substituteVariables(digest.nonce, envVarMap) ?? digest.nonce,
|
||||||
|
qop: substituteVariables(digest.qop, envVarMap) ?? digest.qop,
|
||||||
|
opaque:
|
||||||
|
substituteVariables(digest.opaque, envVarMap) ?? digest.opaque,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case APIAuthType.oauth1:
|
||||||
|
case APIAuthType.oauth2:
|
||||||
|
case APIAuthType.none:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return authModel;
|
||||||
|
}
|
||||||
|
|
||||||
List<EnvironmentVariableSuggestion>? getEnvironmentTriggerSuggestions(
|
List<EnvironmentVariableSuggestion>? getEnvironmentTriggerSuggestions(
|
||||||
String query,
|
String query,
|
||||||
Map<String, List<EnvironmentVariableModel>> envMap,
|
Map<String, List<EnvironmentVariableModel>> envMap,
|
||||||
|
|||||||
@@ -1,44 +1,33 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
import 'package:apidash/consts.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:apidash_design_system/apidash_design_system.dart';
|
import 'package:apidash_design_system/apidash_design_system.dart';
|
||||||
|
import '../screens/common_widgets/env_trigger_field.dart';
|
||||||
|
|
||||||
class AuthTextField extends StatefulWidget {
|
class EnvAuthField extends StatefulWidget {
|
||||||
final String hintText;
|
final String hintText;
|
||||||
final String? title;
|
final String? title;
|
||||||
final TextEditingController controller;
|
|
||||||
final bool isObscureText;
|
final bool isObscureText;
|
||||||
final Function(String)? onChanged;
|
final Function(String)? onChanged;
|
||||||
final bool readOnly;
|
final bool readOnly;
|
||||||
final String? infoText;
|
final String? infoText;
|
||||||
|
final String? initialValue;
|
||||||
|
|
||||||
const AuthTextField(
|
const EnvAuthField(
|
||||||
{super.key,
|
{super.key,
|
||||||
this.title,
|
this.title,
|
||||||
required this.hintText,
|
required this.hintText,
|
||||||
required this.controller,
|
|
||||||
required this.onChanged,
|
required this.onChanged,
|
||||||
this.readOnly = false,
|
this.readOnly = false,
|
||||||
this.isObscureText = false,
|
this.isObscureText = false,
|
||||||
this.infoText});
|
this.infoText,
|
||||||
|
this.initialValue});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<AuthTextField> createState() => _AuthFieldState();
|
State<EnvAuthField> createState() => _AuthFieldState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _AuthFieldState extends State<AuthTextField> {
|
class _AuthFieldState extends State<EnvAuthField> {
|
||||||
late bool _obscureText;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_obscureText = widget.isObscureText;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _toggleVisibility() {
|
|
||||||
setState(() {
|
|
||||||
_obscureText = !_obscureText;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return AutofillGroup(
|
return AutofillGroup(
|
||||||
@@ -67,49 +56,20 @@ class _AuthFieldState extends State<AuthTextField> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 6),
|
const SizedBox(height: 6),
|
||||||
TextFormField(
|
EnvironmentTriggerField(
|
||||||
readOnly: widget.readOnly,
|
keyId: "auth-${widget.title ?? widget.hintText}-${Random.secure()}",
|
||||||
controller: widget.controller,
|
onChanged: widget.readOnly ? null : widget.onChanged,
|
||||||
|
initialValue: widget.initialValue,
|
||||||
style: kCodeStyle.copyWith(
|
style: kCodeStyle.copyWith(
|
||||||
color: Theme.of(context).colorScheme.onSurface,
|
color: Theme.of(context).colorScheme.onSurface,
|
||||||
fontSize: Theme.of(context).textTheme.bodyMedium?.fontSize,
|
fontSize: Theme.of(context).textTheme.bodyMedium?.fontSize,
|
||||||
),
|
),
|
||||||
decoration: InputDecoration(
|
decoration: getTextFieldInputDecoration(
|
||||||
filled: true,
|
Theme.of(context).colorScheme,
|
||||||
fillColor: Theme.of(context).colorScheme.surfaceContainerLowest,
|
|
||||||
constraints: BoxConstraints(
|
|
||||||
maxWidth: MediaQuery.sizeOf(context).width - 80),
|
|
||||||
contentPadding: kP10,
|
|
||||||
hintText: widget.hintText,
|
hintText: widget.hintText,
|
||||||
hintStyle: Theme.of(context).textTheme.bodySmall,
|
isDense: true,
|
||||||
suffixIcon: widget.isObscureText
|
contentPadding: kIsMobile ? kPh6b12 : null,
|
||||||
? IconButton(
|
|
||||||
icon: Icon(
|
|
||||||
_obscureText ? Icons.visibility_off : Icons.visibility,
|
|
||||||
size: 20,
|
|
||||||
),
|
|
||||||
onPressed: _toggleVisibility,
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
focusedBorder: OutlineInputBorder(
|
|
||||||
borderSide: BorderSide(
|
|
||||||
color: Theme.of(context).colorScheme.outline,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
enabledBorder: OutlineInputBorder(
|
|
||||||
borderSide: BorderSide(
|
|
||||||
color: Theme.of(context).colorScheme.surfaceContainerHighest,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
validator: (value) {
|
|
||||||
if (value!.isEmpty) {
|
|
||||||
return "${widget.hintText} cannot be empty!";
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
obscureText: _obscureText,
|
|
||||||
onChanged: widget.onChanged,
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -357,4 +357,98 @@ void main() {
|
|||||||
expect(getVariableStatus(query, envMap, activeEnvironmentId), expected);
|
expect(getVariableStatus(query, envMap, activeEnvironmentId), expected);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
group("Testing auth model environment variable substitution", () {
|
||||||
|
test("Testing basic auth with environment variables", () {
|
||||||
|
const httpRequestModel = HttpRequestModel(
|
||||||
|
url: "{{url}}/test",
|
||||||
|
authModel: AuthModel(
|
||||||
|
type: APIAuthType.basic,
|
||||||
|
basic: AuthBasicAuthModel(
|
||||||
|
username: "{{basic_username}}admin",
|
||||||
|
password: "{{token}}pass",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String?, List<EnvironmentVariableModel>> envMap = {
|
||||||
|
kGlobalEnvironmentId: [
|
||||||
|
EnvironmentVariableModel(key: "url", value: "api.apidash.dev"),
|
||||||
|
EnvironmentVariableModel(key: "basic_username", value: "testuser"),
|
||||||
|
EnvironmentVariableModel(key: "token", value: "secret"),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const activeEnvironmentId = null;
|
||||||
|
final result = substituteHttpRequestModel(
|
||||||
|
httpRequestModel,
|
||||||
|
envMap,
|
||||||
|
activeEnvironmentId,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.authModel?.basic?.username, "testuseradmin");
|
||||||
|
expect(result.authModel?.basic?.password, "secretpass");
|
||||||
|
expect(result.url, "api.apidash.dev/test");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Testing bearer auth with environment variables", () {
|
||||||
|
const httpRequestModel = HttpRequestModel(
|
||||||
|
url: "{{url}}/test",
|
||||||
|
authModel: AuthModel(
|
||||||
|
type: APIAuthType.bearer,
|
||||||
|
bearer: AuthBearerModel(
|
||||||
|
token: "{{bearer_token}}",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String?, List<EnvironmentVariableModel>> envMap = {
|
||||||
|
kGlobalEnvironmentId: [
|
||||||
|
EnvironmentVariableModel(key: "url", value: "api.apidash.dev"),
|
||||||
|
EnvironmentVariableModel(key: "bearer_token", value: "secret123"),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const activeEnvironmentId = null;
|
||||||
|
final result = substituteHttpRequestModel(
|
||||||
|
httpRequestModel,
|
||||||
|
envMap,
|
||||||
|
activeEnvironmentId,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.authModel?.bearer?.token, "secret123");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Testing API key auth with environment variables", () {
|
||||||
|
const httpRequestModel = HttpRequestModel(
|
||||||
|
url: "{{url}}/test",
|
||||||
|
authModel: AuthModel(
|
||||||
|
type: APIAuthType.apiKey,
|
||||||
|
apikey: AuthApiKeyModel(
|
||||||
|
key: "{{api_key}}",
|
||||||
|
name: "{{header_name}}",
|
||||||
|
location: "header",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String?, List<EnvironmentVariableModel>> envMap = {
|
||||||
|
kGlobalEnvironmentId: [
|
||||||
|
EnvironmentVariableModel(key: "url", value: "api.apidash.dev"),
|
||||||
|
EnvironmentVariableModel(key: "api_key", value: "key123"),
|
||||||
|
EnvironmentVariableModel(key: "header_name", value: "X-API-Key"),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const activeEnvironmentId = null;
|
||||||
|
final result = substituteHttpRequestModel(
|
||||||
|
httpRequestModel,
|
||||||
|
envMap,
|
||||||
|
activeEnvironmentId,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.authModel?.apikey?.key, "key123");
|
||||||
|
expect(result.authModel?.apikey?.name, "X-API-Key");
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user