Skip to content

Commit

Permalink
Add tool for generating jwt tokens in cli
Browse files Browse the repository at this point in the history
  • Loading branch information
l7ssha committed Nov 3, 2024
1 parent 6a19f51 commit 4b2d5aa
Show file tree
Hide file tree
Showing 9 changed files with 180 additions and 6 deletions.
22 changes: 22 additions & 0 deletions .env.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
ROD_TOKEN=
ROD_INTENT_FEATURES_ENABLE=
ROD_ADMIN_IDS=
ROD_ADMIN_GUILD=
ROD_DEV=
ROD_DEV_GUILD_ID=
ROD_PREFIX=

ROD_ENABLE_API_SERVER=
API_SERVER_HOST=
API_SERVER_PORT=

DB_HOST=
DB_PORT=
POSTGRES_PASSWORD=
POSTGRES_USER=
POSTGRES_DB=

JWT_SECRET=
DISCORD_CLIENT_ID=
DISCORD_CLIENT_SECRET=
DISCORD_REDIRECT_URI=
80 changes: 80 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
name: Test

on:
push:
branches:
- rewrite
pull_request:

jobs:
analyze:
name: dart analyze
runs-on: ubuntu-latest
steps:
- name: Setup Dart Action
uses: dart-lang/setup-dart@v1

- name: Checkout
uses: actions/checkout@v4

- name: Cache
uses: actions/cache@v4
with:
path: ~/.pub-cache
key: ${{ runner.os }}-pubspec-${{ hashFiles('**/pubspec.lock') }}
restore-keys: |
${{ runner.os }}-pubspec-
- name: Install dependencies
run: dart pub get

- name: Analyze project source
run: dart analyze

fix:
name: dart fix
runs-on: ubuntu-latest
steps:
- name: Setup Dart Action
uses: dart-lang/setup-dart@v1

- name: Checkout
uses: actions/checkout@v4

- name: Cache
uses: actions/cache@v4
with:
path: ~/.pub-cache
key: ${{ runner.os }}-pubspec-${{ hashFiles('**/pubspec.lock') }}
restore-keys: |
${{ runner.os }}-pubspec-
- name: Install dependencies
run: dart pub get

- name: Analyze project source
run: dart fix --dry-run

format:
name: dart format
runs-on: ubuntu-latest
steps:
- name: Setup Dart Action
uses: dart-lang/setup-dart@v1

- name: Checkout
uses: actions/checkout@v4

- name: Cache
uses: actions/cache@v4
with:
path: ~/.pub-cache
key: ${{ runner.os }}-pubspec-${{ hashFiles('**/pubspec.lock') }}
restore-keys: |
${{ runner.os }}-pubspec-
- name: Install dependencies
run: dart pub get

- name: Format
run: dart format --set-exit-if-changed -l120 .
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ doc/api/

# dotenv environment variables file
.env*
!.env.dist

# IDE configuration folders
.vscode/
Expand Down
7 changes: 5 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ format: ## Run dart format
fix: ## Run dart fix
dart fix --apply

fix-project: fix format ## Fix whole project
analyze: ## Run dart analyze
dart analyze

fix-project: fix analyze format ## Fix whole project

run: ## Run dev project
docker compose up --build
docker compose up --build
2 changes: 2 additions & 0 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ include: package:lints/recommended.yaml

