mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-04-26 16:19:32 +00:00
96_allowed.pm: Authorization/Authentication modularized
git-svn-id: https://svn.fhem.de/fhem/trunk@10298 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
a799cabab4
commit
6d26b4c934
10
fhem/CHANGED
10
fhem/CHANGED
@ -1,12 +1,14 @@
|
||||
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
|
||||
# Do not insert empty lines here, update check depends on it.
|
||||
- feature: allowed module added. allowedCommands, basicAuth, password,
|
||||
globalpassword attributes moved to this module.
|
||||
- bugfix: YAMAHA_AVR: fixing not working volumeStraight set command
|
||||
- change: FB_CALLLIST: start call processing only when "event:" is triggered
|
||||
by corresponding FB_CALLMONITOR device.
|
||||
- change: FB_CALLMONITOR: default value for attr fritzbox-remote-phonebook-via
|
||||
has been changed to "tr064" (previous: "web") to ensure
|
||||
no problems with new Fritz!OS web layout.
|
||||
Besides TR-064 is official supported by AVM.
|
||||
- change: FB_CALLMONITOR: default value for attr
|
||||
fritzbox-remote-phonebook-via has been changed to "tr064"
|
||||
(previous: "web") to ensure no problems with new Fritz!OS web
|
||||
layout. Besides TR-064 is official supported by AVM.
|
||||
- feature: PHILIPS_AUDIO: Favorite station selection
|
||||
- feature: YAMAHA_AVR: new set commands and readings for tuner control
|
||||
- new set commands tunerFrequency,tunerPreset for
|
||||
|
@ -218,10 +218,22 @@ FW_SecurityCheck($$)
|
||||
!grep(m/^INITIALIZED$/, @{$dev->{CHANGED}}));
|
||||
my $motd = AttrVal("global", "motd", "");
|
||||
if($motd =~ "^SecurityCheck") {
|
||||
my @list = grep { !AttrVal($_, "basicAuth", undef) }
|
||||
devspec2array("TYPE=FHEMWEB");
|
||||
$motd .= (join(",", sort @list)." has no basicAuth attribute.\n")
|
||||
if(@list);
|
||||
my @list1 = devspec2array("TYPE=FHEMWEB");
|
||||
my @list2 = devspec2array("TYPE=allowed");
|
||||
my @list3;
|
||||
for my $l (@list1) { # This is a hack, as hardcoded to basicAuth
|
||||
next if(!$defs{$l});
|
||||
my $fnd = 0;
|
||||
for my $a (@list2) {
|
||||
next if(!$defs{$a});
|
||||
my $vf = AttrVal($a, "validFor","");
|
||||
$fnd = 1 if((!$vf || $vf =~ m/\b$l\b/) && AttrVal($a, "basicAuth",""));
|
||||
}
|
||||
push @list3, $l if(!$fnd);
|
||||
}
|
||||
$motd .= (join(",", sort @list3).
|
||||
" has no associated allowed device with basicAuth.\n")
|
||||
if(@list3);
|
||||
$attr{global}{motd} = $motd;
|
||||
}
|
||||
$modules{FHEMWEB}{NotifyFn}= "FW_Notify";
|
||||
@ -362,44 +374,24 @@ FW_Read($$)
|
||||
|
||||
|
||||
#############################
|
||||
# BASIC HTTP AUTH
|
||||
my @headerOptions = grep /OPTIONS/, @FW_httpheader; # Need example
|
||||
my $basicAuth = AttrVal($FW_wname, "basicAuth", undef);
|
||||
if($basicAuth) {
|
||||
my $secret = $FW_httpheader{Authorization};
|
||||
$secret =~ s/^Basic //i if($secret);
|
||||
my $pwok = ($secret && $secret eq $basicAuth);
|
||||
if($secret && $basicAuth =~ m/^{.*}$/ || $headerOptions[0]) {
|
||||
eval "use MIME::Base64";
|
||||
if($@) {
|
||||
Log3 $FW_wname, 1, $@;
|
||||
# AUTH
|
||||
if(!defined($FW_chash->{Authenticated})) {
|
||||
my $ret = Authenticate($FW_chash, \%FW_httpheader);
|
||||
if($ret == 0) {
|
||||
$FW_chash->{Authenticated} = 0; # not needed
|
||||
|
||||
} elsif($ret == 1) {
|
||||
$FW_chash->{Authenticated} = 1; # ok
|
||||
|
||||
} else {
|
||||
my ($user, $password) = split(":", decode_base64($secret));
|
||||
$pwok = eval $basicAuth;
|
||||
Log3 $FW_wname, 1, "basicAuth expression: $@" if($@);
|
||||
}
|
||||
}
|
||||
if($headerOptions[0]) {
|
||||
TcpServer_WriteBlocking($hash,
|
||||
"HTTP/1.1 200 OK\r\n".
|
||||
$FW_chash->{".httpAuthHeader"}.
|
||||
$FW_headercors.
|
||||
"Content-Length: 0\r\n\r\n");
|
||||
delete $hash->{CONTENT_LENGTH};
|
||||
FW_Read($hash, 1) if($hash->{BUF});
|
||||
return;
|
||||
};
|
||||
if(!$pwok) {
|
||||
my $msg = AttrVal($FW_wname, "basicAuthMsg", "Fhem: login required");
|
||||
TcpServer_WriteBlocking($hash,
|
||||
"HTTP/1.1 401 Authorization Required\r\n".
|
||||
"WWW-Authenticate: Basic realm=\"$msg\"\r\n".
|
||||
$FW_headercors.
|
||||
"Content-Length: 0\r\n\r\n");
|
||||
delete $hash->{CONTENT_LENGTH};
|
||||
FW_Read($hash, 1) if($hash->{BUF});
|
||||
return;
|
||||
};
|
||||
}
|
||||
}
|
||||
#############################
|
||||
|
||||
@ -1847,8 +1839,7 @@ FW_style($$)
|
||||
my ($cmd, $msg) = @_;
|
||||
my @a = split(" ", $cmd);
|
||||
|
||||
my $ac = AttrVal($FW_wname,"allowedCommands","");
|
||||
return if($ac && $ac !~ m/\b$a[0]\b/);
|
||||
return if(!Authorized($FW_chash, "cmd", $a[0]));
|
||||
|
||||
my $start = "<div id=\"content\"><table><tr><td>";
|
||||
my $end = "</td></tr></table></div>";
|
||||
@ -2173,11 +2164,9 @@ FW_fC($@)
|
||||
my ($cmd, $unique) = @_;
|
||||
my $ret;
|
||||
if($unique) {
|
||||
$ret = AnalyzeCommand($FW_chash, $cmd,
|
||||
AttrVal($FW_wname,"allowedCommands",undef));
|
||||
$ret = AnalyzeCommand($FW_chash, $cmd);
|
||||
} else {
|
||||
$ret = AnalyzeCommandChain($FW_chash, $cmd,
|
||||
AttrVal($FW_wname,"allowedCommands",undef));
|
||||
$ret = AnalyzeCommandChain($FW_chash, $cmd);
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
@ -2185,52 +2174,65 @@ FW_fC($@)
|
||||
sub
|
||||
FW_Attr(@)
|
||||
{
|
||||
my @a = @_;
|
||||
my $hash = $defs{$a[1]};
|
||||
my $name = $hash->{NAME};
|
||||
my ($type, $devName, $attrName, @param) = @_;
|
||||
my $hash = $defs{$devName};
|
||||
my $sP = "stylesheetPrefix";
|
||||
my $retMsg;
|
||||
|
||||
if($a[0] eq "set" && $a[2] eq "HTTPS") {
|
||||
if($type eq "set" && $attrName eq "HTTPS") {
|
||||
TcpServer_SetSSL($hash);
|
||||
}
|
||||
|
||||
if($a[0] eq "set") { # Converting styles
|
||||
if($a[2] eq "smallscreen" || $a[2] eq "touchpad") {
|
||||
$attr{$name}{$sP} = $a[2];
|
||||
$retMsg="$name: attribute $a[2] deprecated, converted to $sP";
|
||||
$a[3] = $a[2]; $a[2] = $sP;
|
||||
}
|
||||
}
|
||||
if($a[2] eq $sP) {
|
||||
# AttrFn is called too early, we have to set/del the attr here
|
||||
if($a[0] eq "set") {
|
||||
$attr{$name}{$sP} = (defined($a[3]) ? $a[3] : "default");
|
||||
FW_readIcons($attr{$name}{$sP});
|
||||
} else {
|
||||
delete $attr{$name}{$sP};
|
||||
if($type eq "set") { # Converting styles
|
||||
if($attrName eq "smallscreen" || $attrName eq "touchpad") {
|
||||
$attr{$devName}{$sP} = $attrName;
|
||||
$retMsg="$devName: attribute $attrName deprecated, converted to $sP";
|
||||
$param[0] = $attrName; $attrName = $sP;
|
||||
}
|
||||
}
|
||||
|
||||
if($a[2] eq "iconPath" && $a[0] eq "set") {
|
||||
foreach my $pe (split(":", $a[3])) {
|
||||
if($attrName eq $sP) {
|
||||
# AttrFn is called too early, we have to set/del the attr here
|
||||
if($type eq "set") {
|
||||
$attr{$devName}{$sP} = (defined($param[0]) ? $param[0] : "default");
|
||||
FW_readIcons($attr{$devName}{$sP});
|
||||
} else {
|
||||
delete $attr{$devName}{$sP};
|
||||
}
|
||||
}
|
||||
|
||||
if(($attrName eq "allowedCommands" ||
|
||||
$attrName eq "basicAuth" ||
|
||||
$attrName eq "basicAuthMsg")
|
||||
&& $type eq "set") {
|
||||
my $aName = "allowed_$devName";
|
||||
my $exists = ($defs{$aName} ? 1 : 0);
|
||||
AnalyzeCommand(undef, "defmod $aName allowed");
|
||||
AnalyzeCommand(undef, "attr $aName validFor $devName");
|
||||
AnalyzeCommand(undef, "attr $aName $attrName ".join(" ",@param));
|
||||
return "$devName: ".($exists ? "modifying":"creating").
|
||||
" device $aName for attribute $attrName";
|
||||
}
|
||||
|
||||
if($attrName eq "iconPath" && $type eq "set") {
|
||||
foreach my $pe (split(":", $param[0])) {
|
||||
$pe =~ s+\.\.++g;
|
||||
FW_readIcons($pe);
|
||||
}
|
||||
}
|
||||
|
||||
if($a[2] eq "JavaScripts" && $a[0] eq "set") { # create some attributes
|
||||
if($attrName eq "JavaScripts" && $type eq "set") { # create some attributes
|
||||
my (%a, @add);
|
||||
map { $a{$_} = 1 } split(" ", $modules{FHEMWEB}{AttrList});
|
||||
map {
|
||||
$_ =~ s+.*/++; $_ =~ s/.js$//; $_ =~ s/fhem_//; $_ .= "Param";
|
||||
push @add, $_ if(!$a{$_} && $_ !~ m/^-/);
|
||||
} split(" ", $a[3]);
|
||||
} split(" ", $param[0]);
|
||||
$modules{FHEMWEB}{AttrList} .= " ".join(" ",@add) if(@add);
|
||||
}
|
||||
|
||||
if($a[2] eq "csrfToken" && $a[0] eq "set") {
|
||||
my $csrf = $a[3];
|
||||
if($attrName eq "csrfToken" && $type eq "set") {
|
||||
my $csrf = $param[0];
|
||||
if($csrf eq "random") {
|
||||
my ($x,$y) = gettimeofday();
|
||||
$csrf = rand($y)*rand($x);
|
||||
@ -2238,7 +2240,7 @@ FW_Attr(@)
|
||||
$hash->{CSRFTOKEN} = $csrf;
|
||||
}
|
||||
|
||||
if($a[2] eq "csrfToken" && $a[0] eq "del") {
|
||||
if($attrName eq "csrfToken" && $type eq "del") {
|
||||
delete($hash->{CSRFTOKEN});
|
||||
}
|
||||
|
||||
@ -2937,50 +2939,13 @@ FW_widgetOverride($$)
|
||||
<ul>
|
||||
<li><a href="#addStateEvent">addStateEvent</a></li>
|
||||
|
||||
<a name="allowedCommands"></a>
|
||||
<li>allowedCommands<br>
|
||||
A comma separated list of commands allowed from this FHEMWEB
|
||||
instance.<br> If set to an empty list <code>, (i.e. comma only)</code>
|
||||
then this FHEMWEB instance will be read-only.<br> If set to
|
||||
<code>get,set</code>, then this FHEMWEB instance will only allow
|
||||
regular usage of the frontend by clicking the icons/buttons/sliders but
|
||||
not changing any configuration.<br>
|
||||
|
||||
|
||||
This attribute intended to be used together with hiddenroom/hiddengroup
|
||||
<br>
|
||||
|
||||
<b>Note:</b>allowedCommands should work as intended, but no guarantee
|
||||
can be given that there is no way to circumvent it. If a command is
|
||||
allowed it can be issued by URL manipulation also for devices that are
|
||||
hidden.</li><br>
|
||||
|
||||
<li><a href="#allowfrom">allowfrom</a></li>
|
||||
</li><br>
|
||||
|
||||
<a name="basicAuth"></a>
|
||||
<li>basicAuth, basicAuthMsg<br>
|
||||
request a username/password authentication for access. You have to set
|
||||
the basicAuth attribute to the Base64 encoded value of
|
||||
<user>:<password>, e.g.:<ul>
|
||||
# Calculate first the encoded string with the commandline program<br>
|
||||
$ echo -n fhemuser:secret | base64<br>
|
||||
ZmhlbXVzZXI6c2VjcmV0<br>
|
||||
fhem.cfg:<br>
|
||||
attr WEB basicAuth ZmhlbXVzZXI6c2VjcmV0
|
||||
</ul>
|
||||
You can of course use other means of base64 encoding, e.g. online
|
||||
Base64 encoders. If basicAuthMsg is set, it will be displayed in the
|
||||
popup window when requesting the username/password.<br>
|
||||
<br>
|
||||
If the argument of basicAuth is enclosed in {}, then it will be
|
||||
evaluated, and the $user and $password variable will be set to the
|
||||
values entered. If the return value is true, then the password will be
|
||||
accepted.
|
||||
Example:<br>
|
||||
<code>
|
||||
attr WEB basicAuth { "$user:$password" eq "admin:secret" }<br>
|
||||
</code>
|
||||
<li>allowedCommands, basicAuth, basicAuthMsg<br>
|
||||
Please create these attributes for the corresponding <a
|
||||
href="#allowed">allowed</a> device, they are deprecated for the FHEMWEB
|
||||
instance from now on.
|
||||
</li><br>
|
||||
|
||||
<a name="closeConn"></a>
|
||||
@ -3647,52 +3612,13 @@ FW_widgetOverride($$)
|
||||
<ul>
|
||||
<li><a href="#addStateEvent">addStateEvent</a></li>
|
||||
|
||||
<a name="allowedCommands"></a>
|
||||
<li>allowedCommands<br>
|
||||
Eine Komma getrennte Liste der erlaubten Befehle. Bei einer leeren
|
||||
Liste (, dh. nur ein Komma) wird dieser FHEMWEB-Instanz "read-only".
|
||||
<br> Falls es auf <code>get,set</code> gesetzt ist, dann sind in dieser
|
||||
FHEMWEB Instanz keine Konfigurationsänderungen möglich, nur
|
||||
"normale" Bedienung der Schalter/etc.<br>
|
||||
|
||||
Dieses Attribut sollte zusammen mit dem hiddenroom/hiddengroup
|
||||
Attributen verwendet werden. <br>
|
||||
|
||||
<b>Achtung:</b> allowedCommands sollte wie hier beschrieben
|
||||
funktionieren, allerdings können wir keine Garantie geben,
|
||||
daß man sie nicht überlisten, und Schaden anrichten kann.
|
||||
</li><br>
|
||||
|
||||
<li><a href="#allowfrom">allowfrom</a>
|
||||
</li><br>
|
||||
|
||||
<a name="basicAuth"></a>
|
||||
<li>basicAuth, basicAuthMsg<br>
|
||||
Fragt username/password zur Autentifizierung ab. Es gibt mehrere
|
||||
Varianten:
|
||||
<ul>
|
||||
<li>falls das Argument <b>nicht</b> in {} eingeschlossen ist, dann wird
|
||||
es als base64 kodiertes benutzername:passwort interpretiert.
|
||||
Um sowas zu erzeugen kann man entweder einen der zahlreichen
|
||||
Webdienste verwenden, oder das base64 Programm. Beispiel:
|
||||
<ul><code>
|
||||
$ echo -n fhemuser:secret | base64<br>
|
||||
ZmhlbXVzZXI6c2VjcmV0<br>
|
||||
fhem.cfg:<br>
|
||||
attr WEB basicAuth ZmhlbXVzZXI6c2VjcmV0
|
||||
</code></ul>
|
||||
</li>
|
||||
<li>Werden die Argumente in {} angegeben, wird es als perl-Ausdruck
|
||||
ausgewertet, die Variablen $user and $password werden auf die
|
||||
eingegebenen Werte gesetzt. Falls der Rückgabewert wahr ist,
|
||||
wird die Anmeldung akzeptiert.
|
||||
|
||||
Beispiel:<br>
|
||||
<code>
|
||||
attr WEB basicAuth { "$user:$password" eq "admin:secret" }<br>
|
||||
</code>
|
||||
</li>
|
||||
</ul>
|
||||
<li>allowedCommands, basicAuth, basicAuthMsg<br>
|
||||
Diese Attribute müssen ab sofort bei dem passenden <a
|
||||
href="#allowed">allowed</a> Gerät angelegt werden, und sind
|
||||
für eine FHEMWEB Instanz unerwünscht.
|
||||
</li><br>
|
||||
|
||||
<a name="closeConn"></a>
|
||||
|
446
fhem/FHEM/96_allowed.pm
Executable file
446
fhem/FHEM/96_allowed.pm
Executable file
@ -0,0 +1,446 @@
|
||||
##############################################
|
||||
# $Id$
|
||||
package main;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
#####################################
|
||||
sub
|
||||
allowed_Initialize($)
|
||||
{
|
||||
my ($hash) = @_;
|
||||
|
||||
$hash->{DefFn} = "allowed_Define";
|
||||
$hash->{AuthorizeFn} = "allowed_Authorize";
|
||||
$hash->{AuthenticateFn} = "allowed_Authenticate";
|
||||
$hash->{AttrFn} = "allowed_Attr";
|
||||
$hash->{AttrList} = "disable:0,1 validFor allowedCommands allowedDevices ".
|
||||
"basicAuth basicAuthMsg password globalpassword";
|
||||
$hash->{UndefFn} = "allowed_Undef";
|
||||
}
|
||||
|
||||
|
||||
#####################################
|
||||
sub
|
||||
allowed_Define($$)
|
||||
{
|
||||
my ($hash, $def) = @_;
|
||||
my @l = split(" ", $def);
|
||||
|
||||
if(@l > 2) {
|
||||
my %list;
|
||||
for(my $i=2; $i<@l; $i++) {
|
||||
$list{$l[$i]} = 1;
|
||||
}
|
||||
$hash->{devices} = \%list;
|
||||
}
|
||||
$auth_refresh = 1;
|
||||
readingsSingleUpdate($hash, "state", "active", 0);
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub
|
||||
allowed_Undef($$)
|
||||
{
|
||||
$auth_refresh = 1;
|
||||
return undef;
|
||||
}
|
||||
|
||||
#####################################
|
||||
# Return 0 for don't care, 1 for Allowed, 2 for forbidden.
|
||||
sub
|
||||
allowed_Authorize($$$$)
|
||||
{
|
||||
my ($me, $cl, $type, $arg) = @_;
|
||||
|
||||
return 0 if($me->{disabled});
|
||||
return 0 if($me->{validFor} && $me->{validFor} !~ m/\b$cl->{SNAME}\b/);
|
||||
|
||||
if($type eq "cmd") {
|
||||
return 0 if(!$me->{allowedCommands});
|
||||
return ($me->{allowedCommands} =~ m/\b$arg\b/) ? 1 : 2;
|
||||
}
|
||||
|
||||
if($type eq "devicename") {
|
||||
return 0 if(!$me->{allowedDevices});
|
||||
return ($me->{allowedDevices} =~ m/\b$arg\b/) ? 1 : 2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#####################################
|
||||
# Return 0 for authentication not needed, 1 for auth-ok, 2 for wrong password
|
||||
sub
|
||||
allowed_Authenticate($$$$)
|
||||
{
|
||||
my ($me, $cl, $param) = @_;
|
||||
|
||||
return 0 if($me->{disabled});
|
||||
return 0 if($me->{validFor} && $me->{validFor} !~ m/\b$cl->{SNAME}\b/);
|
||||
my $aName = $me->{NAME};
|
||||
|
||||
if($cl->{TYPE} eq "FHEMWEB") {
|
||||
my $basicAuth = AttrVal($aName, "basicAuth", undef);
|
||||
return 0 if(!$basicAuth);
|
||||
|
||||
my $FW_httpheader = $param;
|
||||
my $secret = $FW_httpheader->{Authorization};
|
||||
$secret =~ s/^Basic //i if($secret);
|
||||
my $pwok = ($secret && $secret eq $basicAuth); # Base64
|
||||
if($secret && $basicAuth =~ m/^{.*}$/) {
|
||||
eval "use MIME::Base64";
|
||||
if($@) {
|
||||
Log3 $aName, 1, $@;
|
||||
|
||||
} else {
|
||||
my ($user, $password) = split(":", decode_base64($secret));
|
||||
$pwok = eval $basicAuth;
|
||||
Log3 $aName, 1, "basicAuth expression: $@" if($@);
|
||||
}
|
||||
}
|
||||
|
||||
return 1 if($pwok);
|
||||
|
||||
my $msg = AttrVal($aName, "basicAuthMsg", "FHEM: login required");
|
||||
$cl->{".httpAuthHeader"} = "HTTP/1.1 401 Authorization Required\r\n".
|
||||
"WWW-Authenticate: Basic realm=\"$msg\"\r\n";
|
||||
return 2;
|
||||
}
|
||||
|
||||
if($cl->{TYPE} eq "telnet") {
|
||||
my $pw = AttrVal($aName, "password", undef);
|
||||
if(!$pw) {
|
||||
$pw = AttrVal($aName, "globalpassword", undef);
|
||||
$pw = undef if($pw && $cl->{NAME} !~ m/^telnet:127.0.0.1/);
|
||||
}
|
||||
return 0 if(!$pw);
|
||||
return 2 if(!defined($param));
|
||||
if($pw =~ m/^{.*}$/) {
|
||||
my $password = $param;
|
||||
my $ret = eval $pw;
|
||||
Log3 $aName, 1, "password expression: $@" if($@);
|
||||
return ($ret ? 1 : 2);
|
||||
}
|
||||
return ($pw eq $param) ? 1 : 2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
sub
|
||||
allowed_Attr(@)
|
||||
{
|
||||
my ($type, $devName, $attrName, @param) = @_;
|
||||
my $hash = $defs{$devName};
|
||||
|
||||
my $set = ($type eq "del" ? 0 : (!defined($param[0]) || $param[0]) ? 1 : 0);
|
||||
|
||||
if($attrName eq "disable") {
|
||||
readingsSingleUpdate($hash, "state", $set ? "disabled" : "active", 1);
|
||||
if($set) {
|
||||
$hash->{disable} = 1;
|
||||
} else {
|
||||
delete($hash->{disable});
|
||||
}
|
||||
|
||||
} elsif($attrName eq "allowedCommands" || # hoping for some speedup
|
||||
$attrName eq "allowedDevices" ||
|
||||
$attrName eq "validFor") {
|
||||
if($set) {
|
||||
$hash->{$attrName} = join(" ", @param);
|
||||
} else {
|
||||
delete($hash->{$attrName});
|
||||
}
|
||||
|
||||
} elsif(($attrName eq "basicAuth" ||
|
||||
$attrName eq "password" || $attrName eq "globalpassword") &&
|
||||
$type eq "set") {
|
||||
foreach my $d (devspec2array("TYPE=(FHEMWEB|telnet)")) {
|
||||
delete $defs{$d}{Authenticated} if($defs{$d});
|
||||
}
|
||||
}
|
||||
|
||||
return undef;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
=pod
|
||||
=begin html
|
||||
|
||||
<a name="allowed"></a>
|
||||
<h3>allowed</h3>
|
||||
<ul>
|
||||
<br>
|
||||
|
||||
<a name="alloweddefine"></a>
|
||||
<b>Define</b>
|
||||
<ul>
|
||||
<code>define <name> allowed <deviceList></code>
|
||||
<br><br>
|
||||
Authorize execution of commands and modification of devices based on the
|
||||
frontend used.<br>
|
||||
<b>Note:</b> this module should work as intended, but no guarantee
|
||||
can be given that there is no way to circumvent it.<br><br>
|
||||
Examples:
|
||||
<ul><code>
|
||||
define allowedWEB allowed<br>
|
||||
attr allowedWEB validFor WEB,WEBphone,WEBtablet<br>
|
||||
attr allowedWEB basicAuth { "$user:$password" eq "admin:secret" }<br>
|
||||
attr allowedWEB allowedCommands set,get<br><br>
|
||||
|
||||
define allowedTelnet allowed<br>
|
||||
attr allowedTelnet validFor telnetPort<br>
|
||||
attr allowedTelnet password secret<br>
|
||||
</code></ul>
|
||||
<br>
|
||||
</ul>
|
||||
|
||||
<a name="allowedset"></a>
|
||||
<b>Set:</b> <ul>N/A</ul><br>
|
||||
|
||||
<a name="allowedget"></a>
|
||||
<b>Get</b> <ul>N/A</ul><br>
|
||||
|
||||
<a name="allowedattr"></a>
|
||||
<b>Attributes</b>
|
||||
<ul>
|
||||
<li><a href="#disable">disable</a></li><br>
|
||||
|
||||
<a name="allowedCommands"></a>
|
||||
<li>allowedCommands<br>
|
||||
A comma separated list of commands allowed from the matching frontend
|
||||
(see validFor).<br>
|
||||
If set to an empty list <code>, (i.e. comma only)</code>
|
||||
then no comands are allowed. If set to <code>get,set</code>, then only
|
||||
a "regular" usage is allowed via set and get, but changing any
|
||||
configuration is forbidden.<br>
|
||||
</li><br>
|
||||
|
||||
<a name="allowedDevices"></a>
|
||||
<li>allowedDevices<br>
|
||||
A comma separated list of device names which can be manipulated via the
|
||||
matching frontend (see validFor).
|
||||
</li><br>
|
||||
|
||||
<a name="basicAuth"></a>
|
||||
<li>basicAuth, basicAuthMsg<br>
|
||||
request a username/password authentication for FHEMWEB access. You have
|
||||
to set the basicAuth attribute to the Base64 encoded value of
|
||||
<user>:<password>, e.g.:<ul>
|
||||
# Calculate first the encoded string with the commandline program<br>
|
||||
$ echo -n fhemuser:secret | base64<br>
|
||||
ZmhlbXVzZXI6c2VjcmV0<br>
|
||||
# Set the FHEM attribute<br>
|
||||
attr allowed_WEB basicAuth ZmhlbXVzZXI6c2VjcmV0
|
||||
</ul>
|
||||
You can of course use other means of base64 encoding, e.g. online
|
||||
Base64 encoders.<br>
|
||||
|
||||
If the argument of basicAuth is enclosed in { }, then it will be
|
||||
evaluated, and the $user and $password variable will be set to the
|
||||
values entered. If the return value is true, then the password will be
|
||||
accepted.<br>
|
||||
|
||||
If basicAuthMsg is set, it will be displayed in the
|
||||
popup window when requesting the username/password.<br>
|
||||
|
||||
Example:<br>
|
||||
<ul><code>
|
||||
attr allowedWEB basicAuth { "$user:$password" eq "admin:secret" }<br>
|
||||
</code></ul>
|
||||
</li><br>
|
||||
|
||||
<a name="password"></a>
|
||||
<li>password<br>
|
||||
Specify a password for telnet instances, which has to be entered as the
|
||||
very first string after the connection is established. If the argument
|
||||
is enclosed in {}, then it will be evaluated, and the $password
|
||||
variable will be set to the password entered. If the return value is
|
||||
true, then the password will be accepted. If this parameter is
|
||||
specified, FHEM sends telnet IAC requests to supress echo while
|
||||
entering the password. Also all returned lines are terminated with
|
||||
\r\n.
|
||||
Example:<br>
|
||||
<ul>
|
||||
<code>
|
||||
attr allowed_tPort password secret<br>
|
||||
attr allowed_tPort password {"$password" eq "secret"}
|
||||
</code>
|
||||
</ul>
|
||||
Note: if this attribute is set, you have to specify a password as the
|
||||
first argument when using fhem.pl in client mode:
|
||||
<ul>
|
||||
perl fhem.pl localhost:7072 secret "set lamp on"
|
||||
</ul>
|
||||
</li><br>
|
||||
|
||||
<a name="globalpassword"></a>
|
||||
<li>globalpassword<br>
|
||||
Just like the attribute password, but a password will only required for
|
||||
non-local connections.
|
||||
</li><br>
|
||||
|
||||
|
||||
<a name="validFor"></a>
|
||||
<li>validFor<br>
|
||||
A comma separated list of frontend names. Currently supported frontends
|
||||
are all devices connected through the FHEM TCP/IP library, e.g. telnet
|
||||
and FHEMWEB. If set, the rules specified via the other attributes will
|
||||
only apply to the frontends in the list. If not set, the rules apply to
|
||||
all frontends.
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
<br>
|
||||
|
||||
</ul>
|
||||
|
||||
=end html
|
||||
|
||||
=begin html_DE
|
||||
|
||||
<a name="allowed"></a>
|
||||
<h3>allowed</h3>
|
||||
<ul>
|
||||
<br>
|
||||
|
||||
<a name="alloweddefine"></a>
|
||||
<b>Define</b>
|
||||
<ul>
|
||||
<code>define <name> allowed <deviceList></code>
|
||||
<br><br>
|
||||
Authorisiert das Ausführen von Kommandos oder das Ändern von
|
||||
Geräten abhängig vom verwendeten Frontend.<br>
|
||||
|
||||
<b>Achtung:</b> das Modul sollte wie hier beschrieben funktionieren,
|
||||
allerdings können wir keine Garantie geben, daß man sie nicht
|
||||
überlisten, und Schaden anrichten kann.
|
||||
<br>
|
||||
Beispiele:
|
||||
<ul><code>
|
||||
define allowedWEB allowed<br>
|
||||
attr allowedWEB validFor WEB,WEBphone,WEBtablet<br>
|
||||
attr allowedWEB basicAuth { "$user:$password" eq "admin:secret" }<br>
|
||||
attr allowedWEB allowedCommands set,get<br><br>
|
||||
|
||||
define allowedTelnet allowed<br>
|
||||
attr allowedTelnet validFor telnetPort<br>
|
||||
attr allowedTelnet password secret<br>
|
||||
</code></ul>
|
||||
<br>
|
||||
</ul>
|
||||
|
||||
<a name="allowedset"></a>
|
||||
<b>Set:</b> <ul>N/A</ul><br>
|
||||
|
||||
<a name="allowedget"></a>
|
||||
<b>Get</b> <ul>N/A</ul><br>
|
||||
|
||||
<a name="allowedattr"></a>
|
||||
<b>Attribute</b>
|
||||
<ul>
|
||||
<li><a href="#disable">disable</a>
|
||||
</li><br>
|
||||
|
||||
<a name="allowedCommands"></a>
|
||||
<li>allowedCommands<br>
|
||||
Eine Komma getrennte Liste der erlaubten Befehle des passenden
|
||||
Frontends (siehe validFor). Bei einer leeren Liste (, dh. nur ein
|
||||
Komma) wird dieser Frontend "read-only".
|
||||
Falls es auf <code>get,set</code> gesetzt ist, dann sind in dieser
|
||||
Frontend keine Konfigurationsänderungen möglich, nur
|
||||
"normale" Bedienung der Schalter/etc.
|
||||
</li><br>
|
||||
|
||||
<a name="allowedDevices"></a>
|
||||
<li>allowedDevices<br>
|
||||
Komma getrennte Liste von Gerätenamen, die mit dem passenden
|
||||
Frontend (siehe validFor) geändert werden können.
|
||||
</li><br>
|
||||
|
||||
<a name="basicAuth"></a>
|
||||
<li>basicAuth, basicAuthMsg<br>
|
||||
Betrifft nur FHEMWEB Instanzen (siehe validFor): Fragt username /
|
||||
password zur Autentifizierung ab. Es gibt mehrere Varianten:
|
||||
<ul>
|
||||
<li>falls das Argument <b>nicht</b> in { } eingeschlossen ist, dann wird
|
||||
es als base64 kodiertes benutzername:passwort interpretiert.
|
||||
Um sowas zu erzeugen kann man entweder einen der zahlreichen
|
||||
Webdienste verwenden, oder das base64 Programm. Beispiel:
|
||||
<ul><code>
|
||||
$ echo -n fhemuser:secret | base64<br>
|
||||
ZmhlbXVzZXI6c2VjcmV0<br>
|
||||
fhem.cfg:<br>
|
||||
attr WEB basicAuth ZmhlbXVzZXI6c2VjcmV0
|
||||
</code></ul>
|
||||
</li>
|
||||
<li>Werden die Argumente in { } angegeben, wird es als perl-Ausdruck
|
||||
ausgewertet, die Variablen $user and $password werden auf die
|
||||
eingegebenen Werte gesetzt. Falls der Rückgabewert wahr ist,
|
||||
wird die Anmeldung akzeptiert.
|
||||
|
||||
Beispiel:<br>
|
||||
<ul><code>
|
||||
attr allwedWEB basicAuth { "$user:$password" eq "admin:secret" }<br>
|
||||
</code></ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li><br>
|
||||
|
||||
|
||||
<a name="password"></a>
|
||||
<li>password<br>
|
||||
Betrifft nur telnet Instanzen (siehe validFor): Bezeichnet ein
|
||||
Passwort, welches als allererster String eingegeben werden muss,
|
||||
nachdem die Verbindung aufgebaut wurde. Wenn das Argument in { }
|
||||
eingebettet ist, dann wird es als Perl-Ausdruck ausgewertet, und die
|
||||
Variable $password mit dem eingegebenen Passwort verglichen. Ist der
|
||||
zurückgegebene Wert wahr (true), wird das Passwort akzeptiert.
|
||||
Falls dieser Parameter gesetzt wird, sendet FHEM telnet IAC Requests,
|
||||
um ein Echo während der Passworteingabe zu unterdrücken.
|
||||
Ebenso werden alle zurückgegebenen Zeilen mit \r\n abgeschlossen.
|
||||
|
||||
Beispiel:<br>
|
||||
<ul>
|
||||
<code>
|
||||
attr allowed_tPort password secret<br>
|
||||
attr allowed_tPort password {"$password" eq "secret"}
|
||||
</code>
|
||||
</ul>
|
||||
Hinweis: Falls dieses Attribut gesetzt wird, muss als erstes Argument
|
||||
ein Passwort angegeben werden, wenn fhem.pl im Client-mode betrieben
|
||||
wird:
|
||||
<ul>
|
||||
<code>
|
||||
perl fhem.pl localhost:7072 secret "set lamp on"
|
||||
</code>
|
||||
</ul>
|
||||
</li><br>
|
||||
|
||||
<a name="globalpassword"></a>
|
||||
<li>globalpassword<br>
|
||||
Betrifft nur telnet Instanzen (siehe validFor): Entspricht dem
|
||||
Attribut password; ein Passwort wird aber ausschließlich für
|
||||
nicht-lokale Verbindungen verlangt.
|
||||
</li><br>
|
||||
|
||||
<a name="validFor"></a>
|
||||
<li>validFor<br>
|
||||
Komma separierte Liste von Forntend-Instanznamen. Aktuell werden nur
|
||||
Frontends unterstützt, die das FHEM TCP/IP Bibliothek verwenden.
|
||||
Falls gesetzt, dann gelten die mit den anderen Attributen
|
||||
spezifizierten Regeln nur für diese Instanzen. Falls nicht
|
||||
gesetzt, dann sind alle Frontends betroffen.
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
<br>
|
||||
|
||||
</ul>
|
||||
=end html_DE
|
||||
|
||||
=cut
|
@ -31,6 +31,7 @@ telnet_Initialize($)
|
||||
Hlp=>"[utf8|latin1],query and set the character encoding for the current telnet session" );
|
||||
$cmds{encoding} = \%lhash;
|
||||
}
|
||||
|
||||
sub
|
||||
CommandTelnetEncoding($$)
|
||||
{
|
||||
@ -60,16 +61,29 @@ telnet_SecurityCheck($$)
|
||||
!grep(m/^INITIALIZED$/, @{$dev->{CHANGED}}));
|
||||
my $motd = AttrVal("global", "motd", "");
|
||||
if($motd =~ "^SecurityCheck") {
|
||||
my @list = grep { !(AttrVal($_, "password", undef) ||
|
||||
AttrVal($_, "globalpassword", undef)) }
|
||||
devspec2array("TYPE=telnet");
|
||||
$motd .= (join(",", sort @list).
|
||||
" has no password/globalpassword attribute.\n")
|
||||
if(@list);
|
||||
my @list1 = devspec2array("TYPE=telnet");
|
||||
my @list2 = devspec2array("TYPE=allowed");
|
||||
my @list3;
|
||||
for my $l (@list1) { # This is a hack, as hardcoded to basicAuth
|
||||
next if(!$defs{$l});
|
||||
my $fnd = 0;
|
||||
for my $a (@list2) {
|
||||
next if(!$defs{$a});
|
||||
my $vf = AttrVal($a, "validFor","");
|
||||
$fnd = 1 if((!$vf || $vf =~ m/\b$l\b/) &&
|
||||
(AttrVal($a, "password","") ||
|
||||
AttrVal($a, "globalpassword","")));
|
||||
}
|
||||
push @list3, $l if(!$fnd);
|
||||
}
|
||||
$motd .= (join(",", sort @list3).
|
||||
" has no associated allowed device with password/globalpassword.\n")
|
||||
if(@list3);
|
||||
$attr{global}{motd} = $motd;
|
||||
}
|
||||
delete $modules{telnet}{NotifyFn};
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
##########################
|
||||
@ -165,19 +179,6 @@ telnet_Define($$$)
|
||||
}
|
||||
}
|
||||
|
||||
sub
|
||||
telnet_pw($$)
|
||||
{
|
||||
my ($sname, $cname) = @_;
|
||||
my $pw = $attr{$sname}{password};
|
||||
return $pw if($pw);
|
||||
|
||||
$pw = $attr{$sname}{globalpassword};
|
||||
return $pw if($pw && $cname !~ m/^telnet:127.0.0.1/);
|
||||
|
||||
return undef;
|
||||
}
|
||||
|
||||
##########################
|
||||
sub
|
||||
telnet_Read($)
|
||||
@ -193,8 +194,10 @@ telnet_Read($)
|
||||
syswrite($chash->{CD}, sprintf("%c%c%c", 255, 253, 0) )
|
||||
if( AttrVal($name, "encoding", "") ); #DO BINARY
|
||||
$chash->{CD}->flush();
|
||||
my $auth = Authenticate($chash, undef);
|
||||
syswrite($chash->{CD}, sprintf("%c%c%cPassword: ", 255, 251, 1)) # WILL ECHO
|
||||
if(telnet_pw($name, $chash->{NAME}));
|
||||
if($auth);
|
||||
$chash->{Authenticated} = 0 if(!$auth);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -217,8 +220,7 @@ telnet_Read($)
|
||||
|
||||
$buf =~ s/\r//g;
|
||||
my $sname = ($hash->{isClient} ? $name : $hash->{SNAME});
|
||||
my $pw = telnet_pw($sname, $name);
|
||||
if($pw) {
|
||||
if(!defined($hash->{Authenticated}) || $hash->{Authenticated}) {
|
||||
$buf =~ s/\xff..//g; # Telnet IAC stuff
|
||||
$buf =~ s/\xfd(.)//; # Telnet Do ?
|
||||
syswrite($hash->{CD}, sprintf("%c%c%c", 0xff, 0xfc, ord($1)))
|
||||
@ -232,19 +234,11 @@ telnet_Read($)
|
||||
my ($cmd, $rest) = split("\n", $hash->{BUF}, 2);
|
||||
$hash->{BUF} = $rest;
|
||||
|
||||
if(!$hash->{pwEntered}) {
|
||||
if($pw) {
|
||||
if(!defined($hash->{Authenticated})) {
|
||||
syswrite($hash->{CD}, sprintf("%c%c%c\r\n", 255, 252, 1)); # WONT ECHO
|
||||
|
||||
$ret = ($pw eq $cmd);
|
||||
if($pw =~ m/^{.*}$/) { # Expression as pw
|
||||
my $password = $cmd;
|
||||
$ret = eval $pw;
|
||||
Log3 $name, 1, "password expression: $@" if($@);
|
||||
}
|
||||
|
||||
if($ret) {
|
||||
$hash->{pwEntered} = 1;
|
||||
if(Authenticate($hash, $cmd) == 1) {
|
||||
$hash->{Authenticated} = 1;
|
||||
next;
|
||||
} else {
|
||||
if($hash->{isClient}) {
|
||||
@ -256,7 +250,7 @@ telnet_Read($)
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$gotCmd = 1;
|
||||
if($cmd) {
|
||||
if($cmd =~ m/\\ *$/) { # Multi-line
|
||||
@ -268,8 +262,7 @@ telnet_Read($)
|
||||
undef($hash->{prevlines});
|
||||
}
|
||||
$cmd = latin1ToUtf8($cmd) if( $hash->{encoding} eq "latin1" );
|
||||
$ret = AnalyzeCommandChain($hash, $cmd,
|
||||
AttrVal($sname,"allowedCommands",undef));
|
||||
$ret = AnalyzeCommandChain($hash, $cmd);
|
||||
push @ret, $ret if(defined($ret));
|
||||
}
|
||||
} else {
|
||||
@ -288,7 +281,7 @@ telnet_Read($)
|
||||
$ret .= ($hash->{prevlines} ? "> " : $hash->{prompt}." ")
|
||||
if($gotCmd && $hash->{showPrompt} && !$hash->{rcvdQuit});
|
||||
|
||||
$ret =~ s/\n/\r\n/g if($pw); # only for DOS telnet
|
||||
$ret =~ s/\n/\r\n/g if($hash->{Authenticated}); # only for DOS telnet
|
||||
telnet_Output($hash,$ret);
|
||||
|
||||
if($hash->{rcvdQuit}) {
|
||||
@ -323,16 +316,30 @@ telnet_Output($$)
|
||||
sub
|
||||
telnet_Attr(@)
|
||||
{
|
||||
my ($type, $devName, $attrName, @param) = @_;
|
||||
my @a = @_;
|
||||
my $hash = $defs{$a[1]};
|
||||
my $hash = $defs{$devName};
|
||||
|
||||
if($a[0] eq "set" && $a[2] eq "SSL") {
|
||||
if($type eq "set" && $attrName eq "SSL") {
|
||||
TcpServer_SetSSL($hash);
|
||||
if($hash->{CD}) {
|
||||
my $ret = IO::Socket::SSL->start_SSL($hash->{CD});
|
||||
Log3 $a[1], 1, "$hash->{NAME} start_SSL: $ret" if($ret);
|
||||
Log3 $devName, 1, "$hash->{NAME} start_SSL: $ret" if($ret);
|
||||
}
|
||||
}
|
||||
|
||||
if(($attrName eq "allowedCommands" ||
|
||||
$attrName eq "password" ||
|
||||
$attrName eq "globalpassword" ) && $type eq "set") {
|
||||
my $aName = "allowed_$devName";
|
||||
my $exists = ($defs{$aName} ? 1 : 0);
|
||||
AnalyzeCommand(undef, "defmod $aName allowed");
|
||||
AnalyzeCommand(undef, "attr $aName validFor $devName");
|
||||
AnalyzeCommand(undef, "attr $aName $attrName ".join(" ",@param));
|
||||
return "$devName: ".($exists ? "modifying":"creating").
|
||||
" device $aName for attribute $attrName";
|
||||
}
|
||||
|
||||
return undef;
|
||||
}
|
||||
|
||||
@ -387,8 +394,10 @@ telnet_ActivateInform($;$)
|
||||
Examples:
|
||||
<ul>
|
||||
<code>define tPort telnet 7072 global</code><br>
|
||||
<code>attr tPort globalpassword mySecret</code><br>
|
||||
<code>attr tPort SSL</code><br>
|
||||
<code>attr allowed_tPort allowed</code><br>
|
||||
<code>attr allowed_tPort validFor tPort</code><br>
|
||||
<code>attr allowed_tPort globalpassword mySecret</code><br>
|
||||
</ul>
|
||||
Note: The old global attribute port is automatically converted to a
|
||||
telnet instance with the name telnetPort. The global allowfrom attibute is
|
||||
@ -424,37 +433,6 @@ telnet_ActivateInform($;$)
|
||||
<a name="telnetattr"></a>
|
||||
<b>Attributes:</b>
|
||||
<ul>
|
||||
<a href="#allowedCommands">allowedCommands</a><br>
|
||||
|
||||
<a name="password"></a>
|
||||
<li>password<br>
|
||||
Specify a password, which has to be entered as the very first string
|
||||
after the connection is established. If the argument is enclosed in {},
|
||||
then it will be evaluated, and the $password variable will be set to
|
||||
the password entered. If the return value is true, then the password
|
||||
will be accepted. If thies parameter is specified, fhem sends telnet
|
||||
IAC requests to supress echo while entering the password.
|
||||
Also all returned lines are terminated with \r\n.
|
||||
Example:<br>
|
||||
<ul>
|
||||
<code>
|
||||
attr tPort password secret<br>
|
||||
attr tPort password {"$password" eq "secret"}
|
||||
</code>
|
||||
</ul>
|
||||
Note: if this attribute is set, you have to specify a password as the
|
||||
first argument when using fhem.pl in client mode:
|
||||
<ul>
|
||||
perl fhem.pl localhost:7072 secret "set lamp on"
|
||||
</ul>
|
||||
</li><br>
|
||||
|
||||
<a name="globalpassword"></a>
|
||||
<li>globalpassword<br>
|
||||
Just like the attribute password, but a password will only required for
|
||||
non-local connections.
|
||||
</li><br>
|
||||
|
||||
<a name="prompt"></a>
|
||||
<li>prompt<br>
|
||||
Sets the string for the telnet prompt, the default is fhem>
|
||||
@ -538,8 +516,10 @@ telnet_ActivateInform($;$)
|
||||
Beispiele:
|
||||
<ul>
|
||||
<code>define tPort telnet 7072 global</code><br>
|
||||
<code>attr tPort globalpassword mySecret</code><br>
|
||||
<code>attr tPort SSL</code><br>
|
||||
<code>attr allowed_tPort allowed</code><br>
|
||||
<code>attr allowed_tPort validFor tPort</code><br>
|
||||
<code>attr allowed_tPort globalpassword mySecret</code><br>
|
||||
</ul>
|
||||
Hinweis: Das alte (pre 5.3) "global attribute port" wird automatisch in
|
||||
eine telnet-Instanz mit dem Namen telnetPort umgewandelt. Im Rahmen dieser
|
||||
@ -579,43 +559,6 @@ telnet_ActivateInform($;$)
|
||||
<a name="telnetattr"></a>
|
||||
<b>Attribute</b>
|
||||
<ul>
|
||||
<a href="#allowedCommands">allowedCommands</a><br>
|
||||
|
||||
<a name="password"></a>
|
||||
<li>password<br>
|
||||
Bezeichnet ein Passwort, welches als allererster String eingegeben
|
||||
werden muss, nachdem die Verbindung aufgebaut wurde. Wenn das Argument
|
||||
in {} eingebettet ist, dann wird es als Perl-Ausdruck ausgewertet, und
|
||||
die Variable $password mit dem eingegebenen Passwort verglichen. Ist
|
||||
der zurückgegebene Wert wahr (true), wurde das Passwort
|
||||
akzeptiert. Falls dieser Parameter gesetzt wird, sendet fhem
|
||||
telnet IAC Requests, um ein Echo während der Passworteingabe zu
|
||||
unterdrücken. Ebenso werden alle zurückgegebenen Zeilen mit
|
||||
\r\n abgeschlossen.
|
||||
|
||||
Beispiel:<br>
|
||||
<ul>
|
||||
<code>
|
||||
attr tPort password secret<br>
|
||||
attr tPort password {"$password" eq "secret"}
|
||||
</code>
|
||||
</ul>
|
||||
Hinweis: Falls dieses Attribut gesetzt wird, muss als erstes Argument
|
||||
ein Passwort angegeben werden, wenn fhem.pl im Client-mode betrieben
|
||||
wird:
|
||||
<ul>
|
||||
<code>
|
||||
perl fhem.pl localhost:7072 secret "set lamp on"
|
||||
</code>
|
||||
</ul>
|
||||
</li><br>
|
||||
|
||||
<a name="globalpassword"></a>
|
||||
<li>globalpassword<br>
|
||||
Entspricht dem Attribut password; ein Passwort wird aber
|
||||
ausschließlich für nicht-lokale Verbindungen verlangt.
|
||||
</li><br>
|
||||
|
||||
<a name="prompt"></a>
|
||||
<li>prompt<br>
|
||||
Gibt die Zeichenkette an, welche in der Telnet-Sitzung als
|
||||
|
@ -93,6 +93,7 @@
|
||||
<b>Helper modules</b>
|
||||
<ul>
|
||||
<a href="#at">at</a>
|
||||
<a href="#allowed">at</a>
|
||||
<a href="#autocreate">autocreate</a>
|
||||
<a href="#average">average</a>
|
||||
<a href="#Calendar">Calendar</a>
|
||||
|
@ -93,6 +93,7 @@
|
||||
<b>Hilfs (Erweiterungs-) Module</b>
|
||||
<ul>
|
||||
<a href="#at">at</a>
|
||||
<a href="#allowed">at</a>
|
||||
<a href="#autocreate">autocreate</a>
|
||||
<a href="#average">average</a>
|
||||
<a href="#Calendar">Calendar</a>
|
||||
|
81
fhem/fhem.pl
81
fhem/fhem.pl
@ -81,6 +81,7 @@ sub PrintHash($$);
|
||||
sub ReadingsNum($$$);
|
||||
sub ReadingsTimestamp($$$);
|
||||
sub ReadingsVal($$$);
|
||||
sub RefreshAuthList();
|
||||
sub RemoveInternalTimer($);
|
||||
sub ReplaceEventMap($$$);
|
||||
sub ResolveDateWildcards($@);
|
||||
@ -224,6 +225,9 @@ use vars qw($lastDefChange); # number of last def/attr change
|
||||
use vars qw(@structChangeHist); # Contains the last 10 structural changes
|
||||
use vars qw($cmdFromAnalyze); # used by the warnings-sub
|
||||
use vars qw($featurelevel);
|
||||
use vars qw(@authorize); # List of authorization devices
|
||||
use vars qw(@authenticate); # List of authentication devices
|
||||
use vars qw($auth_refresh);
|
||||
|
||||
my $AttrList = "verbose:0,1,2,3,4,5 room group comment:textField-long alias ".
|
||||
"eventMap userReadings:textField-long";
|
||||
@ -231,7 +235,7 @@ my $currcfgfile=""; # current config/include file
|
||||
my $currlogfile; # logfile, without wildcards
|
||||
my $cvsid = '$Id$';
|
||||
my $duplidx=0; # helper for the above pool
|
||||
my $evalSpecials; # Used by EvalSpecials->AnalyzeCommand parameter passing
|
||||
my $evalSpecials; # Used by EvalSpecials->AnalyzeCommand
|
||||
my $intAtCnt=0;
|
||||
my $logopened = 0; # logfile opened or using stdout
|
||||
my $namedef = "where <name> is a single device name, a list separated by komma (,) or a regexp. See the devspec section in the commandref.html for details.\n";
|
||||
@ -910,7 +914,7 @@ CommandIOWrite($$)
|
||||
sub
|
||||
AnalyzeCommandChain($$;$)
|
||||
{
|
||||
my ($c, $cmd, $allowed) = @_;
|
||||
my ($c, $cmd) = @_;
|
||||
my @ret;
|
||||
|
||||
if($cmd =~ m/^[ \t]*(#.*)?$/) { # Save comments
|
||||
@ -933,7 +937,7 @@ AnalyzeCommandChain($$;$)
|
||||
my $subcmd;
|
||||
while(defined($subcmd = shift @cmdList)) {
|
||||
$subcmd =~ s/SeMiCoLoN/;/g;
|
||||
my $lret = AnalyzeCommand($c, $subcmd, $allowed);
|
||||
my $lret = AnalyzeCommand($c, $subcmd);
|
||||
push(@ret, $lret) if(defined($lret));
|
||||
}
|
||||
@cmdList = @saveCmdList;
|
||||
@ -946,15 +950,15 @@ AnalyzeCommandChain($$;$)
|
||||
sub
|
||||
AnalyzePerlCommand($$;$)
|
||||
{
|
||||
my ($cl, $cmd, $calledFromChain) = @_;
|
||||
my ($cl, $cmd, $calledFromChain) = @_; # third parmeter is deprecated
|
||||
|
||||
return "Forbidden command $cmd." if($cl && !Authorized($cl, "cmd", "perl"));
|
||||
|
||||
return "Forbidden command $cmd."
|
||||
if($cl && $cl->{".allowed"} && $cl->{".allowed"} !~ m/\bperl\b/);
|
||||
$cmd =~ s/\\ *\n/ /g; # Multi-line. Probably not needed anymore
|
||||
|
||||
# Make life easier for oneliners:
|
||||
%value = ();
|
||||
if($featurelevel <= 5.6) {
|
||||
%value = ();
|
||||
foreach my $d (keys %defs) {
|
||||
$value{$d} = $defs{$d}{STATE}
|
||||
}
|
||||
@ -994,9 +998,8 @@ AnalyzePerlCommand($$;$)
|
||||
sub
|
||||
AnalyzeCommand($$;$)
|
||||
{
|
||||
my ($cl, $cmd, $allowed) = @_;
|
||||
my ($cl, $cmd) = @_; # third parmeter is deprecated
|
||||
|
||||
$cl->{".allowed"} = $allowed if($cl); # Forum #38276
|
||||
$cmd = "" if(!defined($cmd)); # Forum #29963
|
||||
$cmd =~ s/^(\n|[ \t])*//;# Strip space or \n at the begginning
|
||||
$cmd =~ s/[ \t]*$//;
|
||||
@ -1009,7 +1012,7 @@ AnalyzeCommand($$;$)
|
||||
}
|
||||
|
||||
if($cmd =~ m/^"(.*)"$/s) { # Shell code in bg, to be able to call us from it
|
||||
return "Forbidden command $cmd." if($allowed && $allowed !~ m/\bshell\b/);
|
||||
return "Forbidden command $cmd." if($cl || !Authorized($cl,"cmd","shell"));
|
||||
if($evalSpecials) {
|
||||
map { $ENV{substr($_,1)} = $evalSpecials->{$_}; } keys %{$evalSpecials};
|
||||
}
|
||||
@ -1041,7 +1044,7 @@ AnalyzeCommand($$;$)
|
||||
$fn = $cmds{$fn}{ReplacedBy}
|
||||
if(defined($cmds{$fn}) && defined($cmds{$fn}{ReplacedBy}));
|
||||
|
||||
return "Forbidden command $fn." if($allowed && $allowed !~ m/\b$fn\b/);
|
||||
return "Forbidden command $fn." if($cl && !Authorized($cl,"cmd",$fn));
|
||||
|
||||
#############
|
||||
# autoload commands.
|
||||
@ -1077,6 +1080,11 @@ devspec2array($;$)
|
||||
|
||||
return "" if(!defined($name));
|
||||
if(defined($defs{$name})) {
|
||||
if($cl && !Authorized($cl, "devicename", $name)) {
|
||||
Log 4, "Forbidden device $name";
|
||||
return "";
|
||||
}
|
||||
|
||||
# FHEM2FHEM LOG mode fake device, avoid local set/attr/etc operations on it
|
||||
return "FHEM2FHEM_FAKE_$name" if($defs{$name}{FAKEDEVICE});
|
||||
return $name;
|
||||
@ -1156,6 +1164,7 @@ devspec2array($;$)
|
||||
push @ret,@res;
|
||||
}
|
||||
return $name if(!@ret && !$isAttr);
|
||||
@ret = grep { Authorized($cl, "devicename", $_) } @ret if($cl);
|
||||
return @ret;
|
||||
}
|
||||
|
||||
@ -4526,4 +4535,54 @@ Each($$;$) # can be used e.g. in at, Forum #40022
|
||||
return $arr[$idx];
|
||||
}
|
||||
|
||||
##################
|
||||
# Return 1 if Authorized, else 0
|
||||
sub
|
||||
Authorized($$$)
|
||||
{
|
||||
my ($cl, $type, $arg) = @_;
|
||||
|
||||
return 1 if(!$init_done || !$cl || !$cl->{SNAME}); # Safeguarding
|
||||
RefreshAuthList() if($auth_refresh);
|
||||
|
||||
foreach my $a (@authorize) {
|
||||
my $r = CallFn($a, "AuthorizeFn", $defs{$a}, $cl, $type, $arg);
|
||||
return 1 if($r == 1);
|
||||
return 0 if($r == 2);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
##################
|
||||
# Return 0 if not needed, 1 if authenticated, 2 if authentication failed
|
||||
sub
|
||||
Authenticate($$)
|
||||
{
|
||||
my ($cl, $arg) = @_;
|
||||
|
||||
return 1 if(!$init_done || !$cl || !$cl->{SNAME}); # Safeguarding
|
||||
RefreshAuthList() if($auth_refresh);
|
||||
|
||||
foreach my $a (@authenticate) {
|
||||
my $r = CallFn($a, "AuthenticateFn", $defs{$a}, $cl, $arg);
|
||||
return $r if($r);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub
|
||||
RefreshAuthList()
|
||||
{
|
||||
@authorize = ();
|
||||
@authenticate = ();
|
||||
|
||||
foreach my $d (sort keys %defs) {
|
||||
my $h = $defs{$d};
|
||||
next if(!$h->{TYPE} || !$modules{$h->{TYPE}});
|
||||
push @authorize, $d if($modules{$h->{TYPE}}{AuthorizeFn});
|
||||
push @authenticate, $d if($modules{$h->{TYPE}}{AuthenticateFn});
|
||||
}
|
||||
$auth_refresh = 0;
|
||||
}
|
||||
|
||||
1;
|
||||
|
Loading…
x
Reference in New Issue
Block a user