From 2748cfac070114359a88756c3721dbb7a4658017 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 26 Nov 2008 23:25:24 +0000 Subject: [PATCH] * Role-based access control. Only admins can create projects. Only admins or project owners can edit or delete a project. --- src/Hydra/hydra.conf | 2 + src/Hydra/lib/Hydra.pm | 1 + src/Hydra/lib/Hydra/Controller/Root.pm | 55 +++++++++++++------ src/Hydra/lib/Hydra/Schema.pm | 4 +- src/Hydra/lib/Hydra/Schema/Buildinputs.pm | 4 +- src/Hydra/lib/Hydra/Schema/Buildproducts.pm | 4 +- src/Hydra/lib/Hydra/Schema/Buildresultinfo.pm | 4 +- src/Hydra/lib/Hydra/Schema/Builds.pm | 4 +- .../lib/Hydra/Schema/Buildschedulinginfo.pm | 4 +- src/Hydra/lib/Hydra/Schema/Buildsteps.pm | 4 +- .../lib/Hydra/Schema/Cachedpathinputs.pm | 4 +- .../Hydra/Schema/Cachedsubversioninputs.pm | 4 +- src/Hydra/lib/Hydra/Schema/Jobsetinputalts.pm | 4 +- src/Hydra/lib/Hydra/Schema/Jobsetinputs.pm | 4 +- src/Hydra/lib/Hydra/Schema/Jobsets.pm | 4 +- src/Hydra/lib/Hydra/Schema/Projects.pm | 6 +- src/Hydra/lib/Hydra/Schema/Systemtypes.pm | 4 +- src/Hydra/lib/Hydra/Schema/Userroles.pm | 25 +++++++++ src/Hydra/lib/Hydra/Schema/Users.pm | 9 ++- src/Hydra/root/login.tt | 39 +++++++++++++ src/Hydra/root/project.tt | 4 ++ src/hydra.sql | 17 ++++++ 22 files changed, 163 insertions(+), 47 deletions(-) create mode 100644 src/Hydra/lib/Hydra/Schema/Userroles.pm create mode 100644 src/Hydra/root/login.tt diff --git a/src/Hydra/hydra.conf b/src/Hydra/hydra.conf index ce065328..398ce61e 100644 --- a/src/Hydra/hydra.conf +++ b/src/Hydra/hydra.conf @@ -13,6 +13,8 @@ name Hydra class DBIx::Class user_class DB::Users + role_relation userroles + role_field role diff --git a/src/Hydra/lib/Hydra.pm b/src/Hydra/lib/Hydra.pm index 8728caab..d636d06e 100644 --- a/src/Hydra/lib/Hydra.pm +++ b/src/Hydra/lib/Hydra.pm @@ -11,6 +11,7 @@ use Catalyst qw/-Debug Static::Simple StackTrace Authentication + Authorization::Roles Session Session::Store::FastMmap Session::State::Cookie diff --git a/src/Hydra/lib/Hydra/Controller/Root.pm b/src/Hydra/lib/Hydra/Controller/Root.pm index 6707458b..4bfd92e1 100644 --- a/src/Hydra/lib/Hydra/Controller/Root.pm +++ b/src/Hydra/lib/Hydra/Controller/Root.pm @@ -74,7 +74,7 @@ sub login :Local { ? $c->flash->{afterLogin} : $c->uri_for('/')); return; - } + } $c->stash->{errorMsg} = "Bad username or password."; } @@ -98,7 +98,6 @@ sub requireLogin { sub queue :Local { my ($self, $c) = @_; - return requireLogin($c) if !$c->user_exists; $c->stash->{template} = 'queue.tt'; $c->stash->{queue} = [$c->model('DB::Builds')->search( {finished => 0}, {join => 'schedulingInfo', order_by => ["priority DESC", "timestamp"]})]; @@ -118,6 +117,8 @@ sub updateProject { $project->displayname($displayName); $project->description(trim $c->request->params->{description}); $project->enabled(trim($c->request->params->{enabled}) eq "1" ? 1 : 0); + $project->owner(trim($c->request->params->{owner})) + if $c->check_user_roles('admin'); $project->update; @@ -236,21 +237,35 @@ sub project :Local { $subcommand = "" unless defined $subcommand; - if ($subcommand eq "edit") { - $c->stash->{edit} = 1; - } elsif ($subcommand eq "submit" && $isPosted) { - $c->model('DB')->schema->txn_do(sub { - updateProject($c, $project); - }); - return $c->res->redirect($c->uri_for("/project", trim $c->request->params->{name})); - } elsif ($subcommand eq "delete" && $isPosted) { - $c->model('DB')->schema->txn_do(sub { - $project->delete; - }); - return $c->res->redirect($c->uri_for("/")); - } elsif ($subcommand eq "") { - } else { - return error($c, "Unknown subcommand $subcommand."); + if ($subcommand ne "") { + + return requireLogin($c) if !$c->user_exists; + + if (!$c->check_user_roles('admin') && $c->user->username ne $project->owner) { + return error($c, "Only the project owner or the administrator can perform this operation."); + } + + if ($subcommand eq "edit") { + $c->stash->{edit} = 1; + } + + elsif ($subcommand eq "submit" && $isPosted) { + $c->model('DB')->schema->txn_do(sub { + updateProject($c, $project); + }); + return $c->res->redirect($c->uri_for("/project", trim $c->request->params->{name})); + } + + elsif ($subcommand eq "delete" && $isPosted) { + $c->model('DB')->schema->txn_do(sub { + $project->delete; + }); + return $c->res->redirect($c->uri_for("/")); + } + + else { + return error($c, "Unknown subcommand $subcommand."); + } } $c->stash->{curProject} = $project; @@ -283,6 +298,12 @@ sub project :Local { sub createproject :Local { my ($self, $c, $subcommand) = @_; + return requireLogin($c) if !$c->user_exists; + + if (!$c->check_user_roles('admin')) { + return error($c, "Only administrators can create projects."); + } + if (defined $subcommand && $subcommand eq "submit") { eval { my $projectName = $c->request->params->{name}; diff --git a/src/Hydra/lib/Hydra/Schema.pm b/src/Hydra/lib/Hydra/Schema.pm index 285200a0..6923ce88 100644 --- a/src/Hydra/lib/Hydra/Schema.pm +++ b/src/Hydra/lib/Hydra/Schema.pm @@ -8,8 +8,8 @@ use base 'DBIx::Class::Schema'; __PACKAGE__->load_classes; -# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-26 20:02:52 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:jK/9VMZBot2RJwtlHA6QIg +# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-27 00:07:44 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:OcrPIHyQBUa+kF79Ltf95g # You can replace this text with custom content, and it will be preserved on regeneration diff --git a/src/Hydra/lib/Hydra/Schema/Buildinputs.pm b/src/Hydra/lib/Hydra/Schema/Buildinputs.pm index 3dee83d1..c92f6bae 100644 --- a/src/Hydra/lib/Hydra/Schema/Buildinputs.pm +++ b/src/Hydra/lib/Hydra/Schema/Buildinputs.pm @@ -36,8 +36,8 @@ __PACKAGE__->belongs_to("build", "Hydra::Schema::Builds", { id => "build" }); __PACKAGE__->belongs_to("dependency", "Hydra::Schema::Builds", { id => "dependency" }); -# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-26 20:02:52 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:LRcAsbLWbetVw+DCDnv/9w +# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-27 00:07:44 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:BEl4PIMuykTwqyl7La0pKQ # You can replace this text with custom content, and it will be preserved on regeneration diff --git a/src/Hydra/lib/Hydra/Schema/Buildproducts.pm b/src/Hydra/lib/Hydra/Schema/Buildproducts.pm index 90bfab84..1e599127 100644 --- a/src/Hydra/lib/Hydra/Schema/Buildproducts.pm +++ b/src/Hydra/lib/Hydra/Schema/Buildproducts.pm @@ -33,8 +33,8 @@ __PACKAGE__->set_primary_key("build", "productnr"); __PACKAGE__->belongs_to("build", "Hydra::Schema::Builds", { id => "build" }); -# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-26 20:02:52 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:Pu6gWxltfVJJ+9DBiC9bYg +# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-27 00:07:44 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:uEkpbb6hgGe47sDE7KtLDQ # You can replace this text with custom content, and it will be preserved on regeneration diff --git a/src/Hydra/lib/Hydra/Schema/Buildresultinfo.pm b/src/Hydra/lib/Hydra/Schema/Buildresultinfo.pm index 0a640825..e94cd7cd 100644 --- a/src/Hydra/lib/Hydra/Schema/Buildresultinfo.pm +++ b/src/Hydra/lib/Hydra/Schema/Buildresultinfo.pm @@ -29,8 +29,8 @@ __PACKAGE__->set_primary_key("id"); __PACKAGE__->belongs_to("id", "Hydra::Schema::Builds", { id => "id" }); -# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-26 20:02:52 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:X5GXZRLAaCMl8OKBGjtztw +# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-27 00:07:44 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:CfJnTtjRXGV5dD/MWbrJxA # You can replace this text with custom content, and it will be preserved on regeneration diff --git a/src/Hydra/lib/Hydra/Schema/Builds.pm b/src/Hydra/lib/Hydra/Schema/Builds.pm index 05537fd6..48f20263 100644 --- a/src/Hydra/lib/Hydra/Schema/Builds.pm +++ b/src/Hydra/lib/Hydra/Schema/Builds.pm @@ -70,8 +70,8 @@ __PACKAGE__->has_many( ); -# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-26 20:02:52 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:h32zqOEGcpXQy7pshiWVMA +# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-27 00:07:44 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:AFGyXbj7hMxpQxjzgpvrCw __PACKAGE__->has_many(dependents => 'Hydra::Schema::Buildinputs', 'dependency'); diff --git a/src/Hydra/lib/Hydra/Schema/Buildschedulinginfo.pm b/src/Hydra/lib/Hydra/Schema/Buildschedulinginfo.pm index 3b4e4697..993090e3 100644 --- a/src/Hydra/lib/Hydra/Schema/Buildschedulinginfo.pm +++ b/src/Hydra/lib/Hydra/Schema/Buildschedulinginfo.pm @@ -23,8 +23,8 @@ __PACKAGE__->set_primary_key("id"); __PACKAGE__->belongs_to("id", "Hydra::Schema::Builds", { id => "id" }); -# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-26 20:02:52 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:egegc7kFKTt9cEGuomi0cQ +# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-27 00:07:44 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:O6brsCdVF4TfvtmI9R+TOA # You can replace this text with custom content, and it will be preserved on regeneration diff --git a/src/Hydra/lib/Hydra/Schema/Buildsteps.pm b/src/Hydra/lib/Hydra/Schema/Buildsteps.pm index 3818f0ab..16ea4f15 100644 --- a/src/Hydra/lib/Hydra/Schema/Buildsteps.pm +++ b/src/Hydra/lib/Hydra/Schema/Buildsteps.pm @@ -35,8 +35,8 @@ __PACKAGE__->set_primary_key("id", "stepnr"); __PACKAGE__->belongs_to("id", "Hydra::Schema::Builds", { id => "id" }); -# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-26 20:02:52 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:kFD90OFRM1aqVVCBCh/geA +# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-27 00:07:44 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:klPmbTcngdzKN+Dzhj8gvw # You can replace this text with custom content, and it will be preserved on regeneration diff --git a/src/Hydra/lib/Hydra/Schema/Cachedpathinputs.pm b/src/Hydra/lib/Hydra/Schema/Cachedpathinputs.pm index 030b8b70..11a0c7b4 100644 --- a/src/Hydra/lib/Hydra/Schema/Cachedpathinputs.pm +++ b/src/Hydra/lib/Hydra/Schema/Cachedpathinputs.pm @@ -22,8 +22,8 @@ __PACKAGE__->add_columns( __PACKAGE__->set_primary_key("srcpath", "sha256hash"); -# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-26 20:02:52 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:r/3GaLIIWaX1fh8kfuQp+w +# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-27 00:07:44 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:vGVYmR4k3kezEwiCGSXZWQ # You can replace this text with custom content, and it will be preserved on regeneration diff --git a/src/Hydra/lib/Hydra/Schema/Cachedsubversioninputs.pm b/src/Hydra/lib/Hydra/Schema/Cachedsubversioninputs.pm index 02f818bd..2d065cc1 100644 --- a/src/Hydra/lib/Hydra/Schema/Cachedsubversioninputs.pm +++ b/src/Hydra/lib/Hydra/Schema/Cachedsubversioninputs.pm @@ -20,8 +20,8 @@ __PACKAGE__->add_columns( __PACKAGE__->set_primary_key("uri", "revision"); -# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-26 20:02:52 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:yTp1XcBSQ+6OJvVLugRh1w +# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-27 00:07:44 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:ORooKeTpZBPOQCgosHLGeg # You can replace this text with custom content, and it will be preserved on regeneration diff --git a/src/Hydra/lib/Hydra/Schema/Jobsetinputalts.pm b/src/Hydra/lib/Hydra/Schema/Jobsetinputalts.pm index e9466924..c60d3110 100644 --- a/src/Hydra/lib/Hydra/Schema/Jobsetinputalts.pm +++ b/src/Hydra/lib/Hydra/Schema/Jobsetinputalts.pm @@ -33,8 +33,8 @@ __PACKAGE__->belongs_to( ); -# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-26 20:02:52 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:lYdNLENxLW2mtZ2w+jou8w +# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-27 00:07:44 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:ZeFpiIuYHvaFqRSppuUpoA # You can replace this text with custom content, and it will be preserved on regeneration diff --git a/src/Hydra/lib/Hydra/Schema/Jobsetinputs.pm b/src/Hydra/lib/Hydra/Schema/Jobsetinputs.pm index 1e5d24ef..c1b7989d 100644 --- a/src/Hydra/lib/Hydra/Schema/Jobsetinputs.pm +++ b/src/Hydra/lib/Hydra/Schema/Jobsetinputs.pm @@ -43,8 +43,8 @@ __PACKAGE__->has_many( ); -# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-26 20:02:52 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:Bk/vLWpBjR3ZU0p1KN7KfA +# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-27 00:07:44 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:/PmcpU0eiLZT+dlUZYyTaQ # You can replace this text with custom content, and it will be preserved on regeneration diff --git a/src/Hydra/lib/Hydra/Schema/Jobsets.pm b/src/Hydra/lib/Hydra/Schema/Jobsets.pm index a01e1843..d67bb916 100644 --- a/src/Hydra/lib/Hydra/Schema/Jobsets.pm +++ b/src/Hydra/lib/Hydra/Schema/Jobsets.pm @@ -50,8 +50,8 @@ __PACKAGE__->has_many( ); -# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-26 20:02:52 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:9xvvQg/H0oibycB6B45V5A +# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-27 00:07:44 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:EGgAWXbhcEC0uBobJMfpUw # You can replace this text with custom content, and it will be preserved on regeneration diff --git a/src/Hydra/lib/Hydra/Schema/Projects.pm b/src/Hydra/lib/Hydra/Schema/Projects.pm index 0c9ea455..58d66830 100644 --- a/src/Hydra/lib/Hydra/Schema/Projects.pm +++ b/src/Hydra/lib/Hydra/Schema/Projects.pm @@ -16,6 +16,8 @@ __PACKAGE__->add_columns( { data_type => "text", is_nullable => 0, size => undef }, "enabled", { data_type => "integer", is_nullable => 0, size => undef }, + "owner", + { data_type => "text", is_nullable => 0, size => undef }, ); __PACKAGE__->set_primary_key("name"); __PACKAGE__->has_many( @@ -30,8 +32,8 @@ __PACKAGE__->has_many( ); -# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-26 20:02:52 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:3YMBhMqCjtpUjoTx4JLTOw +# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-27 00:07:44 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:jdyfk3vHisJRyE+VNR6dNA # You can replace this text with custom content, and it will be preserved on regeneration diff --git a/src/Hydra/lib/Hydra/Schema/Systemtypes.pm b/src/Hydra/lib/Hydra/Schema/Systemtypes.pm index 78b0f262..361973d9 100644 --- a/src/Hydra/lib/Hydra/Schema/Systemtypes.pm +++ b/src/Hydra/lib/Hydra/Schema/Systemtypes.pm @@ -16,8 +16,8 @@ __PACKAGE__->add_columns( __PACKAGE__->set_primary_key("system"); -# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-26 20:02:52 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:SSKVFeg7ieeLJcF+s1uWWw +# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-27 00:07:44 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:N/yG0cEhf0Y9Ve9YkdwRfA # You can replace this text with custom content, and it will be preserved on regeneration diff --git a/src/Hydra/lib/Hydra/Schema/Userroles.pm b/src/Hydra/lib/Hydra/Schema/Userroles.pm new file mode 100644 index 00000000..34eac4b1 --- /dev/null +++ b/src/Hydra/lib/Hydra/Schema/Userroles.pm @@ -0,0 +1,25 @@ +package Hydra::Schema::Userroles; + +use strict; +use warnings; + +use base 'DBIx::Class'; + +__PACKAGE__->load_components("Core"); +__PACKAGE__->table("UserRoles"); +__PACKAGE__->add_columns( + "username", + { data_type => "text", is_nullable => 0, size => undef }, + "role", + { data_type => "text", is_nullable => 0, size => undef }, +); +__PACKAGE__->set_primary_key("username", "role"); +__PACKAGE__->belongs_to("username", "Hydra::Schema::Users", { username => "username" }); + + +# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-27 00:07:44 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:f16iU2I/Htdo7mXHvAdwyQ + + +# You can replace this text with custom content, and it will be preserved on regeneration +1; diff --git a/src/Hydra/lib/Hydra/Schema/Users.pm b/src/Hydra/lib/Hydra/Schema/Users.pm index 47df1922..db0f5a8a 100644 --- a/src/Hydra/lib/Hydra/Schema/Users.pm +++ b/src/Hydra/lib/Hydra/Schema/Users.pm @@ -18,10 +18,15 @@ __PACKAGE__->add_columns( { data_type => "text", is_nullable => 0, size => undef }, ); __PACKAGE__->set_primary_key("username"); +__PACKAGE__->has_many( + "userroles", + "Hydra::Schema::Userroles", + { "foreign.username" => "self.username" }, +); -# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-26 20:02:52 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:BgF6FK+9d7+cc72sp6pfCQ +# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-27 00:07:44 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:wwRBfogrkKN2QdgmFjcUlA # You can replace this text with custom content, and it will be preserved on regeneration diff --git a/src/Hydra/root/login.tt b/src/Hydra/root/login.tt new file mode 100644 index 00000000..81f5fd3a --- /dev/null +++ b/src/Hydra/root/login.tt @@ -0,0 +1,39 @@ +[% WRAPPER layout.tt title="Login to Hydra" %] +[% PROCESS common.tt %] + +

Login

+ +[% IF c.user_exists %] +

+You are already logged in as [% c.user.username %]. +You can logout here. +

+[% ELSE %] + +[% IF errorMsg %] +

Error: [% errorMsg %]

+[% END %] + +
+ + + + + + + + + + + + + +
Username:
Password:
+ +
+ +
+ +[% END %] + +[% END %] diff --git a/src/Hydra/root/project.tt b/src/Hydra/root/project.tt index 3c4398e1..04d9234e 100644 --- a/src/Hydra/root/project.tt +++ b/src/Hydra/root/project.tt @@ -163,6 +163,10 @@ Description: [% INCLUDE maybeEditString param="description" value=curProject.description %] + + Owner: + [% INCLUDE maybeEditString param="owner" value=curProject.owner edit=(edit && c.check_user_roles('admin')) %] + Enabled: diff --git a/src/hydra.sql b/src/hydra.sql index d9a1c443..5982d35d 100644 --- a/src/hydra.sql +++ b/src/hydra.sql @@ -154,6 +154,8 @@ create table Projects ( displayName text not null, -- display name (e.g. "PatchELF") description text, enabled integer not null default 1 + owner text not null, + foreign key (owner) references Users(userName) -- ignored by sqlite ); @@ -269,3 +271,18 @@ create table Users ( emailAddress text not null, password text not null -- sha256 hash ); + + +create table UserRoles ( + userName text not null, + role text not null, + primary key (userName, role), + foreign key (userName) references Users(userName) -- ignored by sqlite +); + + +create trigger cascadeUserDelete + before delete on Users + for each row begin + delete from UserRoles where userName = old.userName; + end;