diff --git a/lib/CF/Controller/Events.pm b/lib/CF/Controller/Events.pm
index 30a72a344bd59d86790abaef934246cf45403ab5..5306bb45a69f3cf884188007e542c5a22b7668fb 100644
--- a/lib/CF/Controller/Events.pm
+++ b/lib/CF/Controller/Events.pm
@@ -36,7 +36,7 @@ sub get ($c) {
     my $formatted = $event->format();
     $formatted->{acl} = [];
     $formatted->{stream_url} = $event->stream_url
-                             || '/hls/' . $event->uuid . '.m3u8';
+                             // '/hls/' . $event->uuid . '.m3u8';
 
     my $roles = $event->user_roles( $c->user );
 
@@ -110,10 +110,48 @@ sub delete ($c) {
     $c->render(status => 204, text => '');
 }
 
+sub role_add ($c) {
+    $c->openapi->valid_input or return;
+
+    my $args = $c->req->json;
+
+    my $event = $c->_get('chairman') || return;
+
+    my $subject = $c->schema->resultset('EventACLSubject')->find_or_new({
+        class => $args->{subject_class},
+        key   => $args->{subject_value},
+        name  => $args->{subject_name} || $args->{subject_value},
+    },
+    {
+        key => 'class_key'
+    });
+
+    if ( $subject->in_storage ) {
+        if ( $args->{subject_name} && $args->{subject_name} ne $subject->name ) {
+            $subject->update({
+                updated => \'now()',
+                name    => $args->{subject_name},
+            });
+        }
+    } else {
+        $subject->insert;
+    }
+
+    my $acl = $event->find_or_create_related('acls', {
+        subject_id => $subject->id,
+        role       => $args->{role},
+    });
+
+    $c->render(
+        status => 201,
+        openapi => { id => $acl->id },
+    );
+}
+
 sub _get ($c, $role='') {
 
     my $event = $c->schema->resultset('Event')->find({
-        $c->stash->{id}
+        id => $c->stash->{id}
     });
 
     return $c->error(404, 'Event not found') if ! $event || $event->deleted;
diff --git a/sql/9/up.sql b/sql/9/up.sql
new file mode 100644
index 0000000000000000000000000000000000000000..ef4b1a5997438c89343ff96543b65527a8a0f391
--- /dev/null
+++ b/sql/9/up.sql
@@ -0,0 +1,33 @@
+create table "events_acl_subjects" (
+    "id" integer not null default nextval('uid_seq'),
+    "updated" timestamp(0) not null default now(),
+    "class" varchar(8) not null,
+    "key" text not null,
+    "name" text,
+    primary key("id"),
+    unique("class", "key")
+);
+
+create table "events_acl" (
+    "id" integer not null default nextval('uid_seq'),
+    "event_id" integer not null,
+    "subject_id" integer not null,
+    "role" text,
+    primary key("id"),
+    unique("event_id", "subject_id", "role"),
+    foreign key ("event_id") references "events" ("id") on update cascade on delete cascade,
+    foreign key ("subject_id") references "events_acl_subjects" ("id") on update cascade on delete cascade
+);
+
+create view "events_acl_view" as (
+    select
+        "events_acl"."id",
+        "events_acl"."event_id",
+        "events_acl_subjects"."class" as "subject_class",
+        "events_acl_subjects"."key" as "subject_key",
+        "events_acl_subjects"."name" as "subject_name",
+        "events_acl"."role"
+    from "events_acl"
+    join "events_acl_subjects" on ("events_acl"."subject_id" = "events_acl_subjects"."id")
+);
+