Files
onvif-go/device_security.go
T
ProtoTess b4e4982876 Refactor XML response handling in device extended and security tests
- Adjusted formatting in XML response strings for consistency in device_extended_test.go and device_security_test.go.
- Improved readability by aligning XML declaration and body content.
- Updated mock server responses to ensure proper handling of various ONVIF operations.

Enhance device security and storage handling

- Refactored struct field declarations in device_security.go and device_storage_test.go for improved clarity.
- Ensured consistent formatting across struct definitions and XML tags.

Standardize whitespace and formatting across multiple files

- Removed unnecessary blank lines and adjusted indentation in discovery, imaging, media, and PTZ server files.
- Improved overall code readability and maintainability by ensuring consistent formatting.

Update example applications for better readability

- Cleaned up whitespace in example applications to enhance code clarity.
- Ensured consistent formatting in main.go files across various examples.

Refactor server and SOAP handler code for consistency

- Standardized struct field declarations and XML tag formatting in server and SOAP handler files.
- Improved readability by aligning struct fields and ensuring consistent use of whitespace.

General code cleanup and formatting adjustments

- Applied consistent formatting across various files, including types.go and test files.
- Enhanced readability by aligning struct fields and removing unnecessary blank lines.
2025-12-01 00:49:36 +00:00

616 lines
20 KiB
Go