analyzer:
exclude: [build/**]
errors:
implementation_imports: ignore
32 changes: 32 additions & 0 deletions bin/console.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import 'package:running_on_dart/src/api/jwt_middleware.dart';

enum Commands {
generateAdminJwt('generate-admin-jwt');

final String name;
const Commands(this.name);

static Commands byName(String name) {
return values.firstWhere((e) => e.name == name);
}
}

int main(List<String> args) {
if (args.isEmpty) {
print("Available commands: \n${Commands.values.map((e) => e.name).join(", ")}");
return -1;
}

final command = Commands.byName(args[0]);

switch (command) {
case Commands.generateAdminJwt:
print(generateJwtKey("0", "test-user"));
break;
default:
print("Command '$command' not recognized!");
return -1;
}

return 0;
}
10 changes: 9 additions & 1 deletion lib/src/api/api_server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ enum WebApiPermission {
}

class WebServer {
final Logger _logger = Logger('ROD.ApiServer');

Future<shelf.Response> _handleBotInfo(shelf.Request request) async {
final botInfo = await Injector.appInstance.get<BotInfoService>().getCurrentBotInfo();

Expand Down Expand Up @@ -103,11 +105,17 @@ class WebServer {
shelf.Pipeline().addMiddleware(jwtMiddleware(requiredRoles.map((e) => e.name).toList())).addHandler(inner);

Future<void> startServer() async {
if (!enableApiServer) {
_logger.info("Api server disabled...");
return;
}

final router = await _setupRouter();

final app =
const shelf.Pipeline().addMiddleware(shelf.logRequests()).addMiddleware(corsHeaders()).addHandler(router.call);

await shelf_io.serve(app, "0.0.0.0", 8088);
_logger.info("Starting api server at `$apiServerHost:$apiServerPort`");
await shelf_io.serve(app, apiServerHost, apiServerPort);
}
}
16 changes: 13 additions & 3 deletions lib/src/api/jwt_middleware.dart
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
import 'package:jaguar_jwt/jaguar_jwt.dart';
import 'package:nyxx/nyxx.dart';
import 'package:running_on_dart/running_on_dart.dart';
import 'package:running_on_dart/src/api/api_server.dart';
import 'package:running_on_dart/src/api/utils.dart';
import 'package:shelf/shelf.dart' as shelf;

final jwtKey = getEnv("JWT_SECRET");

class MissingPermissionsException implements Exception {}

String generateJwtKey(String discordUserId, String userName, {List<String> perms = const []}) {
final jwtClaim =
JwtClaim(subject: discordUserId, maxAge: Duration(days: 1), payload: {"name": userName, 'perms': perms});
String generateJwtKey(String discordUserId, String userName) {
final perms = adminIds.contains(Snowflake.parse(discordUserId)) ? WebApiPermission.values.map((e) => e.name) : [];

final jwtClaim = JwtClaim(
subject: discordUserId,
maxAge: Duration(days: 1),
payload: {
"name": userName,
'perms': perms,
},
);

return issueJwtHS256(jwtClaim, jwtKey);
}
Expand Down
16 changes: 16 additions & 0 deletions lib/src/settings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ String getEnv(String key, [String? def]) =>
/// instead of throwing an exception.
bool getEnvBool(String key, [bool? def]) => ['true', '1'].contains(getEnv(key, def?.toString()).toLowerCase());

/// Get a [int] from an environment variable, throwing an exception if it is not set or if cannot parse env value to int.
///
/// If [def] is provided and the environment variable [key] is not set, [def] will be returned
/// instead of throwing an exception.
int getEnvInt(String key, [int? def]) =>
int.tryParse(getEnv(key, def?.toString())) ?? (throw Exception("Cannot parse `$key` env to int"));

/// The token to use for this instance.
final String token = getEnv('ROD_TOKEN');

Expand All @@ -35,6 +42,15 @@ final List<Snowflake> adminIds = getEnv('ROD_ADMIN_IDS').split(RegExp(r'\s+')).m
/// The interval at which to update the docs cache.
final Duration docsUpdateInterval = Duration(seconds: int.parse(getEnv('ROD_DOCS_UPDATE_INTERVAL', '86400')));

/// Whether api server functionality should be enabled.
final bool enableApiServer = getEnvBool('ROD_ENABLE_API_SERVER', false);

/// Api server host. Default 'localhost'.
final String apiServerHost = getEnv('API_SERVER_HOST', 'localhost');

/// Api server port. Default '8088'.
final int apiServerPort = getEnvInt('API_SERVER_PORT', 8088);

/// The packages to cache documentation for.
final List<String> docsPackages =
getEnv('ROD_DOCS_PACKAGES', 'nyxx nyxx_commands nyxx_lavalink nyxx_extensions').split(RegExp(r'\s+'));
Expand Down

0 comments on commit 4b2d5aa

Please sign in to comment.