package uac; use strict; use warnings; no warnings 'redefine'; use CGI::Session qw(-ip-match); use CGI::Cookie(); use Data::Dumper; use auth(); use db(); use template(); use project(); use studios(); use user_settings(); use user_default_studios(); #use base 'Exporter'; our @EXPORT_OK = qw( get_user get_users update_user insert_user delete_user get_roles insert_role update_role get_role_columns get_studios_by_user get_users_by_studio get_projects_by_user get_user_role get_studio_roles assign_user_role remove_user_role get_user_permissions get_user_presets prepare_request set_template_permissions permission_denied ); # get user by name sub get_user($$) { my $config = shift; my $user = shift; my $query = qq{ select id, name, full_name, email, disabled, modified_at, created_at from calcms_users where name=? }; my $bind_values = [$user]; my $dbh = db::connect($config); my $users = db::get( $dbh, $query, $bind_values ); if ( scalar @$users != 1 ) { print STDERR "cannot find user '$user'\n"; return undef; } return $users->[0]; } # get all users sub get_users($;$) { my $config = shift; my $condition = shift; my @conditions = (); my @bind_values = (); for my $key ( 'name', 'email' ) { my $value = $condition->{$key}; next unless defined $value; next if $value eq ''; push @conditions, $key . '=?'; push @bind_values, $value; } my $conditions = ''; $conditions = " where " . join( " and ", @conditions ) if ( scalar @conditions > 0 ); my $query = qq{ select id, name, full_name, email, disabled, modified_at, created_at from calcms_users $conditions }; my $dbh = db::connect($config); my $users = db::get( $dbh, $query, \@bind_values ); return $users; } #TODO: get_users_by_project # get all users of a given studio id # used at series (previously named get_studio_users) sub get_users_by_studio ($$) { my $config = shift; my $condition = shift; return unless defined $condition->{studio_id}; my @conditions = (); my @bind_values = (); if ( ( defined $condition->{project_id} ) && ( $condition->{project_id} ne '' ) ) { push @conditions, 'ur.project_id=?'; push @bind_values, $condition->{project_id}; } if ( ( defined $condition->{studio_id} ) && ( $condition->{studio_id} ne '' ) ) { push @conditions, 'ur.studio_id=?'; push @bind_values, $condition->{studio_id}; } my $conditions = ''; $conditions = " and " . join( " and ", @conditions ) if ( scalar @conditions > 0 ); my $query = qq{ select distinct(u.id), u.name, u.full_name from calcms_user_roles ur, calcms_users u where ur.user_id=u.id $conditions }; my $dbh = db::connect($config); my $users = db::get( $dbh, $query, \@bind_values ); return $users; } # get projects a user is assigned by name sub get_projects_by_user ($$) { my $config = shift; my $condition = shift; my @conditions = (); my @bind_values = (); if ( ( defined $condition->{project_id} ) && ( $condition->{project_id} ne '' ) ) { push @conditions, 'ur.project_id=?'; push @bind_values, $condition->{project_id}; } if ( ( defined $condition->{studio_id} ) && ( $condition->{studio_id} ne '' ) ) { push @conditions, 'ur.studio_id=?'; push @bind_values, $condition->{studio_id}; } if ( ( defined $condition->{user} ) && ( $condition->{user} ne '' ) ) { push @conditions, 'u.name=?'; push @bind_values, $condition->{user}; } my $conditions = ''; $conditions = " and " . join( " and ", @conditions ) if ( @conditions > 0 ); my $query = qq{ select distinct p.*, ur.project_id project_id from calcms_user_roles ur, calcms_users u, calcms_projects p where ur.user_id=u.id and p.project_id=ur.project_id $conditions }; my $dbh = db::connect($config); my $users = db::get( $dbh, $query, \@bind_values ); return $users; } # get all studios a user is assigned to by role # used at series (previously named get_user_studios) sub get_studios_by_user ($$) { my $config = shift; my $condition = shift; my @conditions = (); my @bind_values = (); if ( ( defined $condition->{project_id} ) && ( $condition->{project_id} ne '' ) ) { push @conditions, 'ur.project_id=?'; push @bind_values, $condition->{project_id}; } if ( ( defined $condition->{studio_id} ) && ( $condition->{studio_id} ne '' ) ) { push @conditions, 'ur.studio_id=?'; push @bind_values, $condition->{studio_id}; } if ( ( defined $condition->{user} ) && ( $condition->{user} ne '' ) ) { push @conditions, 'u.name=?'; push @bind_values, $condition->{user}; } my $conditions = ''; $conditions = " and " . join( " and ", @conditions ) if ( @conditions > 0 ); my $query = qq{ select distinct s.*, ur.project_id project_id from calcms_user_roles ur, calcms_users u, calcms_studios s where ur.user_id=u.id and s.id=ur.studio_id $conditions }; my $dbh = db::connect($config); my $users = db::get( $dbh, $query, \@bind_values ); return $users; } sub insert_user($$) { my $config = shift; my $entry = shift; $entry->{created_at} = time::time_to_datetime( time() ); $entry->{modified_at} = time::time_to_datetime( time() ); my $dbh = db::connect($config); db::insert( $dbh, 'calcms_users', $entry ); } sub update_user($$) { my $config = shift; my $entry = shift; $entry->{modified_at} = time::time_to_datetime( time() ); my @keys = sort keys %$entry; my $values = join( ",", map { $_ . '=?' } @keys ); my @bind_values = map { $entry->{$_} } @keys; push @bind_values, $entry->{id}; my $query = qq{ update calcms_users set $values where id=? }; my $dbh = db::connect($config); db::put( $dbh, $query, \@bind_values ); } sub delete_user($$) { my $config = shift; my $id = shift; return unless ( defined $id && ( $id =~ /^\d+$/ ) ); my $query = qq{ delete from calcms_users where id=? }; my $dbh = db::connect($config); db::put( $dbh, $query, [$id] ); } # get all roles used by all users of a studio # available conditions: project_id, studio_id sub get_studio_roles($$) { my $config = shift; my $condition = shift; return [] if ( $condition->{studio_id} eq '' ); my @conditions = (); my @bind_values = (); if ( ( defined $condition->{project_id} ) && ( $condition->{project_id} ne '' ) ) { push @conditions, 'ur.project_id=?'; push @bind_values, $condition->{project_id}; } if ( ( defined $condition->{studio_id} ) && ( $condition->{studio_id} ne '' ) ) { push @conditions, 'ur.studio_id=?'; push @bind_values, $condition->{studio_id}; } my $conditions = ''; $conditions = " and " . join( " and ", @conditions ) if ( @conditions > 0 ); my $query = qq{ select r.*, ur.studio_id, ur.project_id from calcms_roles r, calcms_user_roles ur where r.id=ur.role_id $conditions }; my $dbh = db::connect($config); my $roles = db::get( $dbh, $query, \@bind_values ); return $roles; } # get role columns (for external use only) sub get_role_columns($) { my $config = shift; my $dbh = db::connect($config); my $columns = db::get_columns_hash( $dbh, 'calcms_roles' ); return $columns; } # get roles # filter: studio_id project_id sub get_roles($$) { my $config = shift; my $condition = shift; my @conditions = (); my @bind_values = (); my $dbh = db::connect($config); my $columns = db::get_columns_hash( $dbh, 'calcms_roles' ); for my $column ( sort keys %$columns ) { if ( defined $condition->{$column} ) { push @conditions, $column . '=?'; push @bind_values, $condition->{$column}; } } my $conditions = ''; $conditions = ' where ' . join( ' and ', @conditions ) if ( @conditions > 0 ); my $query = qq{ select r.* from calcms_roles r $conditions }; my $roles = db::get( $dbh, $query, \@bind_values ); return $roles; } #insert role to database, set created_at and modified_at sub insert_role ($$) { my $config = shift; my $entry = shift; $entry->{created_at} = time::time_to_datetime( time() ); $entry->{modified_at} = time::time_to_datetime( time() ); my $dbh = db::connect($config); my $columns = db::get_columns_hash( $dbh, 'calcms_roles' ); my $role = {}; for my $column ( keys %$columns ) { $role->{$column} = $entry->{$column} if defined $entry->{$column}; } db::insert( $dbh, 'calcms_roles', $role ); } #update role, set modified_at sub update_role($$) { my $config = shift; my $entry = shift; $entry->{modified_at} = time::time_to_datetime( time() ); my $dbh = db::connect($config); my $columns = db::get_columns_hash( $dbh, 'calcms_roles' ); my @keys = sort keys %$columns; my $values = join( ",", map { $_ . '=?' } @keys ); my @bind_values = map { $entry->{$_} } @keys; push @bind_values, $entry->{id}; my $query = qq{ update calcms_roles set $values where id=? }; db::put( $dbh, $query, \@bind_values ); } # delete role from database sub delete_role($$) { my $config = shift; my $id = shift; return unless ( defined $id && ( $id =~ /^\d+$/ ) ); my $query = qq{ delete from calcms_roles where id=? }; my $dbh = db::connect($config); db::put( $dbh, $query, [$id] ); } # get all roles for given conditions: project_id, studio_id, user_id, name # includes global admin user role sub get_user_roles($$) { my $config = shift; my $condition = shift; my @conditions = (); my @bind_values = (); if ( defined $condition->{user} ) { push @conditions, 'u.name=?'; push @bind_values, $condition->{user}; } if ( defined $condition->{user_id} ) { push @conditions, 'ur.user_id=?'; push @bind_values, $condition->{user_id}; } if ( defined $condition->{studio_id} ) { push @conditions, 'ur.studio_id=?'; push @bind_values, $condition->{studio_id}; } if ( defined $condition->{project_id} ) { push @conditions, 'ur.project_id=?'; push @bind_values, $condition->{project_id}; } my $conditions = ''; $conditions = " and " . join( " and ", @conditions ) if ( @conditions > 0 ); my $query = qq{ select distinct r.* from calcms_users u, calcms_user_roles ur, calcms_roles r where ur.user_id=u.id and ur.role_id=r.id $conditions }; my $dbh = db::connect($config); my $user_roles = db::get( $dbh, $query, \@bind_values ); #return roles, if the contain an admin role for my $role (@$user_roles) { return $user_roles if $role->{role} eq 'Admin'; } #get all admin roles delete $condition->{studio_id} if defined $condition->{studio_id}; delete $condition->{project_id} if defined $condition->{project_id}; my $admin_roles = get_admin_user_roles( $config, $condition ); #add admin roles to user roles my @user_roles = ( @$admin_roles, @$user_roles ); $user_roles = \@user_roles; return $user_roles; } #return admin user roles for given conditions: project_id, studio_id, user, user_id sub get_admin_user_roles ($$) { my $config = shift; my $condition = shift; my @conditions = (); my @bind_values = (); if ( ( defined $condition->{user} ) && ( $condition->{user} ne '' ) ) { push @conditions, 'u.name=?'; push @bind_values, $condition->{user}; } if ( ( defined $condition->{user_id} ) && ( $condition->{user_id} ne '' ) ) { push @conditions, 'ur.user_id=?'; push @bind_values, $condition->{user_id}; } if ( ( defined $condition->{studio_id} ) && ( $condition->{studio_id} ne '' ) ) { push @conditions, 'ur.studio_id=?'; push @bind_values, $condition->{studio_id}; } if ( ( defined $condition->{project_id} ) && ( $condition->{project_id} ne '' ) ) { push @conditions, 'ur.project_id=?'; push @bind_values, $condition->{project_id}; } my $conditions = ''; $conditions = " and " . join( " and ", @conditions ) if ( @conditions > 0 ); my $query = qq{ select distinct r.*, ur.studio_id, ur.project_id from calcms_users u, calcms_user_roles ur, calcms_roles r where ur.user_id=u.id and ur.role_id=r.id and r.role='Admin' $conditions limit 1 }; my $dbh = db::connect($config); my $user_roles = db::get( $dbh, $query, \@bind_values ); return $user_roles; } # read permissions for given conditions and add to user_permissions # return user_permissions # studio_id, user_id, name sub get_user_permissions ($$;$) { my $config = shift; my $conditions = shift; my $user_permissions = shift; my $user_roles = get_user_roles( $config, $conditions ); my $admin_roles = get_admin_user_roles( $config, $conditions ); my @user_roles = ( @$admin_roles, @$user_roles ); #set default permissions $user_permissions = {} unless defined $user_permissions; $user_permissions->{is_admin} = 1 if scalar @$admin_roles > 0; my $max_level = 0; # aggregate max permissions # should be limited by project and studio for my $user_role (@user_roles) { if ( $user_role->{level} > $max_level ) { $user_permissions->{level} = $user_role->{level}; $user_permissions->{id} = $user_role->{id}; $user_permissions->{role} = $user_role->{role}; $user_permissions->{studio_id} = $user_role->{studio_id}; $user_permissions->{project_id} = $user_role->{project_id}; $max_level = $user_role->{level}; } for my $permission ( keys %$user_role ) { if ( ( $permission ne 'level' ) && ( $permission ne 'id' ) && ( $permission ne 'role' ) && ( $permission ne 'studio_id' ) && ( $permission ne 'project_id' ) ) { $user_permissions->{$permission} = 1 if ( defined $user_role->{$permission} ) && ( $user_role->{$permission} ne '0' ); } } } return $user_permissions; } #get user id by user name sub get_user_id ($$) { my $config = shift; my $user = shift; return undef unless defined $user; my $query = qq{ select id from calcms_users where binary name=? }; my $dbh = db::connect($config); my $users = db::get( $dbh, $query, [$user] ); return undef if scalar @$users == 0; return $users->[0]->{id}; } #get role id by role name sub get_role_id ($$) { my $config = shift; my $role = shift; return undef unless defined $role; my $query = qq{ select id from calcms_roles where role=? }; my $dbh = db::connect($config); my $roles = db::get( $dbh, $query, [$role] ); return undef if scalar @$roles == 0; return $roles->[0]->{id}; } # assign a role to an user (for a studio) sub assign_user_role($$) { my $config = shift; my $options = shift; return undef unless defined $options->{project_id}; return undef unless defined $options->{studio_id}; return undef unless defined $options->{user_id}; return undef unless defined $options->{role_id}; #return if already exists my $query = qq{ select * from calcms_user_roles where project_id=? and studio_id=? and user_id=? and role_id=? }; my $dbh = db::connect($config); my $user_roles = db::get( $dbh, $query, [ $options->{project_id}, $options->{studio_id}, $options->{user_id}, $options->{role_id} ] ); return undef if scalar @$user_roles > 0; #insert entry my $entry = { project_id => $options->{project_id}, studio_id => $options->{studio_id}, user_id => $options->{user_id}, role_id => $options->{role_id}, created_at => time::time_to_datetime( time() ) }; return db::insert( $dbh, 'calcms_user_roles', $entry ); } # unassign a user from a role of (for a studio) sub remove_user_role($$) { my $config = shift; my $options = shift; return undef unless defined $options->{project_id}; return undef unless defined $options->{studio_id}; return undef unless defined $options->{user_id}; return undef unless defined $options->{role_id}; my $query = qq{ delete from calcms_user_roles where project_id=? and studio_id=? and user_id=? and role_id=? }; my $bind_values = [ $options->{project_id}, $options->{studio_id}, $options->{user_id}, $options->{role_id} ]; my $dbh = db::connect($config); my $result = db::put( $dbh, $query, $bind_values ); # successfully return even if no entry exists return 1; } #checks sub is_user_assigned_to_studio ($$) { my $request = shift; my $options = shift; my $config = $request->{config}; return 0 unless defined $request->{user}; return 0 unless defined $options->{studio_id}; return 0 unless defined $options->{project_id}; my $options2 = { user => $request->{user}, studio_id => $options->{studio_id}, project_id => $options->{project_id} }; my $user_studios = uac::get_studios_by_user( $config, $options2 ); return 1 if scalar @$user_studios == 1; return 0; } # print errors at get_user_presets and check for project id and studio id # call after header is printed sub check($$$) { my $config = shift; my $params = shift; my $user_presets = shift; if ( defined $user_presets->{error} ) { uac::print_error( $user_presets->{error} ); return 0; } my $project_check = project::check( $config, { project_id => $params->{project_id} } ); if ( $project_check ne '1' ) { uac::print_error($project_check); return 0; } my $studio_check = studios::check( $config, { studio_id => $params->{studio_id} } ); if ( $studio_check ne '1' ) { uac::print_error($studio_check); return 0; } return 1; } # get user, projects and studios user is assigned to for selected values from params # set permissions for selected project and studio # return request sub get_user_presets($$) { my $config = shift; my $options = shift; my $user = $options->{user} || ''; my $error = undef; return { error => "no user selected" } if ( $user eq '' ); my $project_id = $options->{project_id} || ''; my $studio_id = $options->{studio_id} || ''; $config->{access}->{write} = 0; my $user_settings = user_settings::get( $config, { user => $user } ); $project_id = $user_settings->{project_id} // '' if $project_id eq ''; my $defaults = user_default_studios::get( $config, { user => $user, project_id => $project_id } ); $studio_id = $defaults->{studio_id} // $user_settings->{studio_id} // '' if $studio_id eq ''; #get my $admin_roles = get_admin_user_roles( $config, { user => $user } ); #get all projects by user my $projects = uac::get_projects_by_user( $config, { user => $user } ); return { error => "no project is assigned to user" } if scalar @$projects == 0; $projects = project::get($config) if ( @$admin_roles > 0 ); my @projects = reverse sort { $a->{end_date} cmp $b->{end_date} } (@$projects); $projects = \@projects; if ( $project_id ne '' && $project_id ne '-1' ) { my $projectFound = 0; for my $project (@$projects) { if ( $project->{project_id} eq $project_id ) { $projectFound = 1; last; } } return { error => "project is not assigned to user" } if ( $projectFound == 0 ); } else { $project_id = $projects->[0]->{project_id}; } #check if studios are assigned to project my $studios = project::get_studios( $config, { project_id => $project_id } ); $error = "no studio is assigned to project" if scalar @$studios == 0; if ( scalar @$admin_roles == 0 ) { #get all studios by user $studios = uac::get_studios_by_user( $config, { user => $user, project_id => $project_id } ); $error = "no studio is assigned to user" if scalar @$studios == 0; if ( ( $studio_id ne '' ) && ( $studio_id ne '-1' ) ) { my $studioFound = 0; for my $studio (@$studios) { if ( $studio->{id} eq $studio_id ) { $studioFound = 1; last; } } $error = "studio is not assigned to user" if ( $studioFound == 0 ); } else { $studio_id = $studios->[0]->{id} unless defined $studio_id; } } else { #for admin get studios by project $studios = studios::get( $config, { project_id => $project_id } ); if ( ( $studio_id ne '' ) && ( $studio_id ne '-1' ) ) { my $studioFound = 0; for my $studio (@$studios) { if ( $studio->{id} eq $studio_id ) { $studioFound = 1; last; } } $error = "studio is not assigned to project" if ( $studioFound == 0 ); } else { $studio_id = $studios->[0]->{id} unless defined $studio_id; } } my $permissions = uac::get_user_permissions( $config, { user => $user, project_id => $project_id, studio_id => $studio_id } ); #only admin is allowed to select all projects # if($permissions->{is_admin}==1){ # $projects=project::get($config); # } #set studios and projects as selected, TODO:do in JS my $selectedProject = {}; for my $project (@$projects) { if ( $project_id eq $project->{project_id} ) { $project->{selected} = 'selected="selected"'; $selectedProject = $project; last; } } my $selectedStudio = {}; for my $studio (@$studios) { if ( $studio_id eq $studio->{id} ) { $studio->{selected} = 'selected="selected"'; $selectedStudio = $studio; last; } } my $logout_url = ( split( /\//, $0 ) )[-1]; my $result = { user => $user, logout_url => $logout_url, project_id => $project_id, # from parameter or default projects => $projects, project => $selectedProject, studio_id => $studio_id, # from parameter or default studios => $studios, studio => $selectedStudio, permissions => $permissions, # from parameter or default config => $config }; $result->{error} = $error if defined $error; return $result; } sub setDefaultProject ($$) { my $params = shift; my $user_presets = shift; $params->{project_id} = $user_presets->{project_id} if ( !defined $params->{authAction} ) || ( $params->{authAction} eq '' ) || ( $params->{authAction} eq 'login' ); return $params; } sub setDefaultStudio($$) { my $params = shift; my $user_presets = shift; $params->{studio_id} = $user_presets->{studio_id} if ( !defined $params->{authAction} ) || ( $params->{authAction} eq '' ) || ( $params->{authAction} eq 'login' ); return $params; } #set user preset properties to request sub prepare_request ($$) { my $request = shift; my $user_presets = shift; for my $key ( keys %$user_presets ) { $request->{$key} = $user_presets->{$key}; } #enrich menu parameters for my $key ( 'studio_id', 'project_id', 'studio', 'project', 'studios', 'projects', 'user', 'logout_url' ) { $request->{params}->{checked}->{presets}->{$key} = $user_presets->{$key}; } return $request; } #TODO: shift to permissions sub entry sub set_template_permissions ($$) { my $permissions = shift; my $params = shift; for my $usecase ( keys %$permissions ) { $params->{'allow'}->{$usecase} = 1 if ( $permissions->{$usecase} eq '1' ); } return $params; } #print error message sub permissions_denied($) { my $message = shift; $message =~ s/_/ /g; print '