%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2026 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <& /Admin/Elements/Header, Title => $title &> <& /Elements/Tabs &> <& /Elements/ListActions, actions => \@results &>
<&| /Widgets/TitleBox, title => $has_group_policy ? loc('Enforced MFA Policy') : loc('MFA Policy'), content_class => 'mx-auto width-sm' &> % if ( $has_group_policy ) {
<&|/l&>This group has a custom MFA policy.
% } else {
<&|/l&>This group has no custom MFA policy. Members follow policies from any other groups they belong to, or the global MFA Policy configuration if none apply.
% } <&| /Elements/LabeledValue, Label => loc('MFA Mode') &> <&| /Elements/LabeledValue, Label => loc('Drift'), LabelTooltip => loc('Number of 30-second TOTP windows to accept on each side of the current one (clock-skew tolerance). 0 = current window only, 1 = ±1 window, etc. Maximum 5.') &> <& /Elements/Submit, Name => 'Save', Label => loc('Save Changes'), FullWidth => 1 &> % if ( $has_group_policy ) { <& /Elements/Submit, Name => 'Reset', Label => loc('Clear Group Settings'), FullWidth => 1 &> % }
<%init> my @results; my $Group = RT::Group->new( $session{'CurrentUser'} ); $Group->Load($id); Abort( loc('Could not load group') ) unless $Group->Id; Abort( loc('MFA is disabled') ) if RT->Config->Get('DisableMFA'); Abort( loc('Permission Denied') ) unless $Group->CurrentUserHasRight('AdminGroup'); Abort( loc('MFA policies can only be set on user-defined groups') ) unless $Group->Domain eq 'UserDefined'; if ( $ARGS{Save} ) { # "Not set" on a group without a policy must not create one, which would # replace the global policy for its members. if ( !$Group->MFAPolicy && !length( $ARGS{Mode} // '' ) ) { push @results, loc('Nothing changed'); } else { my ( $ok, $msg ) = $Group->SetMFAPolicy( Mode => $ARGS{Mode} || 'Off', Drift => $ARGS{Drift} // 0, ); push @results, $msg; } } elsif ( $ARGS{Reset} ) { my ( $ok, $msg ) = $Group->ResetMFAPolicy; push @results, $msg; } my $has_group_policy = $Group->MFAPolicy ? 1 : 0; my $policy = $Group->MFAPolicy // { Drift => 0 }; my $title = loc( 'MFA Policy for [_1]', $Group->Name ); <%args> $id => undef