package onvif
import (
"context"
"encoding/xml"
"fmt"
"github.com/0x524a/onvif-go/internal/soap"
)
// GetRemoteUser returns the configured remote user
func (c *Client) GetRemoteUser(ctx context.Context) (*RemoteUser, error) {
type GetRemoteUser struct {
XMLName xml.Name `xml:"tds:GetRemoteUser"`
Xmlns string `xml:"xmlns:tds,attr"`
}
type GetRemoteUserResponse struct {
XMLName xml.Name `xml:"GetRemoteUserResponse"`
RemoteUser *struct {
Username string `xml:"Username"`
Password string `xml:"Password"`
UseDerivedPassword bool `xml:"UseDerivedPassword"`
} `xml:"RemoteUser"`
}
req := GetRemoteUser{
Xmlns: deviceNamespace,
}
var resp GetRemoteUserResponse
username, password := c.GetCredentials()
soapClient := soap.NewClient(c.httpClient, username, password)
if err := soapClient.Call(ctx, c.endpoint, "", req, &resp); err != nil {
return nil, fmt.Errorf("GetRemoteUser failed: %w", err)
}
if resp.RemoteUser == nil {
return nil, nil
}
return &RemoteUser{
Username: resp.RemoteUser.Username,
Password: resp.RemoteUser.Password,
UseDerivedPassword: resp.RemoteUser.UseDerivedPassword,
}, nil
}
// SetRemoteUser sets the remote user
func (c *Client) SetRemoteUser(ctx context.Context, remoteUser *RemoteUser) error {
type SetRemoteUser struct {
XMLName xml.Name `xml:"tds:SetRemoteUser"`
Xmlns string `xml:"xmlns:tds,attr"`
RemoteUser *struct {
Username string `xml:"tds:Username"`
Password string `xml:"tds:Password,omitempty"`
UseDerivedPassword bool `xml:"tds:UseDerivedPassword"`
} `xml:"tds:RemoteUser,omitempty"`
}
req := SetRemoteUser{
Xmlns: deviceNamespace,
}
if remoteUser != nil {
req.RemoteUser = &struct {
Username string `xml:"tds:Username"`
Password string `xml:"tds:Password,omitempty"`
UseDerivedPassword bool `xml:"tds:UseDerivedPassword"`
}{
Username: remoteUser.Username,
Password: remoteUser.Password,
UseDerivedPassword: remoteUser.UseDerivedPassword,
}
}
username, password := c.GetCredentials()
soapClient := soap.NewClient(c.httpClient, username, password)
if err := soapClient.Call(ctx, c.endpoint, "", req, nil); err != nil {
return fmt.Errorf("SetRemoteUser failed: %w", err)
}
return nil
}
// GetIPAddressFilter gets the IP address filter settings from a device
func (c *Client) GetIPAddressFilter(ctx context.Context) (*IPAddressFilter, error) {
type GetIPAddressFilter struct {
XMLName xml.Name `xml:"tds:GetIPAddressFilter"`
Xmlns string `xml:"xmlns:tds,attr"`
}
type GetIPAddressFilterResponse struct {
XMLName xml.Name `xml:"GetIPAddressFilterResponse"`
IPAddressFilter struct {
Type string `xml:"Type"`
IPv4Address []struct {
Address string `xml:"Address"`
PrefixLength int `xml:"PrefixLength"`
} `xml:"IPv4Address"`
IPv6Address []struct {
Address string `xml:"Address"`
PrefixLength int `xml:"PrefixLength"`
} `xml:"IPv6Address"`
} `xml:"IPAddressFilter"`
}
req := GetIPAddressFilter{
Xmlns: deviceNamespace,
}
var resp GetIPAddressFilterResponse
username, password := c.GetCredentials()
soapClient := soap.NewClient(c.httpClient, username, password)
if err := soapClient.Call(ctx, c.endpoint, "", req, &resp); err != nil {
return nil, fmt.Errorf("GetIPAddressFilter failed: %w", err)
}
filter := &IPAddressFilter{
Type: IPAddressFilterType(resp.IPAddressFilter.Type),
}
for _, addr := range resp.IPAddressFilter.IPv4Address {
filter.IPv4Address = append(filter.IPv4Address, PrefixedIPv4Address{
Address: addr.Address,
PrefixLength: addr.PrefixLength,
})
}
for _, addr := range resp.IPAddressFilter.IPv6Address {
filter.IPv6Address = append(filter.IPv6Address, PrefixedIPv6Address{
Address: addr.Address,
PrefixLength: addr.PrefixLength,
})
}
return filter, nil
}
// SetIPAddressFilter sets the IP address filter settings on a device
func (c *Client) SetIPAddressFilter(ctx context.Context, filter *IPAddressFilter) error {
type SetIPAddressFilter struct {
XMLName xml.Name `xml:"tds:SetIPAddressFilter"`
Xmlns string `xml:"xmlns:tds,attr"`
IPAddressFilter struct {
Type string `xml:"tds:Type"`
IPv4Address []struct {
Address string `xml:"tds:Address"`
PrefixLength int `xml:"tds:PrefixLength"`
} `xml:"tds:IPv4Address,omitempty"`
IPv6Address []struct {
Address string `xml:"tds:Address"`
PrefixLength int `xml:"tds:PrefixLength"`
} `xml:"tds:IPv6Address,omitempty"`
} `xml:"tds:IPAddressFilter"`
}
req := SetIPAddressFilter{
Xmlns: deviceNamespace,
}
req.IPAddressFilter.Type = string(filter.Type)
for _, addr := range filter.IPv4Address {
req.IPAddressFilter.IPv4Address = append(req.IPAddressFilter.IPv4Address, struct {
Address string `xml:"tds:Address"`
PrefixLength int `xml:"tds:PrefixLength"`
}{
Address: addr.Address,
PrefixLength: addr.PrefixLength,
})
}
for _, addr := range filter.IPv6Address {
req.IPAddressFilter.IPv6Address = append(req.IPAddressFilter.IPv6Address, struct {
Address string `xml:"tds:Address"`
PrefixLength int `xml:"tds:PrefixLength"`
}{
Address: addr.Address,
PrefixLength: addr.PrefixLength,
})
}
username, password := c.GetCredentials()
soapClient := soap.NewClient(c.httpClient, username, password)
if err := soapClient.Call(ctx, c.endpoint, "", req, nil); err != nil {
return fmt.Errorf("SetIPAddressFilter failed: %w", err)
}
return nil
}
// AddIPAddressFilter adds an IP filter address to a device
func (c *Client) AddIPAddressFilter(ctx context.Context, filter *IPAddressFilter) error {
type AddIPAddressFilter struct {
XMLName xml.Name `xml:"tds:AddIPAddressFilter"`
Xmlns string `xml:"xmlns:tds,attr"`
IPAddressFilter struct {
Type string `xml:"tds:Type"`
IPv4Address []struct {
Address string `xml:"tds:Address"`
PrefixLength int `xml:"tds:PrefixLength"`
} `xml:"tds:IPv4Address,omitempty"`
IPv6Address []struct {
Address string `xml:"tds:Address"`
PrefixLength int `xml:"tds:PrefixLength"`
} `xml:"tds:IPv6Address,omitempty"`
} `xml:"tds:IPAddressFilter"`
}
req := AddIPAddressFilter{
Xmlns: deviceNamespace,
}
req.IPAddressFilter.Type = string(filter.Type)
for _, addr := range filter.IPv4Address {
req.IPAddressFilter.IPv4Address = append(req.IPAddressFilter.IPv4Address, struct {
Address string `xml:"tds:Address"`
PrefixLength int `xml:"tds:PrefixLength"`
}{
Address: addr.Address,
PrefixLength: addr.PrefixLength,
})
}
for _, addr := range filter.IPv6Address {
req.IPAddressFilter.IPv6Address = append(req.IPAddressFilter.IPv6Address, struct {
Address string `xml:"tds:Address"`
PrefixLength int `xml:"tds:PrefixLength"`
}{
Address: addr.Address,
PrefixLength: addr.PrefixLength,
})
}
username, password := c.GetCredentials()
soapClient := soap.NewClient(c.httpClient, username, password)
if err := soapClient.Call(ctx, c.endpoint, "", req, nil); err != nil {
return fmt.Errorf("AddIPAddressFilter failed: %w", err)
}
return nil
}
// RemoveIPAddressFilter deletes an IP filter address from a device
func (c *Client) RemoveIPAddressFilter(ctx context.Context, filter *IPAddressFilter) error {
type RemoveIPAddressFilter struct {
XMLName xml.Name `xml:"tds:RemoveIPAddressFilter"`
Xmlns string `xml:"xmlns:tds,attr"`
IPAddressFilter struct {
Type string `xml:"tds:Type"`
IPv4Address []struct {
Address string `xml:"tds:Address"`
PrefixLength int `xml:"tds:PrefixLength"`
} `xml:"tds:IPv4Address,omitempty"`
IPv6Address []struct {
Address string `xml:"tds:Address"`
PrefixLength int `xml:"tds:PrefixLength"`
} `xml:"tds:IPv6Address,omitempty"`
} `xml:"tds:IPAddressFilter"`
}
req := RemoveIPAddressFilter{
Xmlns: deviceNamespace,
}
req.IPAddressFilter.Type = string(filter.Type)
for _, addr := range filter.IPv4Address {
req.IPAddressFilter.IPv4Address = append(req.IPAddressFilter.IPv4Address, struct {
Address string `xml:"tds:Address"`
PrefixLength int `xml:"tds:PrefixLength"`
}{
Address: addr.Address,
PrefixLength: addr.PrefixLength,
})
}
for _, addr := range filter.IPv6Address {
req.IPAddressFilter.IPv6Address = append(req.IPAddressFilter.IPv6Address, struct {
Address string `xml:"tds:Address"`
PrefixLength int `xml:"tds:PrefixLength"`
}{
Address: addr.Address,
PrefixLength: addr.PrefixLength,
})
}
username, password := c.GetCredentials()
soapClient := soap.NewClient(c.httpClient, username, password)
if err := soapClient.Call(ctx, c.endpoint, "", req, nil); err != nil {
return fmt.Errorf("RemoveIPAddressFilter failed: %w", err)
}
return nil
}
// GetZeroConfiguration gets the zero-configuration from a device
func (c *Client) GetZeroConfiguration(ctx context.Context) (*NetworkZeroConfiguration, error) {
type GetZeroConfiguration struct {
XMLName xml.Name `xml:"tds:GetZeroConfiguration"`
Xmlns string `xml:"xmlns:tds,attr"`
}
type GetZeroConfigurationResponse struct {
XMLName xml.Name `xml:"GetZeroConfigurationResponse"`
ZeroConfiguration struct {
InterfaceToken string `xml:"InterfaceToken"`
Enabled bool `xml:"Enabled"`
Addresses []string `xml:"Addresses"`
} `xml:"ZeroConfiguration"`
}
req := GetZeroConfiguration{
Xmlns: deviceNamespace,
}
var resp GetZeroConfigurationResponse
username, password := c.GetCredentials()
soapClient := soap.NewClient(c.httpClient, username, password)
if err := soapClient.Call(ctx, c.endpoint, "", req, &resp); err != nil {
return nil, fmt.Errorf("GetZeroConfiguration failed: %w", err)
}
return &NetworkZeroConfiguration{
InterfaceToken: resp.ZeroConfiguration.InterfaceToken,
Enabled: resp.ZeroConfiguration.Enabled,
Addresses: resp.ZeroConfiguration.Addresses,
}, nil
}
// SetZeroConfiguration sets the zero-configuration
func (c *Client) SetZeroConfiguration(ctx context.Context, interfaceToken string, enabled bool) error {
type SetZeroConfiguration struct {
XMLName xml.Name `xml:"tds:SetZeroConfiguration"`
Xmlns string `xml:"xmlns:tds,attr"`
InterfaceToken string `xml:"tds:InterfaceToken"`
Enabled bool `xml:"tds:Enabled"`
}
req := SetZeroConfiguration{
Xmlns: deviceNamespace,
InterfaceToken: interfaceToken,
Enabled: enabled,
}
username, password := c.GetCredentials()
soapClient := soap.NewClient(c.httpClient, username, password)
if err := soapClient.Call(ctx, c.endpoint, "", req, nil); err != nil {
return fmt.Errorf("SetZeroConfiguration failed: %w", err)
}
return nil
}
// GetDynamicDNS gets the dynamic DNS settings from a device
func (c *Client) GetDynamicDNS(ctx context.Context) (*DynamicDNSInformation, error) {
type GetDynamicDNS struct {
XMLName xml.Name `xml:"tds:GetDynamicDNS"`
Xmlns string `xml:"xmlns:tds,attr"`
}
type GetDynamicDNSResponse struct {
XMLName xml.Name `xml:"GetDynamicDNSResponse"`
DynamicDNSInformation struct {
Type string `xml:"Type"`
Name string `xml:"Name"`
TTL string `xml:"TTL"`
} `xml:"DynamicDNSInformation"`
}
req := GetDynamicDNS{
Xmlns: deviceNamespace,
}
var resp GetDynamicDNSResponse
username, password := c.GetCredentials()
soapClient := soap.NewClient(c.httpClient, username, password)
if err := soapClient.Call(ctx, c.endpoint, "", req, &resp); err != nil {
return nil, fmt.Errorf("GetDynamicDNS failed: %w", err)
}
return &DynamicDNSInformation{
Type: DynamicDNSType(resp.DynamicDNSInformation.Type),
Name: resp.DynamicDNSInformation.Name,
// TTL would need duration parsing
}, nil
}
// SetDynamicDNS sets the dynamic DNS settings on a device
func (c *Client) SetDynamicDNS(ctx context.Context, dnsType DynamicDNSType, name string) error {
type SetDynamicDNS struct {
XMLName xml.Name `xml:"tds:SetDynamicDNS"`
Xmlns string `xml:"xmlns:tds,attr"`
Type DynamicDNSType `xml:"tds:Type"`
Name string `xml:"tds:Name,omitempty"`
}
req := SetDynamicDNS{
Xmlns: deviceNamespace,
Type: dnsType,
Name: name,
}
username, password := c.GetCredentials()
soapClient := soap.NewClient(c.httpClient, username, password)
if err := soapClient.Call(ctx, c.endpoint, "", req, nil); err != nil {
return fmt.Errorf("SetDynamicDNS failed: %w", err)
}
return nil
}
// GetPasswordComplexityConfiguration retrieves the current password complexity configuration settings
func (c *Client) GetPasswordComplexityConfiguration(ctx context.Context) (*PasswordComplexityConfiguration, error) {
type GetPasswordComplexityConfiguration struct {
XMLName xml.Name `xml:"tds:GetPasswordComplexityConfiguration"`
Xmlns string `xml:"xmlns:tds,attr"`
}
type GetPasswordComplexityConfigurationResponse struct {
XMLName xml.Name `xml:"GetPasswordComplexityConfigurationResponse"`
MinLen int `xml:"MinLen"`
Uppercase int `xml:"Uppercase"`
Number int `xml:"Number"`
SpecialChars int `xml:"SpecialChars"`
BlockUsernameOccurrence bool `xml:"BlockUsernameOccurrence"`
PolicyConfigurationLocked bool `xml:"PolicyConfigurationLocked"`
}
req := GetPasswordComplexityConfiguration{
Xmlns: deviceNamespace,
}
var resp GetPasswordComplexityConfigurationResponse
username, password := c.GetCredentials()
soapClient := soap.NewClient(c.httpClient, username, password)
if err := soapClient.Call(ctx, c.endpoint, "", req, &resp); err != nil {
return nil, fmt.Errorf("GetPasswordComplexityConfiguration failed: %w", err)
}
return &PasswordComplexityConfiguration{
MinLen: resp.MinLen,
Uppercase: resp.Uppercase,
Number: resp.Number,
SpecialChars: resp.SpecialChars,
BlockUsernameOccurrence: resp.BlockUsernameOccurrence,
PolicyConfigurationLocked: resp.PolicyConfigurationLocked,
}, nil
}
// SetPasswordComplexityConfiguration allows setting of the password complexity configuration
func (c *Client) SetPasswordComplexityConfiguration(ctx context.Context, config *PasswordComplexityConfiguration) error {
type SetPasswordComplexityConfiguration struct {
XMLName xml.Name `xml:"tds:SetPasswordComplexityConfiguration"`
Xmlns string `xml:"xmlns:tds,attr"`
MinLen int `xml:"tds:MinLen,omitempty"`
Uppercase int `xml:"tds:Uppercase,omitempty"`
Number int `xml:"tds:Number,omitempty"`
SpecialChars int `xml:"tds:SpecialChars,omitempty"`
BlockUsernameOccurrence bool `xml:"tds:BlockUsernameOccurrence,omitempty"`
PolicyConfigurationLocked bool `xml:"tds:PolicyConfigurationLocked,omitempty"`
}
req := SetPasswordComplexityConfiguration{
Xmlns: deviceNamespace,
MinLen: config.MinLen,
Uppercase: config.Uppercase,
Number: config.Number,
SpecialChars: config.SpecialChars,
BlockUsernameOccurrence: config.BlockUsernameOccurrence,
PolicyConfigurationLocked: config.PolicyConfigurationLocked,
}
username, password := c.GetCredentials()
soapClient := soap.NewClient(c.httpClient, username, password)
if err := soapClient.Call(ctx, c.endpoint, "", req, nil); err != nil {
return fmt.Errorf("SetPasswordComplexityConfiguration failed: %w", err)
}
return nil
}
// GetPasswordHistoryConfiguration retrieves the current password history configuration settings
func (c *Client) GetPasswordHistoryConfiguration(ctx context.Context) (*PasswordHistoryConfiguration, error) {
type GetPasswordHistoryConfiguration struct {
XMLName xml.Name `xml:"tds:GetPasswordHistoryConfiguration"`
Xmlns string `xml:"xmlns:tds,attr"`
}
type GetPasswordHistoryConfigurationResponse struct {
XMLName xml.Name `xml:"GetPasswordHistoryConfigurationResponse"`
Enabled bool `xml:"Enabled"`
Length int `xml:"Length"`
}
req := GetPasswordHistoryConfiguration{
Xmlns: deviceNamespace,
}
var resp GetPasswordHistoryConfigurationResponse
username, password := c.GetCredentials()
soapClient := soap.NewClient(c.httpClient, username, password)
if err := soapClient.Call(ctx, c.endpoint, "", req, &resp); err != nil {
return nil, fmt.Errorf("GetPasswordHistoryConfiguration failed: %w", err)
}
return &PasswordHistoryConfiguration{
Enabled: resp.Enabled,
Length: resp.Length,
}, nil
}
// SetPasswordHistoryConfiguration allows setting of the password history configuration
func (c *Client) SetPasswordHistoryConfiguration(ctx context.Context, config *PasswordHistoryConfiguration) error {
type SetPasswordHistoryConfiguration struct {
XMLName xml.Name `xml:"tds:SetPasswordHistoryConfiguration"`
Xmlns string `xml:"xmlns:tds,attr"`
Enabled bool `xml:"tds:Enabled"`
Length int `xml:"tds:Length"`
}
req := SetPasswordHistoryConfiguration{
Xmlns: deviceNamespace,
Enabled: config.Enabled,
Length: config.Length,
}
username, password := c.GetCredentials()
soapClient := soap.NewClient(c.httpClient, username, password)
if err := soapClient.Call(ctx, c.endpoint, "", req, nil); err != nil {
return fmt.Errorf("SetPasswordHistoryConfiguration failed: %w", err)
}
return nil
}
// GetAuthFailureWarningConfiguration retrieves the current authentication failure warning configuration
func (c *Client) GetAuthFailureWarningConfiguration(ctx context.Context) (*AuthFailureWarningConfiguration, error) {
type GetAuthFailureWarningConfiguration struct {
XMLName xml.Name `xml:"tds:GetAuthFailureWarningConfiguration"`
Xmlns string `xml:"xmlns:tds,attr"`
}
type GetAuthFailureWarningConfigurationResponse struct {
XMLName xml.Name `xml:"GetAuthFailureWarningConfigurationResponse"`
Enabled bool `xml:"Enabled"`
MonitorPeriod int `xml:"MonitorPeriod"`
MaxAuthFailures int `xml:"MaxAuthFailures"`
}
req := GetAuthFailureWarningConfiguration{
Xmlns: deviceNamespace,
}
var resp GetAuthFailureWarningConfigurationResponse
username, password := c.GetCredentials()
soapClient := soap.NewClient(c.httpClient, username, password)
if err := soapClient.Call(ctx, c.endpoint, "", req, &resp); err != nil {
return nil, fmt.Errorf("GetAuthFailureWarningConfiguration failed: %w", err)
}
return &AuthFailureWarningConfiguration{
Enabled: resp.Enabled,
MonitorPeriod: resp.MonitorPeriod,
MaxAuthFailures: resp.MaxAuthFailures,
}, nil
}
// SetAuthFailureWarningConfiguration allows setting of the authentication failure warning configuration
func (c *Client) SetAuthFailureWarningConfiguration(ctx context.Context, config *AuthFailureWarningConfiguration) error {
type SetAuthFailureWarningConfiguration struct {
XMLName xml.Name `xml:"tds:SetAuthFailureWarningConfiguration"`
Xmlns string `xml:"xmlns:tds,attr"`
Enabled bool `xml:"tds:Enabled"`
MonitorPeriod int `xml:"tds:MonitorPeriod"`
MaxAuthFailures int `xml:"tds:MaxAuthFailures"`
}
req := SetAuthFailureWarningConfiguration{
Xmlns: deviceNamespace,
Enabled: config.Enabled,
MonitorPeriod: config.MonitorPeriod,
MaxAuthFailures: config.MaxAuthFailures,
}
username, password := c.GetCredentials()
soapClient := soap.NewClient(c.httpClient, username, password)
if err := soapClient.Call(ctx, c.endpoint, "", req, nil); err != nil {
return fmt.Errorf("SetAuthFailureWarningConfiguration failed: %w", err)
}
return nil
}