diff --git a/root/usr/share/smanager/lib/SrvMngr.pm b/root/usr/share/smanager/lib/SrvMngr.pm index 280eac1..7e9fcb7 100644 --- a/root/usr/share/smanager/lib/SrvMngr.pm +++ b/root/usr/share/smanager/lib/SrvMngr.pm @@ -30,8 +30,11 @@ use SrvMngr::Plugin::WithoutCache; use esmith::I18N; +# Import the function(s) you need +use SrvMngr_Auth qw(check_admin_access); + #this is overwrittrn with the "release" by the spec file - release can be "99.el8.sme" -our $VERSION = '70.el8.sme'; +our $VERSION = '78.el8.sme'; #Extract the release value if ($VERSION =~ /^(\d+)/) { $VERSION = $1; # $1 contains the matched numeric digits @@ -46,7 +49,7 @@ our @EXPORT_OK = qw( getNavigation ip_number validate_password is_normal_password email_simple mac_address_or_blank mac_address ip_number_or_blank lang_space get_routes_list subnet_mask get_reg_mask - gen_locale_date_string get_public_ip_address + gen_locale_date_string get_public_ip_address simpleNavMerge ); has home => sub { @@ -301,10 +304,11 @@ sub setup_routing { $if_logged_in->get('/userpassword')->to('userpassword#main')->name('passwd'); $if_logged_in->post('/userpassword')->to('userpassword#change_password')->name('passwd2'); - my $if_admin = $r->under( sub { - my $c =shift; - return $c->is_admin || $c->auth_fail($c->l("acs_ADMIN")); - }); + my $if_admin = $r->under( sub { + my $c = shift; + # Call the imported function directly + return check_admin_access($c) || $c->auth_fail($c->l("acs_ADMIN")); + }); $if_admin->get('/backup')->to('backup#main')->name('backup'); $if_admin->post('/backup')->to('backup#do_display')->name('backupd'); @@ -549,9 +553,10 @@ sub getNavigation { use esmith::NavigationDB; - my $c = shift; + my $class = shift; #not the controller as it is called as an external, not part of the controller. my $lang = shift || 'en-us'; my $menu = shift || 'N'; + my $username = shift || ''; #Username when logged in as a user not admin # my $lang = $c->session->{lang} || 'en-us'; @@ -560,6 +565,26 @@ sub getNavigation { my @files = (); my %files_hash = (); + + # Added: Store allowed admin panels for non-admin users + my @allowed_admin_panels = (); + my $is_admin = 1; # Default to admin (full access) + + # Added: Check if user is non-admin and get their allowed panels + if ($username ne '') { + # Get the AccountsDB to check user permissions + my $accountsdb = esmith::AccountsDB->open_ro() or + die "Couldn't open AccountsDB\n"; + + # Check if user has AdminPanels property + my $user_rec = $accountsdb->get($username); + if (defined $user_rec && $user_rec->prop('AdminPanels')) { + $is_admin = 0; # User is non-admin with specific panel access + # Get comma-separated list of allowed admin panels + my $admin_panels = $user_rec->prop('AdminPanels'); + @allowed_admin_panels = split(/,/, $admin_panels); + } + } #----------------------------------------------------- # Determine the directory where the functions are kept @@ -638,70 +663,110 @@ sub getNavigation { } foreach my $file (keys %files_hash) - { - #my $heading = 'Unknown'; - my $heading = 'Legacy'; - - my $description = $file; - my $headingWeight = 99999; - my $descriptionWeight = 99999; - my $urlpath = ''; - my $menucat = 'A'; # admin menu (default) + { + #my $heading = 'Unknown'; + my $heading = 'Legacy'; + + my $description = $file; + my $headingWeight = 99999; + my $descriptionWeight = 99999; + my $urlpath = ''; + my $menucat = 'A'; # admin menu (default) - my $rec = $navdb->get($file); + my $rec = $navdb->get($file); - if (defined $rec) - { - $heading = $rec->prop('Heading'); - $description = $rec->prop('Description'); - $headingWeight = $rec->prop('HeadingWeight') || 99999; #Stop noise in logs if file in dir does not have nav header. - $descriptionWeight = $rec->prop('DescriptionWeight'); - $urlpath = $rec->prop('UrlPath') || ''; - $menucat = $rec->prop('MenuCat') || 'A'; # admin menu (default) - } - next if $menu ne $menucat; + if (defined $rec) + { + $heading = $rec->prop('Heading'); + $description = $rec->prop('Description'); + $headingWeight = $rec->prop('HeadingWeight') || 99999; #Stop noise in logs if file in dir does not have nav header. + $descriptionWeight = $rec->prop('DescriptionWeight'); + $urlpath = $rec->prop('UrlPath') || ''; + $menucat = $rec->prop('MenuCat') || 'A'; # admin menu (default) + } + + # Added: Check if this is an admin menu item and if user has access + if ($menucat eq 'A' && !$is_admin) { + # Skip this admin panel if user doesn't have access to it + my $has_access = 0; + my $file_no_ext = $file; + $file_no_ext =~ s/\.pm$//; # Remove .pm extension if present + foreach my $allowed_panel (@allowed_admin_panels) { + if ($file_no_ext eq lc($allowed_panel)) { + #die("Here!!$file $file_no_ext $allowed_panel "); + $has_access = 1; + last; + } + } + next if !$has_access; + } - #-------------------------------------------------- - # add heading, description and weight information to data structure - #-------------------------------------------------- + next if $menu ne $menucat; - unless (exists $nav {$heading}) - { - $nav {$heading} = { COUNT => 0, WEIGHT => 0, DESCRIPTIONS => [] }; - } + #-------------------------------------------------- + # add heading, description and weight information to data structure + #-------------------------------------------------- - $nav {$heading} {'COUNT'} ++; - $nav {$heading} {'WEIGHT'} += $headingWeight; + unless (exists $nav {$heading}) + { + $nav {$heading} = { COUNT => 0, WEIGHT => 0, DESCRIPTIONS => [] }; + } - # Check for manager panel, and assign the appropriate - # cgi-bin prefix for the links. - # Grab the last 2 directories by splitting for '/'s and - # then concatenating the last 2 - # probably a better way, but I don't know it. + $nav {$heading} {'COUNT'} ++; + $nav {$heading} {'WEIGHT'} += $headingWeight; - my $path; - if ( $files_hash{$file} eq 'ctrl') { - $path = "2"; - } elsif ( $files_hash{$file} eq 'cgim') { - $path = "/cgi-bin"; - } else { - my @filename = split /\//, $files_hash{$file}; - $path = "/$filename[scalar @filename - 2]/$filename[scalar @filename - 1]"; - }; + # Check for manager panel, and assign the appropriate + # cgi-bin prefix for the links. + # Grab the last 2 directories by splitting for '/'s and + # then concatenating the last 2 + # probably a better way, but I don't know it. - push @{ $nav {$heading} {'DESCRIPTIONS'} }, - { DESCRIPTION => $description, - WEIGHT => $descriptionWeight, - FILENAME => $urlpath ? $urlpath : "$path/$file", - CGIPATH => $path, - MENUCAT => $menucat + my $path; + if ( $files_hash{$file} eq 'ctrl') { + $path = "2"; + } elsif ( $files_hash{$file} eq 'cgim') { + $path = "/cgi-bin"; + } else { + my @filename = split /\//, $files_hash{$file}; + $path = "/$filename[scalar @filename - 2]/$filename[scalar @filename - 1]"; }; + + push @{ $nav {$heading} {'DESCRIPTIONS'} }, + { DESCRIPTION => $description, + WEIGHT => $descriptionWeight, + FILENAME => $urlpath ? $urlpath : "$path/$file", + CGIPATH => $path, + MENUCAT => $menucat + }; } return \%nav; } +sub simpleNavMerge { + #Used to merge two nav structures - used for the user and selected admin menu. + my ($class,$nav1, $nav2) = @_; + my %result = %$nav1; # Start with a copy of first nav + + # Merge in second nav + foreach my $heading (keys %$nav2) { + if (exists $result{$heading}) { + # Add counts and weights + $result{$heading}{COUNT} += $nav2->{$heading}{COUNT}; + $result{$heading}{WEIGHT} += $nav2->{$heading}{WEIGHT}; + # Append descriptions + push @{$result{$heading}{DESCRIPTIONS}}, @{$nav2->{$heading}{DESCRIPTIONS}}; + } else { + # Just copy the heading + $result{$heading} = $nav2->{$heading}; + } + } + + return \%result; +} + + sub _lang_space { @@ -925,4 +990,4 @@ sub get_reg_mask { } -1; \ No newline at end of file +1; diff --git a/root/usr/share/smanager/lib/SrvMngr_Auth.pm b/root/usr/share/smanager/lib/SrvMngr_Auth.pm new file mode 100644 index 0000000..8da58aa --- /dev/null +++ b/root/usr/share/smanager/lib/SrvMngr_Auth.pm @@ -0,0 +1,99 @@ +# Optimized SrvMngr_Auth module using stash caching and Exporter + +package SrvMngr_Auth; + +use strict; +use warnings; +use Exporter qw(import); # Import the Exporter module +use esmith::AccountsDB; + +# Define functions to be exported upon request +our @EXPORT_OK = qw(check_admin_access load_user_auth_info has_panel_access get_panel_from_path); + +# Helper function to extract panel name from path +sub get_panel_from_path { + my ($path) = @_; + + if ($path =~ m{^/([^/]+)}) { + return $1; + } + + return ''; # Return empty string if no panel found +} + +# Load user authentication info and cache it in the stash +sub load_user_auth_info { + my ($c) = @_; + + # Check if auth info is already cached in the stash + return if exists $c->stash->{auth_info}; + + my %auth_info = ( + username => '', # Initialize username + is_admin => 0, + allowed_panels => [], + ); + + # Get username from session + $auth_info{username} = $c->session->{username} || ''; # Provide default empty string + + # Check if user is admin + $auth_info{is_admin} = $c->is_admin || 0; + + # If not admin, get allowed panels + if (!$auth_info{is_admin} && $auth_info{username}) { + my $accountsdb = esmith::AccountsDB->open_ro(); + if ($accountsdb) { + my $user_rec = $accountsdb->get($auth_info{username}); + # Check if the property exists before trying to get its value + if (defined $user_rec && $user_rec->prop('AdminPanels')) { + # Get comma-separated list of allowed admin panels + my $admin_panels = $user_rec->prop('AdminPanels'); + $auth_info{allowed_panels} = [split(/,/, $admin_panels)]; + } + } + } + + # Store the calculated info in the stash + $c->stash(auth_info => \%auth_info); +} + +# Check if a user has access to a specific panel (uses cached info) +sub has_panel_access { + my ($c, $panel) = @_; + + # Ensure auth info is loaded + load_user_auth_info($c); + + my $auth_info = $c->stash->{auth_info}; + + # Check if requested panel is in allowed panels + foreach my $allowed_panel (@{$auth_info->{allowed_panels}}) { + return 1 if $panel eq lc($allowed_panel); #Controller files are capitalised, but that is lost in panel id. + } + + return 0; +} + +# Main function to check admin access (uses cached info) +sub check_admin_access { + my ($c) = @_; + + # Ensure auth info is loaded + load_user_auth_info($c); + + my $auth_info = $c->stash->{auth_info}; + + # First check if user is admin + return 1 if $auth_info->{is_admin}; + + # If not admin, check if they have access to the specific panel + my $current_path = $c->req->url->path; + my $requested_panel = $current_path; + return 0 unless $requested_panel; + + # Check if user has access to this panel using the cached info + return has_panel_access($c, $requested_panel); +} + +1; # Return true value for module loading diff --git a/root/usr/share/smanager/themes/default/templates/partials/_user_menu.html.ep b/root/usr/share/smanager/themes/default/templates/partials/_user_menu.html.ep index c5e68a0..d13c0de 100644 --- a/root/usr/share/smanager/themes/default/templates/partials/_user_menu.html.ep +++ b/root/usr/share/smanager/themes/default/templates/partials/_user_menu.html.ep @@ -1,5 +1,7 @@ -% use SrvMngr qw( getNavigation ); -% my %nav = %{SrvMngr->getNavigation( $c->languages(), 'U' )}; +% use SrvMngr qw( getNavigation simpleNavMerge ); +% my %nav1 = %{SrvMngr->getNavigation( $c->languages(), 'U' )}; +% my %nav2 = %{SrvMngr->getNavigation( $c->languages(), 'A', session('username') )}; +% my %nav = $c->session->{is_admin} ? %nav1 : %{SrvMngr->simpleNavMerge(\%nav1, \%nav2)};
Current User (<%= session 'username' %>) @@ -7,26 +9,24 @@ % my $cc = 300; % foreach my $h (sort { ($nav{$a}{'WEIGHT'}/$nav{$a}{'COUNT'}) % <=> ($nav{$b}{'WEIGHT'}/$nav{$b}{'COUNT'}) } keys %nav) { - -% my ($classNew, $target, $href) = ''; -% foreach (sort { $a->{'WEIGHT'} <=> $b->{'WEIGHT'} } @{$nav{$h}{'DESCRIPTIONS'}}) { - -% next if ($_->{'MENUCAT'} ne 'U' ); # menu User - -% if ( $_->{'FILENAME'} =~ m/^2\// ) { -% $target = '_self'; -% (my $file2 = $_->{'FILENAME'}) =~ s|^2/||; -% $href = '/smanager/' . $file2; -% } else { -% $target = 'main'; -% $href = '/server-manager' . $_->{'FILENAME'}; -% } - + +% my ($classNew, $target, $href) = ''; +% foreach (sort { $a->{'WEIGHT'} <=> $b->{'WEIGHT'} } @{$nav{$h}{'DESCRIPTIONS'}}) { +% next if ($_->{'MENUCAT'} ne 'A' && $_->{'MENUCAT'} ne 'U' ); # menu User +% if ( $_->{'FILENAME'} =~ m/^2\// ) { +% $target = '_self'; +% (my $file2 = $_->{'FILENAME'}) =~ s|^2/||; +% $href = '/smanager/' . $file2; +% } else { +% $target = 'main'; +% $href = '/server-manager' . $_->{'FILENAME'}; +% } + % $cc++; -% } +% } % }
- \ No newline at end of file + diff --git a/smeserver-manager.spec b/smeserver-manager.spec index 883c2e5..5aacbcb 100644 --- a/smeserver-manager.spec +++ b/smeserver-manager.spec @@ -2,7 +2,7 @@ Summary: Sme server navigation module : manager 2 %define name smeserver-manager Name: %{name} %define version 11.0.0 -%define release 78 +%define release 79 Version: %{version} Release: %{release}%{?dist} License: GPL @@ -143,6 +143,9 @@ true %defattr(-,root,root) %changelog +* Wed Apr 30 2025 Brian Read 11.0.0-79.sme +- Add code in SrvMngr to take note of user panel setting + * Thu Apr 17 2025 Brian Read 11.0.0-78.sme - typo in remoteaccess panel - Fix crash in veiwlogfiles if viewlogfiles key not in DB