Skip to content
This repository has been archived by the owner on Mar 17, 2024. It is now read-only.

Commit

Permalink
Added X-API-Key support
Browse files Browse the repository at this point in the history
  • Loading branch information
zbejas committed Sep 9, 2022
1 parent 86201e9 commit fdb6a9e
Show file tree
Hide file tree
Showing 6 changed files with 190 additions and 36 deletions.
4 changes: 2 additions & 2 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ class MyApp extends StatelessWidget {
settings.init(storage);
});

const Map<String, Widget> _routes = {
const Map<String, Widget> routes = {
'/users': UserManagerPage(),
'/settings': SettingsPage(),
'/home/container': ContainerDetailsPage(),
Expand All @@ -90,7 +90,7 @@ class MyApp extends StatelessWidget {
//routes: _routes,
onGenerateRoute: (settings) {
String pageName = settings.name ?? '/';
Widget routeWidget = _routes[pageName] ?? const Wrapper();
Widget routeWidget = routes[pageName] ?? const Wrapper();

if (pageName == '/home/container') {
return PageRouteBuilder(
Expand Down
15 changes: 13 additions & 2 deletions lib/models/portainer/token.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,15 @@ String tokenToJson(Token data) => json.encode(data.toJson());
class Token {
Token({
required this.jwt,
this.manuallySet = false,
});

@HiveField(0)
String jwt;

@HiveField(1)
bool manuallySet;

factory Token.fromJson(Map<String, dynamic> json) => Token(
jwt: json["jwt"],
);
Expand All @@ -28,8 +32,15 @@ class Token {
"jwt": jwt,
};

String getBearerToken() {
return 'Bearer $jwt';
Map<String, String> getHeaders() {
if (manuallySet) {
return {
'X-API-Key': jwt,
};
}
return {
'Authorization': 'Bearer $jwt',
};
}

bool get hasJwt => jwt.isNotEmpty;
Expand Down
11 changes: 11 additions & 0 deletions lib/models/portainer/user.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class User extends ChangeNotifier {
late Token? token;

/// is token manually set
@HiveField(4)
bool tokenManuallySet = false;

User(
Expand Down Expand Up @@ -95,7 +96,17 @@ class User extends ChangeNotifier {
notifyListeners();
}

void manuallySetToken(Token newToken) {
token = newToken;
tokenManuallySet = true;
notifyListeners();
}

void resetToken() {
if (tokenManuallySet) {
return;
}

token = null;
notifyListeners();
}
Expand Down
14 changes: 8 additions & 6 deletions lib/pages/auth/authpage.dart
Original file line number Diff line number Diff line change
Expand Up @@ -229,13 +229,15 @@ class _AuthPageState extends State<AuthPage> {

User? userData = box.get('user');

setState(() {
_hostUrlController.text = userData?.hostUrl ?? '';
_usernameController.text = userData?.username ?? '';
_passwordController.text = userData?.password ?? '';
if (mounted) {
setState(() {
_hostUrlController.text = userData?.hostUrl ?? '';
_usernameController.text = userData?.username ?? '';
_passwordController.text = userData?.password ?? '';

_isLoading = false;
});
_isLoading = false;
});
}
}

_authenticateButton(User user, StorageManager storage) async {
Expand Down
132 changes: 131 additions & 1 deletion lib/pages/users/user_managment.dart
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ class _UserManagerPageState extends State<UserManagerPage> {
floatingActionButton: FloatingActionButton(
tooltip: 'Add User',
onPressed: () async {
await _showAddUserDialog(storage);
await _showAddMethodDialog(storage);
},
child: const Icon(Icons.add),
),
Expand Down Expand Up @@ -160,6 +160,58 @@ class _UserManagerPageState extends State<UserManagerPage> {
}
}

_validateToken(String token, String hostUrl, StorageManager storage) async {
Token? tokenObj = Token(jwt: token, manuallySet: true);
User user = User(
username: 'API Token',
password: 'null',
hostUrl: hostUrl,
token: tokenObj,
);

if (!(await RemoteService().isTokenValid(user))) {
ToastContext().init(context);
Toast.show(
'Invalid token.',
);
return;
}

await storage.addUserToList(user);
if (mounted) {
Navigator.pop(context);
}
}

_showAddMethodDialog(StorageManager storage) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Select Method'),
content: const Text(
'Select the method you want to use to add a user.',
),
actions: [
TextButton(
child: const Text('Authentication'),
onPressed: () async {
Navigator.pop(context);
await _showAddUserDialog(storage);
},
),
TextButton(
child: const Text('Token'),
onPressed: () async {
Navigator.pop(context);
await _showAddViaTokenDialog(storage);
},
),
]);
},
);
}

_showUserDialog(User loggedUser, User clickedUser, StorageManager storage,
bool fromAuthPage) async {
showDialog(
Expand Down Expand Up @@ -335,4 +387,82 @@ class _UserManagerPageState extends State<UserManagerPage> {
},
);
}

_showAddViaTokenDialog(StorageManager storage) {
TextEditingController tokenController = TextEditingController();
TextEditingController hostUrlController = TextEditingController();

Size size = MediaQuery.of(context).size;

showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Add User'),
content: Form(
child: Wrap(
children: [
TextFormField(
controller: hostUrlController,
scrollPadding: EdgeInsets.all(size.height * .15),
textInputAction: TextInputAction.next,
keyboardType: TextInputType.url,
decoration: const InputDecoration(
labelText: 'Host URL',
hintText: 'https://portainer.example.com',
border: OutlineInputBorder(),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter a host URL';
}
return null;
},
),
SizedBox(
height: 10,
width: size.width,
),
TextFormField(
controller: tokenController,
scrollPadding: EdgeInsets.all(size.height * .1),
textInputAction: TextInputAction.next,
decoration: const InputDecoration(
labelText: 'Token',
hintText: 'abc123',
border: OutlineInputBorder(),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter the token';
}
return null;
},
),
SizedBox(
height: 10,
width: size.width,
),
],
),
),
actions: <Widget>[
TextButton(
child: const Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
},
),
TextButton(
child: const Text('Add'),
onPressed: () {
_validateToken(
tokenController.text, hostUrlController.text, storage);
},
),
],
);
},
);
}
}
50 changes: 25 additions & 25 deletions lib/services/remote.dart
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,7 @@ class RemoteService {

http.Response response = await client.post(
uri,
headers: {
"Authorization": user.token?.getBearerToken() ?? '',
},
headers: user.token?.getHeaders() ?? {},
);

if (response.statusCode == 204) {
Expand All @@ -89,7 +87,7 @@ class RemoteService {
Uri uri = Uri.parse("${user.hostUrl}/api/motd");
http.Response response = await client.get(
uri,
headers: {"Authorization": user.token?.getBearerToken() ?? ''},
headers: user.token?.getHeaders() ?? {},
);

if (response.statusCode == 200) {
Expand All @@ -110,9 +108,10 @@ class RemoteService {
"${user.hostUrl}/api/endpoints/${endpoint.id}/docker/containers/json?all=true",
);

http.Response response = await client.get(uri, headers: {
"Authorization": user.token?.getBearerToken() ?? '',
});
http.Response response = await client.get(
uri,
headers: user.token?.getHeaders() ?? {},
);

if (response.statusCode == 200) {
return dockerContainerFromJson(response.body);
Expand All @@ -133,9 +132,10 @@ class RemoteService {
Uri uri = Uri.parse(
"${user.hostUrl}/api/endpoints/${endpoint.id}/docker/containers/$containerId/json");

http.Response response = await client.get(uri, headers: {
"Authorization": user.token?.getBearerToken() ?? '',
});
http.Response response = await client.get(
uri,
headers: user.token?.getHeaders() ?? {},
);

if (response.statusCode == 200) {
return detailedDockerContainerFromJson(response.body);
Expand All @@ -156,9 +156,10 @@ class RemoteService {
Uri uri = Uri.parse(
"${user.hostUrl}/api/endpoints/${endpoint.id}/docker/containers/$containerId/restart");

http.Response response = await client.post(uri, headers: {
"Authorization": user.token?.getBearerToken() ?? '',
});
http.Response response = await client.post(
uri,
headers: user.token?.getHeaders() ?? {},
);

if (response.statusCode == 204) {
return true;
Expand All @@ -180,10 +181,7 @@ class RemoteService {
"${user.hostUrl}/api/endpoints/${endpoint.id}/docker/containers/$containerId/start");

http.Response response = await client.post(uri,
headers: {
"Authorization": user.token?.getBearerToken() ?? '',
},
body: jsonEncode({}));
headers: user.token?.getHeaders() ?? {}, body: jsonEncode({}));

if (response.statusCode == 204) {
return true;
Expand All @@ -204,9 +202,10 @@ class RemoteService {
Uri uri = Uri.parse(
"${user.hostUrl}/api/endpoints/${endpoint.id}/docker/containers/$containerId/stop");

http.Response response = await client.post(uri, headers: {
"Authorization": user.token?.getBearerToken() ?? '',
});
http.Response response = await client.post(
uri,
headers: user.token?.getHeaders() ?? {},
);

if (response.statusCode == 204) {
return true;
Expand All @@ -225,7 +224,7 @@ class RemoteService {

http.Response response = await client.get(
uri,
headers: {"Authorization": user.token?.getBearerToken() ?? ''},
headers: user.token?.getHeaders() ?? {},
);

if (response.statusCode == 200) {
Expand All @@ -244,7 +243,7 @@ class RemoteService {

http.Response response = await client.get(
uri,
headers: {"Authorization": user.token?.getBearerToken() ?? ''},
headers: user.token?.getHeaders() ?? {},
);

if (response.statusCode == 200) {
Expand All @@ -263,9 +262,10 @@ class RemoteService {
Uri uri = Uri.parse(
"${user.hostUrl}/api/endpoints/${endpoint.id}/docker/containers/$containerId/logs?tail=200&stdout=true&stderr=true");

http.Response response = await client.get(uri, headers: {
"Authorization": user.token?.getBearerToken() ?? '',
});
http.Response response = await client.get(
uri,
headers: user.token?.getHeaders() ?? {},
);

if (response.statusCode == 200) {
String splitStr = response.body.replaceRange(7, response.body.length, '');
Expand Down

0 comments on commit fdb6a9e

Please sign in to comment.