Skip to content

Commit

Permalink
add security definitions to individual routes
Browse files Browse the repository at this point in the history
  • Loading branch information
HomelessDinosaur committed Mar 5, 2024
1 parent b5b8007 commit 9586189
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 52 deletions.
9 changes: 5 additions & 4 deletions example/services/nitric_example.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,16 @@ class Profile {
}

void main() {
// Create an API named 'public'
final profileApi = Nitric.api("public");

var oidc = Nitric.oidcRule(
"profile security",
"https://dev-w7gm5ldb.us.auth0.com",
["https://test-security-definition/"]);

Nitric.attachOidc(profileApi.name, oidc);
// Create an API named 'public'
final profileApi = Nitric.api("public",
opts: ApiOptions(security: [
oidc(["user:read"])
]));

// Define a collection named 'profiles', then request reading and writing permissions.
final profiles = Nitric.store("profiles").requires([
Expand Down
12 changes: 6 additions & 6 deletions lib/src/nitric.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'package:nitric_sdk/resources.dart';

import 'resources/common.dart';

abstract class Worker {
Expand Down Expand Up @@ -31,7 +33,8 @@ class Nitric {
_makeResource(name, BucketResource.new) as BucketResource;

/// Create a [name]d api for registering HTTP handlers.
static Api api(String name) => _makeResource(name, Api.new) as Api;
static Api api(String name, {ApiOptions? opts}) =>
_makeResource(name, (name) => Api(name, opts: opts)) as Api;

/// Create a [name]d collection for storing documents.
static KeyValueStoreResource store(String name) =>
Expand All @@ -53,13 +56,10 @@ class Nitric {
_makeResource(name, Websocket.new) as Websocket;

/// Create a [name]d oidc rule for attaching security definitions to APIs.
static oidcRule(String name, String issuer, List<String> audiences) {
static SecurityOption oidcRule(
String name, String issuer, List<String> audiences) {
return (List<String> scopes) {
return OidcOptions(name, issuer, audiences, scopes);
};
}

static attachOidc(String apiName, OidcOptions oidc) {
return OidcSecurityDefinition(apiName, "${oidc.name}-$apiName", oidc);
}
}
102 changes: 64 additions & 38 deletions lib/src/resources/api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,25 @@ enum HttpMethod {
options,
}

class ApiOptions {
List<OidcOptions> security;
String basePath;

ApiOptions({this.security = const [], this.basePath = "/"});
}

/// An API resource.
class Api extends Resource {
Api(String name, {$p.ResourcesClient? client}) : super(name, client);
late ApiOptions opts;

Api(String name, {ApiOptions? opts, $p.ResourcesClient? client})
: super(name, client) {
if (opts == null) {
this.opts = ApiOptions();
} else {
this.opts = opts;
}
}

@override
Future<void> register() async {
Expand All @@ -19,64 +35,57 @@ class Api extends Resource {
type: $p.ResourceType.Api,
);

//var apiScopes = $p.ApiScopes();
var apiResource = $p.ApiResource(security: {}); // TODO security
var apiResource = $p.ApiResource();

for (var opt in opts.security) {
await _attach_oidc(name, opt);

apiResource.security[opt.name] = $p.ApiScopes(scopes: opt.scopes);
}

await client
.declare($p.ResourceDeclareRequest(id: resource, api: apiResource));
}

/// A GET request [handler] that [match]es a specific route.
Future<void> get(
String match,
HttpHandler handler,
) async {
Route(this, match).get(handler);
Future<void> get(String match, HttpHandler handler,
{List<OidcOptions> security = const []}) async {
Route(this, match, security: security).get(handler);
}

/// A POST request [handler] that [match]es a specific route.
Future<void> post(
String match,
HttpHandler handler,
) async {
Route(this, match).post(handler);
Future<void> post(String match, HttpHandler handler,
{List<OidcOptions> security = const []}) async {
Route(this, match, security: security).post(handler);
}

/// A PUT request [handler] that [match]es a specific route.
Future<void> put(
String match,
HttpHandler handler,
) async {
Route(this, match).put(handler);
Future<void> put(String match, HttpHandler handler,
{List<OidcOptions> security = const []}) async {
Route(this, match, security: security).put(handler);
}

/// A DELETE request [handler] that [match]es a specific route.
Future<void> delete(
String match,
HttpHandler handler,
) async {
Route(this, match).delete(handler);
Future<void> delete(String match, HttpHandler handler,
{List<OidcOptions> security = const []}) async {
Route(this, match, security: security).delete(handler);
}

/// A OPTIONS request [handler] that [match]es a specific route.
Future<void> options(
String match,
HttpHandler handler,
) async {
Route(this, match).options(handler);
Future<void> options(String match, HttpHandler handler,
{List<OidcOptions> security = const []}) async {
Route(this, match, security: security).options(handler);
}

/// A request [handler] that [match]es a specific route on all HTTP methods.
Future<void> all(
String match,
HttpHandler handler,
) async {
Route(this, match).all(handler);
Future<void> all(String match, HttpHandler handler,
{List<OidcOptions> security = const []}) async {
Route(this, match, security: security).all(handler);
}

/// Create a route that [match]es on a specific path.
Route route(String match) {
return Route(this, match);
Route route(String match, {List<OidcOptions> security = const []}) {
return Route(this, match, security: security);
}
}

Expand All @@ -88,7 +97,10 @@ class Route {
/// The path that this route will match on.
String match;

Route(this.api, this.match);
/// The security to apply to the route.
List<OidcOptions> security;

Route(this.api, this.match, {this.security = const []});

/// A GET request [handler] for this route.
Future<void> get(HttpHandler handler) async {
Expand Down Expand Up @@ -144,7 +156,10 @@ class ApiWorker implements Worker {
/// The HTTP methods that will be accepted for this trigger.
List<HttpMethod> methods;

ApiWorker(this.route, this.handler, this.methods);
/// The security to apply to this api worker.
List<OidcOptions> security;

ApiWorker(this.route, this.handler, this.methods, {this.security = const []});

/// Start the route handler.
@override
Expand All @@ -153,12 +168,23 @@ class ApiWorker implements Worker {
final channel = createClientChannelFromEnvVar();
final client = $ap.ApiClient(channel);

var options = $ap.ApiWorkerOptions();
for (var s in security) {
await _attach_oidc(route.api.name, s);

options.security[s.name] = $ap.ApiWorkerScopes(scopes: s.scopes);
}

if (options.security.isEmpty) {
options.securityDisabled = true;
}

// Create the request to register the API with the membrane
final registrationRequest = $ap.RegistrationRequest(
api: route.api.name,
path: route.match,
methods: methods.map((e) => e.name.toUpperCase()),
options: $ap.ApiWorkerOptions(securityDisabled: true),
options: options,
);
final initMsg = $ap.ClientMessage(registrationRequest: registrationRequest);

Expand Down
1 change: 1 addition & 0 deletions lib/src/resources/common.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'dart:async';
import 'dart:convert';

import 'package:nitric_sdk/api.dart';
import 'package:nitric_sdk/resources.dart';
import 'package:nitric_sdk/src/api/queue.dart';
import 'package:nitric_sdk/src/grpc_helper.dart';
import 'package:nitric_sdk/src/nitric.dart';
Expand Down
11 changes: 9 additions & 2 deletions lib/src/resources/oidc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,22 @@ class OidcOptions {

typedef SecurityOption = OidcOptions Function(List<String> scopes);

Future<OidcSecurityDefinition> _attach_oidc(
String apiName, OidcOptions options) async {
var secDef = OidcSecurityDefinition(apiName, options);
await secDef.register();
return secDef;
}

class OidcSecurityDefinition extends Resource {
String apiName;
late String ruleName;
late String issuer;
late List<String> audiences;

OidcSecurityDefinition(this.apiName, String name, OidcOptions options,
OidcSecurityDefinition(this.apiName, OidcOptions options,
{$p.ResourcesClient? client})
: super(name, client) {
: super("${options.name}-$apiName", client) {
ruleName = options.name;
issuer = options.issuer;
audiences = options.audiences;
Expand Down
3 changes: 1 addition & 2 deletions test/src/resources/oidc_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@ void main() {
var opts =
OidcOptions("oidcName", "http://test-issuer", ["users"], ["user:read"]);

var oidc = OidcSecurityDefinition("apiName", "oidcName-apiName", opts,
client: resourceClient);
var oidc = OidcSecurityDefinition("apiName", opts, client: resourceClient);

var req = ResourceDeclareRequest(
id: ResourceIdentifier(
Expand Down

0 comments on commit 9586189

Please sign in to comment.