Config-based KasmVNC
This commit is contained in:
committed by
Anthony Merrill
parent
d9cf46f83e
commit
36a1ffc5e4
199
unix/KasmVNC/CliOption.pm
Normal file
199
unix/KasmVNC/CliOption.pm
Normal file
@@ -0,0 +1,199 @@
|
||||
package KasmVNC::CliOption;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use v5.10;
|
||||
use List::Util qw(first);
|
||||
use List::MoreUtils qw(all);
|
||||
use Switch;
|
||||
use Data::Dumper;
|
||||
|
||||
use KasmVNC::DataClumpValidator;
|
||||
use KasmVNC::Utils;
|
||||
|
||||
our $fetchValueSub;
|
||||
$KasmVNC::CliOption::dataClumpValidator = KasmVNC::DataClumpValidator->new();
|
||||
@KasmVNC::CliOption::isActiveCallbacks = ();
|
||||
|
||||
sub new {
|
||||
my ($class, $args) = @_;
|
||||
my $self = bless {
|
||||
name => $args->{name},
|
||||
configKeys => $args->{configKeys},
|
||||
deriveValueSub => $args->{deriveValueSub} || sub {
|
||||
my $self = shift;
|
||||
my @values = @{ listify($self->configValues()) };
|
||||
|
||||
@values = map { deriveBoolean($_) } @values;
|
||||
|
||||
join ",", @values;
|
||||
},
|
||||
isActiveSub => $args->{isActiveSub} || sub {
|
||||
my $self = shift;
|
||||
|
||||
scalar $self->configValues() > 0;
|
||||
},
|
||||
errors => []
|
||||
}, $class;
|
||||
}
|
||||
|
||||
sub activate {
|
||||
my $self = shift;
|
||||
|
||||
$self->makeKeysWithValuesAccessible();
|
||||
}
|
||||
|
||||
sub beforeIsActive {
|
||||
my $callback = shift;
|
||||
|
||||
push @KasmVNC::CliOption::isActiveCallbacks, $callback;
|
||||
}
|
||||
|
||||
sub isActiveByCallbacks {
|
||||
my $self = shift;
|
||||
|
||||
all { $_->($self) } @KasmVNC::CliOption::isActiveCallbacks;
|
||||
}
|
||||
|
||||
sub makeKeysWithValuesAccessible {
|
||||
my $self = shift;
|
||||
|
||||
foreach my $name (@{ $self->configKeyNames() }) {
|
||||
my $value = $self->fetchValue($name);
|
||||
$self->{$name} = $value if defined($value);
|
||||
}
|
||||
}
|
||||
|
||||
sub isActive {
|
||||
my $self = shift;
|
||||
|
||||
$self->isActiveByCallbacks() && $self->{isActiveSub}->($self);
|
||||
}
|
||||
|
||||
sub toString {
|
||||
my $self = shift;
|
||||
|
||||
return unless $self->isActive();
|
||||
|
||||
my $derivedValue = $self->deriveValue();
|
||||
if (defined($derivedValue)) {
|
||||
return "-$self->{name} " . "'$derivedValue'";
|
||||
}
|
||||
|
||||
"-$self->{name}";
|
||||
}
|
||||
|
||||
sub toValue {
|
||||
my $self = shift;
|
||||
|
||||
return unless $self->isActive();
|
||||
|
||||
$self->deriveValue();
|
||||
}
|
||||
|
||||
sub deriveValue {
|
||||
my $self = shift;
|
||||
|
||||
my $value = $self->{deriveValueSub}->($self);
|
||||
$self->interpolateEnvVars($value);
|
||||
}
|
||||
|
||||
sub interpolateEnvVars {
|
||||
my $self = shift;
|
||||
my $value = shift;
|
||||
|
||||
return $value unless defined($value);
|
||||
|
||||
while ($value =~ /\$\{(\w+)\}/) {
|
||||
my $envValue = $ENV{$1};
|
||||
$value =~ s/\Q$&\E/$envValue/;
|
||||
}
|
||||
|
||||
$value;
|
||||
}
|
||||
|
||||
sub errorMessages {
|
||||
my $self = shift;
|
||||
|
||||
join "\n", @{ $self->{errors} };
|
||||
}
|
||||
|
||||
# private
|
||||
|
||||
sub isValid {
|
||||
my $self = shift;
|
||||
|
||||
$self->validate() unless $self->{validated};
|
||||
|
||||
$self->isNoErrorsPresent();
|
||||
}
|
||||
|
||||
sub validate {
|
||||
my $self = shift;
|
||||
|
||||
$self->validateDataClump();
|
||||
$self->validateConfigValues();
|
||||
|
||||
$self->{validated} = 1;
|
||||
}
|
||||
|
||||
sub isNoErrorsPresent {
|
||||
my $self = shift;
|
||||
|
||||
scalar @{ $self->{errors} } == 0;
|
||||
}
|
||||
|
||||
sub validateDataClump {
|
||||
my $self = shift;
|
||||
|
||||
$KasmVNC::CliOption::dataClumpValidator->validate($self);
|
||||
}
|
||||
|
||||
sub configValues {
|
||||
my $self = shift;
|
||||
|
||||
map { $self->fetchValue($_->{name}) } @{ $self->{configKeys} };
|
||||
}
|
||||
|
||||
sub configValue {
|
||||
my $self = shift;
|
||||
|
||||
die "Multiple or no config keys defined for $self->{name}"
|
||||
if (scalar @{ $self->{configKeys} } != 1);
|
||||
|
||||
@{ listify($self->configValues()) }[0];
|
||||
}
|
||||
|
||||
sub configKeyNames {
|
||||
my $self = shift;
|
||||
|
||||
my @result = map { $_->{name} } @{ $self->{configKeys} };
|
||||
\@result;
|
||||
}
|
||||
|
||||
sub hasKey {
|
||||
my $self = shift;
|
||||
my $configKey = shift;
|
||||
|
||||
first { $_ eq $configKey } @{ $self->configKeyNames() };
|
||||
}
|
||||
|
||||
sub addErrorMessage {
|
||||
my ($self, $errorMessage) = @_;
|
||||
|
||||
push @{ $self->{errors} }, $errorMessage;
|
||||
}
|
||||
|
||||
sub validateConfigValues {
|
||||
my $self = shift;
|
||||
|
||||
map { $_->validate($self) } @{ $self->{configKeys} };
|
||||
}
|
||||
|
||||
sub fetchValue {
|
||||
my $self = shift;
|
||||
|
||||
&$fetchValueSub(shift);
|
||||
}
|
||||
|
||||
1;
|
||||
86
unix/KasmVNC/Config.pm
Normal file
86
unix/KasmVNC/Config.pm
Normal file
@@ -0,0 +1,86 @@
|
||||
package KasmVNC::Config;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use v5.10;
|
||||
use YAML::Tiny;
|
||||
use Data::Dumper;
|
||||
use Hash::Merge::Simple;
|
||||
use KasmVNC::Utils;
|
||||
|
||||
sub merge {
|
||||
my @configsToMerge = map { $_->{data} } @_;
|
||||
my $mergedConfig = Hash::Merge::Simple::merge(@configsToMerge) // {};
|
||||
|
||||
KasmVNC::Config->new({ data => $mergedConfig });
|
||||
}
|
||||
|
||||
sub new {
|
||||
my ($class, $args) = @_;
|
||||
my $self = bless {
|
||||
filename => $args->{filename},
|
||||
data => $args->{data}
|
||||
}, $class;
|
||||
|
||||
$self->load() if $self->{filename};
|
||||
$self;
|
||||
}
|
||||
|
||||
sub load {
|
||||
my $self = shift;
|
||||
|
||||
failIfConfigNotReadable($self->{filename});
|
||||
|
||||
$self->{data} = YAML::Tiny->read($self->{filename})->[0];
|
||||
}
|
||||
|
||||
sub get {
|
||||
my ($self, $absoluteKey) = @_;
|
||||
my $path = absoluteKeyToHashPath($absoluteKey);
|
||||
my $config = $self->{data};
|
||||
|
||||
my $value = eval "\$config$path";
|
||||
return unless defined($value);
|
||||
|
||||
$value;
|
||||
}
|
||||
|
||||
sub exists {
|
||||
my ($self, $absoluteKey) = @_;
|
||||
my $path = absoluteKeyToHashPath($absoluteKey);
|
||||
my $config = $self->{data};
|
||||
|
||||
eval "exists \$config$path";
|
||||
}
|
||||
|
||||
sub delete {
|
||||
my ($self, $absoluteKey) = @_;
|
||||
my $path = absoluteKeyToHashPath($absoluteKey);
|
||||
my $config = $self->{data};
|
||||
|
||||
eval "delete \$config$path";
|
||||
}
|
||||
|
||||
sub isEmpty {
|
||||
my ($self, $absoluteKey) = @_;
|
||||
my $path = absoluteKeyToHashPath($absoluteKey);
|
||||
my $config = $self->{data};
|
||||
|
||||
$self->exists($absoluteKey) && isBlank($self->get($absoluteKey));
|
||||
}
|
||||
|
||||
sub absoluteKeyToHashPath {
|
||||
my $absoluteKey = shift;
|
||||
|
||||
my @keyParts = split(/\./, $absoluteKey);
|
||||
@keyParts = map { "->{\"$_\"}" } @keyParts;
|
||||
join "", @keyParts;
|
||||
}
|
||||
|
||||
sub failIfConfigNotReadable {
|
||||
my $config = shift;
|
||||
|
||||
-r $config || die "Couldn't load config: $config";
|
||||
}
|
||||
|
||||
1;
|
||||
123
unix/KasmVNC/ConfigKey.pm
Normal file
123
unix/KasmVNC/ConfigKey.pm
Normal file
@@ -0,0 +1,123 @@
|
||||
package KasmVNC::ConfigKey;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use v5.10;
|
||||
use Switch;
|
||||
use Data::Dumper;
|
||||
|
||||
use KasmVNC::Utils;
|
||||
|
||||
our $fetchValueSub;
|
||||
|
||||
use constant {
|
||||
INT => 0,
|
||||
STRING => 1,
|
||||
BOOLEAN => 2,
|
||||
ANY => 4
|
||||
};
|
||||
|
||||
sub new {
|
||||
my ($class, $args) = @_;
|
||||
my $self = bless {
|
||||
name => $args->{name},
|
||||
type => $args->{type},
|
||||
validator => $args->{validator}
|
||||
}, $class;
|
||||
}
|
||||
|
||||
sub validate {
|
||||
my $self = shift;
|
||||
$self->{cliOption} = shift;
|
||||
|
||||
return if $self->isValueBlank();
|
||||
|
||||
if ($self->{validator}) {
|
||||
$self->resolveValidatorFromFunction() if (ref $self->{validator} eq "CODE");
|
||||
|
||||
$self->{validator}->validate($self);
|
||||
return;
|
||||
}
|
||||
|
||||
switch($self->{type}) {
|
||||
case INT {
|
||||
$self->validateInt();
|
||||
}
|
||||
case BOOLEAN {
|
||||
$self->validateBoolean();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub resolveValidatorFromFunction {
|
||||
my $self = shift;
|
||||
|
||||
$self->{validator} = $self->{validator}();
|
||||
}
|
||||
|
||||
sub addErrorMessage {
|
||||
my $self = shift;
|
||||
|
||||
my $errorMessage = $self->constructErrorMessage($_[0]);
|
||||
$self->{cliOption}->addErrorMessage($errorMessage);
|
||||
}
|
||||
|
||||
# private
|
||||
|
||||
sub validateBoolean {
|
||||
my $self = shift;
|
||||
|
||||
return if $self->isValidBoolean();
|
||||
$self->addErrorMessage("must be true or false");
|
||||
}
|
||||
|
||||
sub validateInt {
|
||||
my $self = shift;
|
||||
|
||||
return if $self->isValidInt();
|
||||
|
||||
$self->addErrorMessage("must be an integer");
|
||||
}
|
||||
|
||||
sub isValueBlank {
|
||||
my $self = shift;
|
||||
|
||||
my $value = $self->value();
|
||||
!defined($value) || $value eq "";
|
||||
}
|
||||
|
||||
sub fetchValue {
|
||||
my $self = shift;
|
||||
|
||||
&$fetchValueSub(shift);
|
||||
}
|
||||
|
||||
sub constructErrorMessage {
|
||||
my $self = shift;
|
||||
my $staticErrorMessage = shift;
|
||||
|
||||
my $name = $self->{name};
|
||||
my $value = join ", ", @{ listify($self->fetchValue($name)) };
|
||||
|
||||
"$name '$value': $staticErrorMessage";
|
||||
}
|
||||
|
||||
sub isValidInt {
|
||||
my $self = shift;
|
||||
|
||||
$self->value() =~ /^(-)?\d+$/;
|
||||
}
|
||||
|
||||
sub isValidBoolean {
|
||||
my $self = shift;
|
||||
|
||||
$self->value() =~ /^true|false$/;
|
||||
}
|
||||
|
||||
sub value {
|
||||
my $self = shift;
|
||||
|
||||
$self->fetchValue($self->{name});
|
||||
}
|
||||
|
||||
our @EXPORT_OK = ('INT', 'STRING', 'BOOLEAN');
|
||||
48
unix/KasmVNC/DataClumpValidator.pm
Normal file
48
unix/KasmVNC/DataClumpValidator.pm
Normal file
@@ -0,0 +1,48 @@
|
||||
package KasmVNC::DataClumpValidator;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use v5.10;
|
||||
use Data::Dumper;
|
||||
|
||||
sub new {
|
||||
my ($class, $args) = @_;
|
||||
my $self = bless {
|
||||
}, $class;
|
||||
}
|
||||
|
||||
sub validate {
|
||||
my $self = shift;
|
||||
$self->{cliOption} = shift;
|
||||
|
||||
if ($self->isDataClump() && !$self->isWhole()) {
|
||||
$self->{cliOption}->addErrorMessage($self->errorMessage());
|
||||
}
|
||||
}
|
||||
|
||||
# private
|
||||
|
||||
sub isWhole {
|
||||
my $self = shift;
|
||||
|
||||
my $numberOfValues = scalar $self->{cliOption}->configValues();
|
||||
return 1 if $numberOfValues == 0;
|
||||
|
||||
scalar @{ $self->{cliOption}->{configKeys} } == $numberOfValues;
|
||||
}
|
||||
|
||||
sub isDataClump {
|
||||
my $self = shift;
|
||||
|
||||
scalar(@{ $self->{cliOption}->{configKeys} }) > 1;
|
||||
}
|
||||
|
||||
sub errorMessage {
|
||||
my $self = shift;
|
||||
|
||||
my $configKeys = join ", ", @{ $self->{cliOption}->configKeyNames() };
|
||||
|
||||
"$configKeys: either all keys or none must be present";
|
||||
}
|
||||
|
||||
1;
|
||||
36
unix/KasmVNC/EnumValidator.pm
Normal file
36
unix/KasmVNC/EnumValidator.pm
Normal file
@@ -0,0 +1,36 @@
|
||||
package KasmVNC::EnumValidator;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use v5.10;
|
||||
use List::MoreUtils qw(any);
|
||||
use Data::Dumper;
|
||||
use KasmVNC::Utils;
|
||||
|
||||
sub new {
|
||||
my ($class, $args) = @_;
|
||||
my $self = bless {
|
||||
allowedValues => $args->{allowedValues}
|
||||
}, $class;
|
||||
}
|
||||
|
||||
sub validate {
|
||||
my $self = shift;
|
||||
my $configKey = shift;
|
||||
my @values = @{ listify($configKey->value()) };
|
||||
|
||||
foreach my $value (@values) {
|
||||
unless (any { $_ eq $value } @{ $self->{allowedValues} }) {
|
||||
$configKey->addErrorMessage($self->errorMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub errorMessage {
|
||||
my $self = shift;
|
||||
|
||||
my $allowedValuesText = join ", ", @{ $self->{allowedValues} };
|
||||
"must be one of [$allowedValuesText]"
|
||||
}
|
||||
|
||||
1;
|
||||
20
unix/KasmVNC/Logger.pm
Normal file
20
unix/KasmVNC/Logger.pm
Normal file
@@ -0,0 +1,20 @@
|
||||
package KasmVNC::Logger;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use v5.10;
|
||||
use Data::Dumper;
|
||||
|
||||
sub new {
|
||||
my ($class, $args) = @_;
|
||||
my $self = bless {
|
||||
}, $class;
|
||||
}
|
||||
|
||||
sub warn {
|
||||
my $self = shift;
|
||||
|
||||
say { *STDERR } @_;
|
||||
}
|
||||
|
||||
1;
|
||||
37
unix/KasmVNC/PatternValidator.pm
Normal file
37
unix/KasmVNC/PatternValidator.pm
Normal file
@@ -0,0 +1,37 @@
|
||||
package KasmVNC::PatternValidator;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use v5.10;
|
||||
use Data::Dumper;
|
||||
|
||||
use KasmVNC::Utils;
|
||||
|
||||
sub new {
|
||||
my ($class, $args) = @_;
|
||||
my $self = bless {
|
||||
pattern => $args->{pattern},
|
||||
errorMessage => $args->{errorMessage}
|
||||
}, $class;
|
||||
}
|
||||
|
||||
sub validate {
|
||||
my $self = shift;
|
||||
$self->{configKey} = shift;
|
||||
my @values = @{ listify($self->{configKey}->value()) };
|
||||
|
||||
foreach my $value (@values) {
|
||||
$self->validateValue($value);
|
||||
}
|
||||
}
|
||||
|
||||
sub validateValue {
|
||||
my $self = shift;
|
||||
my $value = shift;
|
||||
|
||||
unless ($value =~ $self->{pattern}) {
|
||||
$self->{configKey}->addErrorMessage($self->{errorMessage});
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
40
unix/KasmVNC/TextOption.pm
Normal file
40
unix/KasmVNC/TextOption.pm
Normal file
@@ -0,0 +1,40 @@
|
||||
package KasmVNC::TextOption;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use v5.10;
|
||||
use Data::Dumper;
|
||||
|
||||
sub new {
|
||||
my ($class, $args) = @_;
|
||||
my $self = bless {
|
||||
description => $args->{description},
|
||||
callback => $args->{callback} || sub {},
|
||||
}, $class;
|
||||
}
|
||||
|
||||
use overload fallback => 1, q("") => sub {
|
||||
my $self = shift;
|
||||
|
||||
$self->stringify();
|
||||
};
|
||||
|
||||
sub stringify {
|
||||
my $self = shift;
|
||||
|
||||
$self->{description};
|
||||
}
|
||||
|
||||
sub description {
|
||||
my $self = shift;
|
||||
|
||||
$self->{description};
|
||||
}
|
||||
|
||||
sub callback {
|
||||
my $self = shift;
|
||||
|
||||
$self->{callback};
|
||||
}
|
||||
|
||||
1;
|
||||
55
unix/KasmVNC/TextUI.pm
Normal file
55
unix/KasmVNC/TextUI.pm
Normal file
@@ -0,0 +1,55 @@
|
||||
package KasmVNC::TextUI;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use v5.10;
|
||||
use Data::Dumper;
|
||||
|
||||
@KasmVNC::TextUI::ISA = qw(Exporter);
|
||||
|
||||
our @EXPORT = ('Prompt', 'askUserToChooseOption');
|
||||
|
||||
sub askUserToChooseOption {
|
||||
my %args = @_;
|
||||
my $banner = $args{banner};
|
||||
my $prompt = $args{prompt};
|
||||
my $options = $args{options};
|
||||
|
||||
my $userInput;
|
||||
my $i = 1;
|
||||
my %numberedOptions = map { $i++ => $_ } @$options;
|
||||
|
||||
while (1) {
|
||||
say $banner;
|
||||
|
||||
printOptions(\%numberedOptions);
|
||||
|
||||
$userInput = Prompt($prompt . ": ");
|
||||
last if $numberedOptions{$userInput};
|
||||
|
||||
say "Invalid choice: '$userInput'";
|
||||
}
|
||||
|
||||
$numberedOptions{$userInput};
|
||||
}
|
||||
|
||||
sub printOptions {
|
||||
my $choices = shift;
|
||||
|
||||
foreach my $choiceNumber (sort keys %$choices) {
|
||||
say "[$choiceNumber] " . $choices->{$choiceNumber};
|
||||
}
|
||||
print "\n";
|
||||
}
|
||||
|
||||
sub Prompt {
|
||||
my $prompt = shift;
|
||||
|
||||
print($prompt);
|
||||
my $userInput = <STDIN>;
|
||||
$userInput =~ s/^\s+|\s+$//g;
|
||||
|
||||
return $userInput;
|
||||
}
|
||||
|
||||
1;
|
||||
54
unix/KasmVNC/User.pm
Normal file
54
unix/KasmVNC/User.pm
Normal file
@@ -0,0 +1,54 @@
|
||||
package KasmVNC::User;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use v5.10;
|
||||
|
||||
sub new {
|
||||
my ($class, $args) = @_;
|
||||
|
||||
my $self = bless {
|
||||
name => $args->{name},
|
||||
permissions => $args->{permissions}
|
||||
}, $class;
|
||||
}
|
||||
|
||||
sub permissionsExplanation {
|
||||
my $self = shift;
|
||||
|
||||
my %permissionExplanations = ("w" => "can use keyboard and mouse",
|
||||
"o" => "can add/remove users",
|
||||
"" => "can only view");
|
||||
foreach (qw(ow wo)) {
|
||||
$permissionExplanations{$_} = "can use keyboard and mouse, add/remove users";
|
||||
}
|
||||
|
||||
$self->{permissions} =~ s/r//g;
|
||||
$permissionExplanations{$self->{permissions}};
|
||||
}
|
||||
|
||||
sub name {
|
||||
my $self = shift;
|
||||
|
||||
$self->{name};
|
||||
}
|
||||
|
||||
sub permissions {
|
||||
my $self = shift;
|
||||
|
||||
$self->{permissions};
|
||||
}
|
||||
|
||||
sub isOwner {
|
||||
my $self = shift;
|
||||
|
||||
$self->permissions() =~ /o/;
|
||||
}
|
||||
|
||||
sub toString {
|
||||
my $self = shift;
|
||||
|
||||
$self->name() . " (" . $self->permissionsExplanation() . ")";
|
||||
}
|
||||
|
||||
1;
|
||||
169
unix/KasmVNC/Users.pm
Normal file
169
unix/KasmVNC/Users.pm
Normal file
@@ -0,0 +1,169 @@
|
||||
package KasmVNC::Users;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use v5.10;
|
||||
use Data::Dumper;
|
||||
use List::MoreUtils qw(any);
|
||||
use KasmVNC::User;
|
||||
|
||||
our $vncPasswdBin;
|
||||
our $logger;
|
||||
|
||||
sub new {
|
||||
my ($class, $args) = @_;
|
||||
my $self = bless {
|
||||
passwordFileName => $args->{passwordFileName},
|
||||
}, $class;
|
||||
}
|
||||
|
||||
sub loadFrom {
|
||||
my ($self, $passwordFileName) = @_;
|
||||
|
||||
my $users = KasmVNC::Users->new({
|
||||
passwordFileName => $passwordFileName,
|
||||
vncPasswdBin => $vncPasswdBin
|
||||
});
|
||||
$users->load();
|
||||
|
||||
$users;
|
||||
}
|
||||
|
||||
sub optionsToCliOptions {
|
||||
my %options = @_;
|
||||
my @cliOptons = ();
|
||||
|
||||
push(@cliOptons, "-u \"@{[$options{username}]}\"");
|
||||
if ($options{permissions}) {
|
||||
push(@cliOptons, "-" . $options{permissions});
|
||||
}
|
||||
if ($options{changePermissions}) {
|
||||
push(@cliOptons, "-n");
|
||||
}
|
||||
|
||||
join " ", @cliOptons;
|
||||
}
|
||||
|
||||
sub runKasmvncpasswd {
|
||||
my ($self, $options) = @_;
|
||||
my @cliOptions = optionsToCliOptions(%{ $options });
|
||||
|
||||
system("$vncPasswdBin " . join(" ", @cliOptions) . " " . $self->{passwordFileName});
|
||||
$? ? 0 : 1;
|
||||
}
|
||||
|
||||
sub findByPermissions {
|
||||
my ($self, $permissions) = @_;
|
||||
|
||||
any { $_->{permissions} =~ /$permissions/ }
|
||||
(values %{ $self->{store} });
|
||||
}
|
||||
|
||||
sub fetchUser {
|
||||
my ($self, $username) = @_;
|
||||
|
||||
$self->{store}->{$username};
|
||||
}
|
||||
|
||||
sub userExists {
|
||||
fetchUser @_;
|
||||
}
|
||||
|
||||
sub addUser {
|
||||
my ($self, $username, $permissions) = @_;
|
||||
|
||||
if ($self->userExists($username)) {
|
||||
$logger->warn("User $username already exists");
|
||||
return;
|
||||
}
|
||||
|
||||
$self->runKasmvncpasswd({ username => $username, permissions => $permissions });
|
||||
}
|
||||
|
||||
sub checkUserExists {
|
||||
my ($self, $username) = @_;
|
||||
|
||||
unless ($self->fetchUser($username)) {
|
||||
die "User \"$username\" doesn't exist";
|
||||
}
|
||||
}
|
||||
|
||||
sub addPermissions {
|
||||
my ($self, $username, $permissions) = @_;
|
||||
|
||||
$self->checkUserExists($username);
|
||||
|
||||
my $user = $self->fetchUser($username);
|
||||
$permissions .= $user->{permissions};
|
||||
|
||||
$self->changePermissions($username, $permissions);
|
||||
}
|
||||
|
||||
sub changePermissions {
|
||||
my ($self, $username, $permissions) = @_;
|
||||
|
||||
$self->checkUserExists($username);
|
||||
|
||||
$self->runKasmvncpasswd({ username => $username, permissions => $permissions,
|
||||
changePermissions => 1 });
|
||||
}
|
||||
|
||||
sub load {
|
||||
my $self = shift;
|
||||
|
||||
$self->{store} = $self->_load();
|
||||
}
|
||||
|
||||
sub reload {
|
||||
my $self = shift;
|
||||
|
||||
$self->load();
|
||||
}
|
||||
|
||||
sub count {
|
||||
my $self = shift;
|
||||
|
||||
return scalar(keys %{ $self->{store} });
|
||||
}
|
||||
|
||||
sub is_empty {
|
||||
my $self = shift;
|
||||
|
||||
$self->count() eq 0;
|
||||
}
|
||||
|
||||
sub _load {
|
||||
my $self = shift;
|
||||
|
||||
my $store = {};
|
||||
|
||||
open(FH, '<', $self->{passwordFileName}) or return $store;
|
||||
|
||||
while(<FH>){
|
||||
chomp $_;
|
||||
my ($name, $__, $permissions) = split(':', $_);
|
||||
$store->{$name} = KasmVNC::User->new({
|
||||
name => $name,
|
||||
permissions => $permissions
|
||||
})
|
||||
}
|
||||
|
||||
close(FH);
|
||||
|
||||
$store;
|
||||
}
|
||||
|
||||
sub users {
|
||||
my $self = shift;
|
||||
|
||||
values %{ $self->{store} }
|
||||
}
|
||||
|
||||
sub toString {
|
||||
my $self = shift;
|
||||
|
||||
my @userDescriptions = map { $_->toString() } $self->users();
|
||||
join "\n", @userDescriptions;
|
||||
}
|
||||
|
||||
1;
|
||||
66
unix/KasmVNC/Utils.pm
Normal file
66
unix/KasmVNC/Utils.pm
Normal file
@@ -0,0 +1,66 @@
|
||||
package KasmVNC::Utils;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use v5.10;
|
||||
use Data::Dumper;
|
||||
use Switch;
|
||||
|
||||
use Exporter;
|
||||
|
||||
@KasmVNC::Utils::ISA = qw(Exporter);
|
||||
|
||||
our @EXPORT = ('listify', 'flatten', 'isBlank', 'isPresent', 'deriveBoolean',
|
||||
'printStackTrace');
|
||||
|
||||
sub listify {
|
||||
# Implementation based on Hyper::Functions
|
||||
if (scalar @_ > 1) {
|
||||
return [ @_ ];
|
||||
} elsif (defined $_[0]) {
|
||||
my $ref_type = ref $_[0];
|
||||
return ($ref_type && $ref_type eq 'ARRAY') ? $_[0] : [ $_[0] ];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
sub flatten {
|
||||
map { ref $_ ? flatten(@{$_}) : $_ } @_;
|
||||
}
|
||||
|
||||
sub isBlank {
|
||||
!isPresent(shift);
|
||||
}
|
||||
|
||||
sub isPresent {
|
||||
my $value = shift;
|
||||
if (ref($value) eq "HASH") {
|
||||
return scalar(keys %$value) > 0;
|
||||
}
|
||||
|
||||
defined($value);
|
||||
}
|
||||
|
||||
sub deriveBoolean {
|
||||
my $value = shift;
|
||||
|
||||
switch($value) {
|
||||
case 'true' {
|
||||
return 1;
|
||||
}
|
||||
case 'false' {
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub printStackTrace {
|
||||
my $trace = Devel::StackTrace->new;
|
||||
print { *STDERR } $trace->as_string;
|
||||
}
|
||||
|
||||
1;
|
||||
0
unix/kasmvnc.yaml
Normal file
0
unix/kasmvnc.yaml
Normal file
131
unix/kasmvnc_defaults.yaml
Normal file
131
unix/kasmvnc_defaults.yaml
Normal file
@@ -0,0 +1,131 @@
|
||||
---
|
||||
desktop:
|
||||
resolution:
|
||||
width: 1024
|
||||
height: 768
|
||||
allow_resize: true
|
||||
pixel_depth: 24
|
||||
|
||||
network:
|
||||
protocol: http
|
||||
interface: 0.0.0.0
|
||||
websocket_port: auto
|
||||
use_ipv4: true
|
||||
use_ipv6: true
|
||||
udp:
|
||||
public_ip: auto
|
||||
port: auto
|
||||
ssl:
|
||||
pem_certificate: /etc/ssl/certs/ssl-cert-snakeoil.pem
|
||||
pem_key: /etc/ssl/private/ssl-cert-snakeoil.key
|
||||
require_ssl: true
|
||||
|
||||
user_session:
|
||||
# session_type: shared
|
||||
new_session_disconnects_existing_exclusive_session: false
|
||||
concurrent_connections_prompt: false
|
||||
concurrent_connections_prompt_timeout: 10
|
||||
idle_timeout: never
|
||||
|
||||
keyboard:
|
||||
remap_keys:
|
||||
# - 0x22->0x40
|
||||
ignore_numlock: false
|
||||
raw_keyboard: false
|
||||
|
||||
# Mouse, trackpad, etc.
|
||||
pointer:
|
||||
enabled: true
|
||||
|
||||
runtime_configuration:
|
||||
allow_client_to_override_kasm_server_settings: true
|
||||
allow_override_standard_vnc_server_settings: true
|
||||
allow_override_list:
|
||||
- pointer.enabled
|
||||
- data_loss_prevention.clipboard.server_to_client.enabled
|
||||
- data_loss_prevention.clipboard.client_to_server.enabled
|
||||
- data_loss_prevention.clipboard.server_to_client.primary_clipboard_enabled
|
||||
|
||||
logging:
|
||||
log_writer_name: all
|
||||
log_dest: logfile
|
||||
# 0 - minimal verbosity, 100 - most verbose
|
||||
level: 30
|
||||
|
||||
security:
|
||||
brute_force_protection:
|
||||
blacklist_threshold: 5
|
||||
blacklist_timeout: 10
|
||||
|
||||
data_loss_prevention:
|
||||
visible_region:
|
||||
# top: 10
|
||||
# left: 10
|
||||
# right: 40
|
||||
# bottom: 40
|
||||
concealed_region:
|
||||
allow_click_down: false
|
||||
allow_click_release: false
|
||||
clipboard:
|
||||
delay_between_operations: none
|
||||
allow_mimetypes:
|
||||
- chromium/x-web-custom-data
|
||||
- text/html
|
||||
- image/png
|
||||
# Add to docs: Cut buffers and CLIPBOARD selection.
|
||||
server_to_client:
|
||||
enabled: false
|
||||
size: unlimited
|
||||
primary_clipboard_enabled: false
|
||||
client_to_server:
|
||||
enabled: false
|
||||
size: unlimited
|
||||
keyboard:
|
||||
enabled: true
|
||||
rate_limit: unlimited
|
||||
# "verbose" SETTING LOGS YOUR PRIVATE INFORMATION. Keypresses and clipboard
|
||||
# content.
|
||||
logging:
|
||||
level: off
|
||||
|
||||
encoding:
|
||||
max_frame_rate: 60
|
||||
full_frame_updates: none
|
||||
rect_encoding_mode:
|
||||
min_quality: 7
|
||||
max_quality: 8
|
||||
consider_lossless_quality: 10
|
||||
rectangle_compress_threads: auto
|
||||
|
||||
video_encoding_mode:
|
||||
jpeg_quality: -1
|
||||
webp_quality: -1
|
||||
max_resolution:
|
||||
width: 1920
|
||||
height: 1080
|
||||
enter_video_encoding_mode:
|
||||
time_threshold: 5
|
||||
area_threshold: 45%
|
||||
exit_video_encoding_mode:
|
||||
time_threshold: 3
|
||||
logging:
|
||||
level: off
|
||||
scaling_algorithm: progressive_bilinear
|
||||
|
||||
compare_framebuffer: auto
|
||||
zrle_zlib_level: auto
|
||||
hextile_improved_compression: true
|
||||
|
||||
server:
|
||||
advanced:
|
||||
x_font_path: auto
|
||||
httpd_directory: /usr/share/kasmvnc/www
|
||||
kasm_password_file: ${HOME}/.kasmpasswd
|
||||
x_authority_file: auto
|
||||
auto_shutdown:
|
||||
no_user_session_timeout: never
|
||||
active_user_session_timeout: never
|
||||
inactive_user_session_timeout: never
|
||||
|
||||
command_line:
|
||||
prompt: true
|
||||
3261
unix/vncserver
Normal file → Executable file
3261
unix/vncserver
Normal file → Executable file
File diff suppressed because it is too large
Load Diff
@@ -4,8 +4,6 @@ vncserver \- start or stop a VNC server
|
||||
.SH SYNOPSIS
|
||||
.B vncserver
|
||||
.RI [: display# ]
|
||||
.RB [ \-name
|
||||
.IR desktop-name ]
|
||||
.RB [ \-geometry
|
||||
.IR width x height ]
|
||||
.RB [ \-depth
|
||||
@@ -17,7 +15,7 @@ vncserver \- start or stop a VNC server
|
||||
.RB [ \-fg ]
|
||||
.RB [ \-autokill ]
|
||||
.RB [ \-noxstartup ]
|
||||
.RB [ \-xstartup
|
||||
.RB [ \-xstartup
|
||||
.IR script ]
|
||||
.RI [ Xvnc-options... ]
|
||||
.br
|
||||
@@ -53,16 +51,15 @@ In addition to the options listed below, any unrecognised options will be
|
||||
passed to Xvnc - see the Xvnc man page, or "Xvnc \-help", for details.
|
||||
|
||||
.TP
|
||||
.B \-name \fIdesktop-name\fP
|
||||
Each VNC desktop has a name which may be displayed by the viewer. The desktop
|
||||
name defaults to "\fIhost\fP:\fIdisplay#\fP (\fIusername\fP)", but you can
|
||||
change it with this option. The desktop name option is passed to the xstartup
|
||||
script via the $VNCDESKTOP environment variable, which allows you to run a
|
||||
different set of applications depending on the name of the desktop.
|
||||
.
|
||||
.B \-select-de [\fIde_name\fP]
|
||||
Select Desktop Enviromnent to run. Cinnamon, Mate, LXDE, LXQT, KDE, Gnome, XFCE
|
||||
are supported. If \fIde_name\fP isn't specified, a text UI prompt to select a
|
||||
Desktop Enviromnent will be shown.
|
||||
Warning: $HOME/.vnc/xstartup will be overwritten.
|
||||
|
||||
.TP
|
||||
.B \-geometry \fIwidth\fPx\fIheight\fP
|
||||
Specify the size of the VNC desktop to be created. Default is 1024x768.
|
||||
Specify the size of the VNC desktop to be created. Default is 1024x768.
|
||||
.
|
||||
.TP
|
||||
.B \-depth \fIdepth\fP
|
||||
@@ -138,45 +135,28 @@ Xvnc. This is useful to run full-screen applications.
|
||||
.TP
|
||||
.B \-list
|
||||
Lists all VNC desktops started by vncserver.
|
||||
.TP
|
||||
.B \-dry-run
|
||||
Print full command VNC server would be run with and exit. Won't run VNC server.
|
||||
|
||||
.SH FILES
|
||||
Several VNC-related files are found in the directory $HOME/.vnc:
|
||||
.TP
|
||||
/etc/kasmvnc/kasmvnc.yaml
|
||||
System-wide KasmVNC config. By default, all settings are commented out. The
|
||||
commented out settings are the defaults used. Uncomment if you want to change
|
||||
them. Otherwise, there's no need.
|
||||
.TP
|
||||
$HOME/.vnc/kasmvnc.yaml
|
||||
An optional user-level server config. Settings here override the system-wide
|
||||
config.
|
||||
.TP
|
||||
$HOME/.vnc/xstartup
|
||||
A shell script specifying X applications to be run when a VNC desktop is
|
||||
started. If this file does not exist, then vncserver will create a default
|
||||
xstartup script which attempts to launch your chosen window manager.
|
||||
.TP
|
||||
/etc/kasmvnc/vncserver-config-defaults
|
||||
The optional system-wide equivalent of $HOME/.vnc/config. If this file exists
|
||||
and defines options to be passed to Xvnc, they will be used as defaults for
|
||||
users. The user's $HOME/.vnc/config overrides settings configured in this file.
|
||||
The overall configuration file load order is: this file, $HOME/.vnc/config,
|
||||
and then /etc/kasmvnc/vncserver-config-mandatory. None are required to exist.
|
||||
.TP
|
||||
/etc/kasmvnc/vncserver-config-mandatory
|
||||
The optional system-wide equivalent of $HOME/.vnc/config. If this file exists
|
||||
and defines options to be passed to Xvnc, they will override any of the same
|
||||
options defined in a user's $HOME/.vnc/config. This file offers a mechanism
|
||||
to establish some basic form of system-wide policy. WARNING! There is
|
||||
nothing stopping users from constructing their own vncserver-like script
|
||||
that calls Xvnc directly to bypass any options defined in
|
||||
/etc/kasmvnc/vncserver-config-mandatory. Likewise, any CLI arguments passed
|
||||
to vncserver will override ANY config file setting of the same name. The
|
||||
overall configuration file load order is:
|
||||
/etc/kasmvnc/vncserver-config-defaults, $HOME/.vnc/config, and then this file.
|
||||
None are required to exist.
|
||||
.TP
|
||||
$HOME/.vnc/config
|
||||
An optional server config file wherein options to be passed to Xvnc are listed
|
||||
to avoid hard-coding them to the physical invocation. List options in this file
|
||||
one per line. For those requiring an argument, simply separate the option from
|
||||
the argument with an equal sign, for example: "geometry=2000x1200". Options
|
||||
without an argument are simply listed as a single word, for example: "localhost"
|
||||
or "alwaysshared".
|
||||
.TP
|
||||
$HOME/.vnc/passwd
|
||||
The VNC password file.
|
||||
$HOME/.kasmpasswd
|
||||
The KasmVNC password file.
|
||||
.TP
|
||||
$HOME/.vnc/\fIhost\fP:\fIdisplay#\fP.log
|
||||
The log file for Xvnc and applications started in xstartup.
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
.TH Xvnc 1 "" "KasmVNC" "Virtual Network Computing"
|
||||
.SH NAME
|
||||
Xvnc \- the X VNC server
|
||||
Xvnc \- the X VNC server
|
||||
.SH SYNOPSIS
|
||||
.B Xvnc
|
||||
.RI [ options ]
|
||||
.RI [ options ]
|
||||
.RI : display#
|
||||
.SH DESCRIPTION
|
||||
.B Xvnc
|
||||
@@ -50,7 +50,7 @@ depth 16 is RGB565 and for depth 24 and 32 is RGB888.
|
||||
Listen on interface. By default Xvnc listens on all available interfaces.
|
||||
.
|
||||
.TP
|
||||
.B \-inetd
|
||||
.B \-inetd
|
||||
This significantly changes Xvnc's behaviour so that it can be launched from
|
||||
inetd. See the section below on usage with inetd.
|
||||
.
|
||||
@@ -145,11 +145,6 @@ Which port to use for UDP. Default same as websocket.
|
||||
Accept clipboard updates from clients. Default is on.
|
||||
.
|
||||
.TP
|
||||
.B \-MaxCutText \fIbytes\fP
|
||||
The maximum size of a clipboard update that will be accepted from a client.
|
||||
Default is \fB262144\fP.
|
||||
.
|
||||
.TP
|
||||
.B \-SendCutText
|
||||
Send clipboard changes to clients. Default is on.
|
||||
.
|
||||
|
||||
Reference in New Issue
Block a user