Art-of-WiFi / UniFi-API-client

A PHP API client class to interact with Ubiquiti's UniFi Controller API
MIT License
1.09k stars 217 forks source link

UDM SE updated to 3.2.7, cannot re-use cookies anymore #206

Closed tflatebo closed 6 months ago

tflatebo commented 6 months ago

My UDM SE was updated overnight to version 3.2.7, and my API integration broke.

Debugging it, I could see that the API client (art of wifi) was getting a 401 when I would reuse a cookie. If I stopped using cookies, I could login just fine. Once I started using cookies again, the first login would succeed, pull the cookie, then fail on the next request when I used that same cookie. This only started happening this morning with the recent Unifi update.

Anyone else seeing this?

malle-pietje commented 6 months ago

Hmm, I can confirm the same when using the API browser which also uses cookies. Has anyone figured out yet what Ubiquiti have changed?

malle-pietje commented 6 months ago

I've tried to find a quick fix because I have not yet been able to identify an alternative method to use cookies.

Can you see what happens when you change these lines in the __destruct() method to this:

        if (isset($_SESSION['unificookie']) && !$this->is_unifi_os) {
            return;
        }

Please me know whether this works for you.

malle-pietje commented 6 months ago

With this change you do not need to change your application code. It basically disable cookies for UniFi OS consoles.

tflatebo commented 6 months ago

Hey there thank you for the quick response, I have already disabled cookies in my application, but thank you for the code suggestion.

Hopefully we can find out what they changed.

malle-pietje commented 6 months ago

If I am not mistaken, the latest version should again support cookies when working with UniFi OS 3.2.27. Maybe you can confirm?

tflatebo commented 6 months ago

debug.log

The new 1.1.83 client code didn't resolve this for me. I still get 401s when using a cookie for authentication.

Attached is a debug log of what is happening.

  1. I cleared the cache (I store the cookies in a local cache to reuse across API requests)
  2. Made a successful API request that logged in, and stored the cookie in the cache
  3. Made a second request that failed by trying to use the cookie that was just retrieved in the previous login

My setup is I have a laravel php client that I wrote which uses your library to have a REST API that is stateless. I use my API from a Web UI, and some automation from rundeck, so the my API can make many logins per second to the Unifi API. With the most recent Unifi change limiting API logins, I added the cookie storage using your client to work with the new Unifi requirements.

Let me know if you need anything else.

malle-pietje commented 6 months ago

Hmm, that is strange. Not sure what is causing this.

Looking at the debug messages I see that the logout route is called. That shouldn't happen if the correct session variable exists: https://github.com/Art-of-WiFi/UniFi-API-client/blob/master/src/Client.php#L93-L114

Maybe that provides a clue?

tflatebo commented 6 months ago

Tested again, still not working, and I do have that session variable set in my code.

I ran the API browser docker container from here, and it was able to interact with my UDM SE, but I don't know how to tell if that is persisting cookies or not.

Here is the code in my API client that logs in:

class FirewallRulesController extends Controller
{
    protected $unifi_connection;

    public function __construct()
    {
        // Create a connection that can be reused
        $this->unifi_connection =
            new Unifi_Client(
                env("UNIFI_USER"),
                env("UNIFI_PASS"),
                env("UNIFI_URI"),
                env("UNIFI_SITE"),
                env("UNIFI_VERSION"),
                false
            );

        // set debug if needed
        $this->unifi_connection->set_debug(true);

        // check if we have a cookie from a previous API call, if so, set it on the 
        // client and in the global PHP session
        if(Cache::has('unificookie'))
        {             
            Log::debug("Cookie from cache: " . Cache::get('unificookie'));
            Log::debug("Setting cookie in client from cache in constructor");

            $this->unifi_connection->set_cookies(Cache::get('unificookie'));

            // This is the all-important line, without this it will not 
            // use the cookie
            $_SESSION['unificookie'] = Cache::get('unificookie');

            Log::debug("\$_SESSION['unificookie']) is set to: " . $_SESSION['unificookie']);
        }
        else
        {
            Log::debug("Cookie was not found in cache");
        }

        // prime the client by calling login
        if($this->unifi_connection->login())
        { // have a valid login

            Log::debug("Valid login in controller constructor");
            Log::debug($this->unifi_connection->get_cookies());

            Log::debug("Store cookie in cache in constructor");
            Cache::put('unificookie', $this->unifi_connection->get_cookies());
        }
    }
malle-pietje commented 6 months ago

A couple of recommendations and observations:

tflatebo commented 6 months ago

Can do all of those suggestions thank you!

Any idea why the PHP code still doesn't work with cookies?

Are there any examples of working code with cookies?

On Thu, Dec 21, 2023, 02:45 malle-pietje @.***> wrote:

A couple of recommendations and observations:

  • Move the creation of the session var to the start, no need to pass the cookie using the set_cookies() method
  • If the session var exists, the client class will pick it up automatically when you instatiate the instance and login
  • Laravel best pratices: never directly pick up ENV values within a controller, instead use a config file and pull them from there. Otherwise config caching can mess up things for you.

— Reply to this email directly, view it on GitHub https://github.com/Art-of-WiFi/UniFi-API-client/issues/206#issuecomment-1865875942, or unsubscribe https://github.com/notifications/unsubscribe-auth/AATR6O6PG7QRPZQ2VJQFPMDYKPZMHAVCNFSM6AAAAABAXZAF7GVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTQNRVHA3TKOJUGI . You are receiving this because you authored the thread.Message ID: @.***>

malle-pietje commented 6 months ago

As far as I can tell the cookie implementation in the API Browser works fine. Have a look and install it if you haven’t already done so: https://github.com/Art-of-WiFi/UniFi-API-browser

apadgett01 commented 6 months ago

My UDM SE was updated overnight to version 3.2.7, and my API integration broke.

Debugging it, I could see that the API client (art of wifi) was getting a 401 when I would reuse a cookie. If I stopped using cookies, I could login just fine. Once I started using cookies again, the first login would succeed, pull the cookie, then fail on the next request when I used that same cookie. This only started happening this morning with the recent Unifi update.

Anyone else seeing this?

I'm having the same issue and have not been able to find a workaround. GET works fine using the cookie. Using PUT to enable or disable firewall rules provides a 401 unauthorized. Nothing changed except the 3.2.7 update which broke it immediately afterwards. Even running curl while using root directly from UDM SE gives the same error. Any workarounds would be appreciated.

malle-pietje commented 6 months ago

As far as I can tell the cookie implementation in the API Browser works fine. Have a look and install it if you haven’t already done so: https://github.com/Art-of-WiFi/UniFi-API-browser

The API Browser mainly uses routes that require GET. Will see if I can find the time to figure out what has changed with POST requests. At the moment I have other things on my plate with a much higher priority so it may take a while…

apadgett01 commented 6 months ago

Thank you so much! Below is a basic curl example of what worked prior to the update. The authentication succeeds and I can reuse the cookie for GET. I can no longer do PUT to enable/disable a firewall or traffic rule, without getting a 401 unauthorized. I can code in any workaround you can find. Once again, thank you so much for your help. I use Unifi at home and business and automation of triggering firewall rules based on certain events is essential to my needs.

Login with verbose output

curl -k -c "api-cookie.txt" -d '{"username":"admin", "password":"mypassword"}' -H "Content-Type: application/json" -X POST "https://192.168.0.1:443/api/auth/login" -v

Enable the firewall rule with verbose output

curl -k -b "api-cookie.txt" -X PUT "https://192.168.0.1:443/proxy/network/api/s/default/rest/firewallrule/647506e30f8fb20eadd08f9e" -H "Content-Type: application/json" -d '{"enabled": true}' -v

malle-pietje commented 6 months ago

Thanks for the example. Can you share a PHP example using the client class where you see the same issue?

apadgett01 commented 6 months ago

I'm using C# below:

using System; using System.Net; using System.Net.Http; using System.Text; using Newtonsoft.Json; using System.Linq;

public class FirewallController { private readonly string controllerUrl; private readonly string username; private readonly string password;

public FirewallController(string url, string user, string pass)
{
    ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
    ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;

    controllerUrl = url;
    username = user;
    password = pass;
}

public void ModifyFirewallRule(string firewallRuleId, bool enableRule)
{
    try
    {
        using (var handler = new HttpClientHandler())
        {
            using (var client = new HttpClient(handler))
            {
                // Authenticate and get CSRF token
                string csrfToken = Authenticate(client);

                // Modify the firewall rule
                UpdateFirewallRule(client, csrfToken, firewallRuleId, enableRule);

                Console.WriteLine("Firewall rule modified successfully.");
            }
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine($"An error occurred: {ex.Message}");
    }
}

private string Authenticate(HttpClient client)
{
    var authContent = new StringContent(JsonConvert.SerializeObject(new { username, password }), Encoding.UTF8, "application/json");
    var authResponse = client.PostAsync($"{controllerUrl}/api/auth/login", authContent).Result;
    authResponse.EnsureSuccessStatusCode();

    authResponse.Headers.TryGetValues("X-CSRF-Token", out var headerValues);
    return headerValues?.First() ?? string.Empty;
}

private void UpdateFirewallRule(HttpClient client, string csrfToken, string firewallRuleId, bool enableRule)
{
    client.DefaultRequestHeaders.Add("X-CSRF-Token", csrfToken);

    var ruleData = new { _id = firewallRuleId, enabled = enableRule };
    string jsonPayload = JsonConvert.SerializeObject(ruleData);
    var modifyRuleContent = new StringContent(jsonPayload, Encoding.UTF8, "application/json");

    var modifyRuleResponse = client.PutAsync($"{controllerUrl}/proxy/network/api/s/default/rest/portforward/{firewallRuleId}", modifyRuleContent).Result;
    modifyRuleResponse.EnsureSuccessStatusCode();
}

}

class Program { static void Main(string[] args) { var firewallController = new FirewallController("https://192.168.0.1", "admin", "mypassword"); firewallController.ModifyFirewallRule("647506e30f8fb20eadd08f9e", true); // Set to 'false' to disable the rule } }

apadgett01 commented 6 months ago

Below produces the same error in PHP:

<?php // Enabling error reporting for debugging (remove in production) error_reporting(E_ALL); ini_set('display_errors', 1);

// API endpoint and credentials $controllerUrl = "https://192.168.0.1"; $username = "admin"; $password = "mypassword"; $firewallRuleId = "647506e30f8fb20eadd08f9e";

try { // Initialize cURL session $ch = curl_init();

// Authentication request setup
$authPayload = json_encode(['username' => $username, 'password' => $password]);
curl_setopt($ch, CURLOPT_URL, "{$controllerUrl}/api/auth/login");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $authPayload);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

// Execute authentication request
$authResponse = curl_exec($ch);
if (!$authResponse) {
    throw new Exception('Authentication request failed: ' . curl_error($ch));
}

// Extract CSRF token
$csrfToken = curl_getinfo($ch, CURLINFO_HEADER_OUT);
preg_match('/X-CSRF-Token: (\S+)/', $csrfToken, $matches);
$csrfToken = $matches[1] ?? '';

// PUT request to modify the firewall rule
$ruleData = json_encode([
    '_id' => $firewallRuleId,
    'enabled' => true // Set to false to disable the rule
]);
curl_setopt($ch, CURLOPT_URL, "{$controllerUrl}/proxy/network/api/s/default/rest/portforward/{$firewallRuleId}");
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_POSTFIELDS, $ruleData);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    "X-CSRF-Token: $csrfToken"
]);

// Execute request to modify the firewall rule
$modifyRuleResponse = curl_exec($ch);
if (!$modifyRuleResponse) {
    throw new Exception('Firewall rule modification failed: ' . curl_error($ch));
}

echo "Firewall rule modified successfully.\n";

} catch (Exception $ex) { echo "An error occurred: " . $ex->getMessage() . "\n"; } finally { // Close cURL session curl_close($ch); } ?>

apadgett01 commented 6 months ago

Here is the final PHP draft, leveraging your class. I'd need to help with the authenticate and updateFirewall function as it appears it would most likely result in 401 as well. Let me know if there's anything else I can provide.

<?php

require_once 'path/to/Client.php'; // Update with the correct path to the UniFi API client class

use UniFi_API\Client as UniFiClient;

class FirewallController { private $controllerUrl; private $username; private $password; private $unifiClient;

public function __construct($url, $user, $pass)
{
    $this->controllerUrl = $url;
    $this->username = $user;
    $this->password = $pass;

    // Instantiate UniFi API Client
    $this->unifiClient = new UniFiClient($url, $user, $pass);
}

public function modifyFirewallRule($firewallRuleId, $enableRule)
{
    try {
        // Authenticate
        $this->authenticate();

        // Modify the firewall rule
        $this->updateFirewallRule($firewallRuleId, $enableRule);

        echo "Firewall rule modified successfully.\n";
    } catch (Exception $ex) {
        echo "An error occurred: {$ex->getMessage()}\n";
    }
}

private function authenticate()
{
    // Implement authentication logic
    // Likely involves sending a POST request with the username and password
    // Store CSRF token if necessary
}

private function updateFirewallRule($firewallRuleId, $enableRule)
{
    // Implement the logic to modify the firewall rule
    // This will involve sending a PUT request with the firewall rule ID and the enable/disable status
}

}

// Usage $firewallController = new FirewallController("https://192.168.0.1", "admin", "mypassword"); $firewallController->modifyFirewallRule("647506e30f8fb20eadd08f9e", true); // Set to false to disable the rule

malle-pietje commented 6 months ago

All, I have created a quick and dirty script to test the use of cookies. The script must installed on a web server and be called through a web browser because it uses sessions which are not available when running a script from the CLI.

Here the example config.php file:

<?php
/**
 * Copyright (c) 2023, Art of WiFi
 *
 * This file is subject to the MIT license that is bundled with this package in the file LICENSE.md
 */

/**
 * Controller configuration
 * ===============================
 * Copy this file to your working directory, rename it to config.php and update the section below with your UniFi
 * controller details and credentials
 */
$controller_user     = ''; // the user name for access to the UniFi Controller
$controller_password = ''; // the password for access to the UniFi Controller
$controller_url      = ''; // full url to the UniFi Controller, eg. 'https://22.22.11.11:8443', for UniFi OS-based
                          // controllers a port suffix isn't required, no trailing slashes should be added
$controller_version  = '8.0.26'; // the version of the Controller software, e.g. '4.6.6' (must be at least 4.0.0)
$site_id             = 'default';
$ap_mac              = '';

/**
 * set to true (without quotes) to enable debug output to the browser and the PHP error log
 */
$debug = true;

And here's the test script itself which I've called cookie_test.php:

<?php
/**
 * Copyright (c) 2023, Art of WiFi
 *
 * This file is subject to the MIT license that is bundled with this package in the file LICENSE.md
 */

session_start();

$controller_user     = '';
$controller_password = '';
$controller_url      = '';
$controller_version  = '';
$site_id             = '';
$debug               = false;
$ap_mac              = '';

/**
 * load the class using the composer autoloader
 */
require_once 'vendor/autoload.php';

require_once 'config.php';

echo '<pre>';

if (!isset($_SESSION['unificookie'])) {
    echo 'No unifi cookie found, setting one now';
    echo '<br>';
    $_SESSION['unificookie'] = '';
} else {
    echo 'Unifi cookie exists and is set:';
    echo '<br>';
    echo $_SESSION['unificookie'];
    echo '<br>';
}

if (isset($_GET['cookie_test'])) {
    echo 'GET parameter cookie_test is set';
    echo '<br>';
    echo 'Request method to test is: ' . $_GET['cookie_test'];
    echo '<br>';
}

/**
 * initialize the UniFi API connection class, log in to the controller
 * (this example assumes you have already assigned the correct values in the config.php file)
 */
$unifi_connection = new UniFi_API\Client(
    $controller_user,
    $controller_password,
    $controller_url,
    $site_id,
    $controller_version,
    false
);

$unifi_connection->set_debug($debug);
$login = $unifi_connection->login();

if (!isset($_GET['cookie_test'])) {
    $locate_ap_off = $unifi_connection->locate_ap($ap_mac, false);
}

if ($login === true) {
    echo 'Logged in and setting the cookie';
    echo '<br>';
    $_SESSION['unificookie'] = $unifi_connection->get_cookie();

    /**
     * we can safely continue, so we show two links to the user
     * one that uses a route using GET, and one that uses a route using POST
     */
    echo "<a href='?cookie_test=get'>Click here to test the cookie using GET</a>";
    echo '<br>';
    echo "<a href='?cookie_test=post'>Click here to test the cookie using POST</a>";
    echo '<br>';
} else {
    /**
     * we have a connection problem, check the login method for the error code and message
     */
    echo 'Not logged in';
    echo '<br>';
    echo $login;
    echo '<br>';
}

if (isset($_GET['cookie_test']) && $_GET['cookie_test'] === 'get' && $login === true) {
    $site_info = $unifi_connection->stat_sysinfo();
    echo 'Site info is: ';
    echo '<br>';
    print_r($site_info);
    echo '<br>';
    echo '<a href="./cookie_test.php">Click here to go back</a>';
    echo '<br>';
}

if (isset($_GET['cookie_test']) && $_GET['cookie_test'] === 'post' && $login === true) {
    $locate_ap = $unifi_connection->locate_ap($ap_mac, true);
    echo 'Locating results: ';
    echo '<br>';
    print_r($locate_ap);
    echo '<br>';
    echo '<a href="./cookie_test.php">Click here to go back</a>';
    echo '<br>';
}

echo '</pre>';
malle-pietje commented 6 months ago

This test script runs fine here and leverages the UniFi cookies without issues against a UDR with UniFi OS version 3.2.9.14421 and Network Application version 8.0.26.

If you install this script and access it through a browser and see any issues (e.g. a login for each page load with debug = true), please share the details of your environment and UniFi controller/console.

malle-pietje commented 6 months ago

Remember, cookies are actually meant to be used by browsers and that is what I am testing here. Any other use of cookies (e.g. with CLI scripts) is unsupported as far as I'm concerned.

tflatebo commented 6 months ago

So the reason why any of us (at least me) started using cookies in our backend APIs is because Unifi added a login limit to their API of 5 requests/minute without any documentation or warning. This broke API integrations that made multiple requests per second or minute like mine. My API is called by a custom frontend, and also other automation including rundeck.

So I implemented cookie support in my Laravel API to reuse cookies after the first login with a long TTL in a local cache. Again, not because I wanted to use cookies, but because there was no other way to get my integration working again.

This worked great until v3.2.7 and cookies stopped working for some reason in my API code using your library. I have not been able to get cookies working again. In reality, I don't care about cookies or not, I just want to have a stateless wrapper API for my own purposes.

@apadgett01 So my workaround is to increase the login/minute limit documented here by Travis Vitek: I found it. It is controlled by the success.login.limit.count option in /usr/lib/ulp-go/config.props.

This change does require a reboot, and does persist across reboots. I changed mine to 600 or something like that and now my API works the way it did before Unifi added their login limits (no cookies needed).

apadgett01 commented 6 months ago

I have extended your code by incorporating a 'client.php' file reference and adding a 'modifyFirewallRule' function, as detailed below. I'll share the debug results in a follow-up comment. It's important to mention that my function utilizes curl for its operations. I would appreciate your guidance if your API offers a more efficient method for this purpose. The outcomes of executing this code will be shared in my subsequent comment. Thanks once again for your help.

<?php
/**
 * Copyright (c) 2023, Art of WiFi
 *
 * This file is subject to the MIT license that is bundled with this package in the file LICENSE.md
 */

session_start();

$controller_user     = '';
$controller_password = '';
$controller_url      = '';
$controller_version  = '';
$site_id             = '';
$debug               = false;
$ap_mac              = '';

/**
 * load the class using the composer autoloader
 */
require_once 'vendor/autoload.php';
require_once 'config.php';
require_once 'client.php';

echo '<pre>';

if (!isset($_SESSION['unificookie'])) {
    echo 'No unifi cookie found, setting one now<br>';
    $_SESSION['unificookie'] = '';
} else {
    echo 'Unifi cookie exists and is set:<br>';
    echo $_SESSION['unificookie'] . '<br>';
}

if (isset($_GET['cookie_test'])) {
    echo 'GET parameter cookie_test is set<br>';
    echo 'Request method to test is: ' . $_GET['cookie_test'] . '<br>';
}

$unifi_connection = new UniFi_API\Client(
    $controller_user,
    $controller_password,
    $controller_url,
    $site_id,
    $controller_version,
    false
);

$unifi_connection->set_debug($debug);
$login = $unifi_connection->login();

if (!isset($_GET['cookie_test'])) {
    $locate_ap_off = $unifi_connection->locate_ap($ap_mac, false);
}

/**
 * Function to modify a firewall rule
 */
function modifyFirewallRule($controllerUrl, $firewallRuleId, $enableRule, $cookie, $debug) {
    $ch = curl_init();

    $url = "{$controllerUrl}/proxy/network/api/s/default/rest/portforward/{$firewallRuleId}";
    $data = json_encode(['_id' => $firewallRuleId, 'enabled' => $enableRule]);
    $headers = [
        'Content-Type: application/json',
        'Cookie: ' . $cookie
    ];

    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
    curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

    $response = curl_exec($ch);
    if ($response === false) {
        echo "CURL Error: " . curl_error($ch) . "<br>";
        curl_close($ch);
        return;
    }

    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    if ($debug) {
        echo "Response Code: " . $httpCode . "<br>";
        echo "Response: " . $response . "<br>";
    }

    if ($httpCode != 200) {
        echo "Error in modifying firewall rule. HTTP Code: " . $httpCode . "<br>";
    } else {
        echo "Firewall rule modified successfully.<br>";
    }

    curl_close($ch);
}

if ($login === true) {
        echo 'Logged in and setting the cookie<br>';
    $_SESSION['unificookie'] = $unifi_connection->get_cookie();

    echo "<a href='?cookie_test=get'>Click here to test the cookie using GET</a><br>";
    echo "<a href='?cookie_test=post'>Click here to test the cookie using POST</a><br>";
    echo "<a href='?modify_rule=1'>Click here to modify a firewall rule</a><br>";
} else {
    echo 'Not logged in<br>';
    echo $login . '<br>';
}

if (isset($_GET['cookie_test']) && $_GET['cookie_test'] === 'get' && $login === true) {
    $site_info = $unifi_connection->stat_sysinfo();
    echo 'Site info is: <br>';
    print_r($site_info);
    echo '<br>';
    echo '<a href="./cookie_test.php">Click here to go back</a><br>';
}

if (isset($_GET['cookie_test']) && $_GET['cookie_test'] === 'post' && $login === true) {
    $locate_ap = $unifi_connection->locate_ap($ap_mac, true);
    echo 'Locating results: <br>';
    print_r($locate_ap);
    echo '<br>';
    echo '<a href="./cookie_test.php">Click here to go back</a><br>';
}

if (isset($_GET['modify_rule']) && $_GET['modify_rule'] == '1') {
    // Replace 'example_rule_id' and 'true' with actual values
    modifyFirewallRule($controller_url, 'example_rule_id', true, $_SESSION['unificookie'], $debug);
}
echo '</pre>';
?>
apadgett01 commented 6 months ago

The GET request functions correctly, but I'm encountering persistent issues with the PUT request for updating the Firewall Rule, which consistently returns a 403 error. Additionally, when attempting a POST request, it results in a 400 bad request error, citing "api.err.Unknown Device" as the reason. Could you guide us through the appropriate next steps to resolve these issues? Thank you for your assistance.

**UDM SE OS 3.2.7; Network 8.0.26

When clicking modify firewall rule:

Response Code: 403
Response: {"error":{"code":403,"message":"Forbidden"}}
Error in modifying firewall rule. HTTP Code: 403

When clicking test GET:

Unifi cookie exists and is set:
TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJmZWZjOWI1ZS1kNzc1LTQ2ZTMtYjJjZS1jMzk3NTM5MGEzZDQiLCJwYXNzd29yZFJldmlzaW9uIjoxNzAzNjYyMzY3LCJpc1JlbWVtYmVyZWQiOmZhbHNlLCJjc3JmVG9rZW4iOiIxYzAzMzI2Yi0wNGRhLTQyZTMtOGJlMi0zZTVlZmVkY2UwMTUiLCJpYXQiOjE3MDQ0MDE5NzEsImV4cCI6MTcwNDQwOTE3MSwianRpIjoiZjM3NTZiOGMtODk0OS00MWYxLTg3NzAtMzEyNDFmNDM5Yjk1In0.w4oc6alW2RVEKlUnL67UAGsCDG9O8c0cHPOj4ErxOOg
GET parameter cookie_test is set
Request method to test is: get
Logged in and setting the cookie
[Click here to test the cookie using GET]
[Click here to test the cookie using POST]
[Click here to modify a firewall rule]

---------cURL INFO-----------
Array
(
    [url] => https://192.168.0.1:443/proxy/network/api/s/default/stat/sysinfo
    [content_type] => application/json;charset=UTF-8
    [http_code] => 200
    [header_size] => 657
    [request_size] => 524
    [filetime] => -1
    [ssl_verify_result] => 18
    [redirect_count] => 0
    [total_time] => 0.026247
    [namelookup_time] => 0.000115
    [connect_time] => 0.001083
    [pretransfer_time] => 0.010675
    [size_upload] => 0
    [size_download] => 1519
    [speed_download] => 58423
    [speed_upload] => 0
    [download_content_length] => 1519
    [upload_content_length] => -1
    [starttransfer_time] => 0.026103
    [redirect_time] => 0
    [redirect_url] => 
    [primary_ip] => 192.168.0.1
    [certinfo] => Array
        (
        )

    [primary_port] => 443
    [local_ip] => 192.168.0.252
    [local_port] => 62261
    [http_version] => 3
    [protocol] => 2
    [ssl_verifyresult] => 0
    [scheme] => HTTPS
    [appconnect_time_us] => 10256
    [connect_time_us] => 1083
    [namelookup_time_us] => 115
    [pretransfer_time_us] => 10675
    [redirect_time_us] => 0
    [starttransfer_time_us] => 26103
    [total_time_us] => 26247
)

-------URL & PAYLOAD---------
https://192.168.0.1:443/proxy/network/api/s/default/stat/sysinfo
empty payload
----------RESPONSE-----------
{"meta":{"rc":"ok"},"data":[{"timezone":"America/Chicago","autobackup":false,"build":"atag_8.0.26_24388","version":"8.0.26","previous_version":"8.0.24","data_retention_days":90,"data_retention_time_in_hours_for_5minutes_scale":24,"data_retention_time_in_hours_for_hourly_scale":168,"data_retention_time_in_hours_for_daily_scale":2160,"data_retention_time_in_hours_for_monthly_scale":8760,"data_retention_time_in_hours_for_others":2160,"update_available":false,"update_downloaded":false,"live_chat":"super-only","store_enabled":"super-only","hostname":"Dream-Machine-Special-Edition","name":"Dream Machine Special Edition","ip_addrs":["75.62.79.127"],"inform_port":8080,"https_port":8443,"portal_http_port":8880,"override_inform_host":false,"image_maps_use_google_engine":false,"radius_disconnect_running":false,"facebook_wifi_registered":false,"sso_app_id":"90waTUDb0XgRdfyYlA7sM5xEGS90S0N3zyFbyo8v","sso_app_sec":"8IVW8RyPBsqYLsPX9LPMmWMQxHU8E6wV8cX6C6iIlRnLT4bvuuTdLqchzvsq24TnR3nMW37mrNaOT0Z6SEapGoAExfu9hTD7MZHPx8Mew0xWK7ozecSJm5VSYBWOPX9f","uptime":739201,"anonymous_controller_id":"8c384c38-2342-4b3a-bba5-12f3c71d3704","has_webrtc_support":true,"debug_setting_preference":"auto","debug_mgmt":"warn","debug_system":"warn","debug_device":"warn","debug_sdn":"warn","ubnt_device_type":"UDMPROSE","udm_version":"3.2.7.14259","unsupported_device_count":0,"unsupported_device_list":[],"unifi_go_enabled":false,"default_site_device_auth_password_alert":false,"is_cloud_console":false,"console_display_version":"3.2.7"}]}
-----------------------------

Site info is: 
Array
(
    [0] => stdClass Object
        (
            [timezone] => America/Chicago
            [autobackup] => 
            [build] => atag_8.0.26_24388
            [version] => 8.0.26
            [previous_version] => 8.0.24
            [data_retention_days] => 90
            [data_retention_time_in_hours_for_5minutes_scale] => 24
            [data_retention_time_in_hours_for_hourly_scale] => 168
            [data_retention_time_in_hours_for_daily_scale] => 2160
            [data_retention_time_in_hours_for_monthly_scale] => 8760
            [data_retention_time_in_hours_for_others] => 2160
            [update_available] => 
            [update_downloaded] => 
            [live_chat] => super-only
            [store_enabled] => super-only
            [hostname] => Dream-Machine-Special-Edition
            [name] => Dream Machine Special Edition
            [ip_addrs] => Array
                (
                    [0] => 75.62.79.127
                )

            [inform_port] => 8080
            [https_port] => 8443
            [portal_http_port] => 8880
            [override_inform_host] => 
            [image_maps_use_google_engine] => 
            [radius_disconnect_running] => 
            [facebook_wifi_registered] => 
            [sso_app_id] => 90waTUDb0XgRdfyYlA7sM5xEGS90S0N3zyFbyo8v
            [sso_app_sec] => 8IVW8RyPBsqYLsPX9LPMmWMQxHU8E6wV8cX6C6iIlRnLT4bvuuTdLqchzvsq24TnR3nMW37mrNaOT0Z6SEapGoAExfu9hTD7MZHPx8Mew0xWK7ozecSJm5VSYBWOPX9f
            [uptime] => 739201
            [anonymous_controller_id] => 8c384c38-2342-4b3a-bba5-12f3c71d3704
            [has_webrtc_support] => 1
            [debug_setting_preference] => auto
            [debug_mgmt] => warn
            [debug_system] => warn
            [debug_device] => warn
            [debug_sdn] => warn
            [ubnt_device_type] => UDMPROSE
            [udm_version] => 3.2.7.14259
            [unsupported_device_count] => 0
            [unsupported_device_list] => Array
                (
                )

            [unifi_go_enabled] => 
            [default_site_device_auth_password_alert] => 
            [is_cloud_console] => 
            [console_display_version] => 3.2.7
        )

)

[Click here to go back]

When clicking test POST:

Unifi cookie exists and is set:
TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJmZWZjOWI1ZS1kNzc1LTQ2ZTMtYjJjZS1jMzk3NTM5MGEzZDQiLCJwYXNzd29yZFJldmlzaW9uIjoxNzAzNjYyMzY3LCJpc1JlbWVtYmVyZWQiOmZhbHNlLCJjc3JmVG9rZW4iOiIxYzAzMzI2Yi0wNGRhLTQyZTMtOGJlMi0zZTVlZmVkY2UwMTUiLCJpYXQiOjE3MDQ0MDE5NzEsImV4cCI6MTcwNDQwOTE3MSwianRpIjoiZjM3NTZiOGMtODk0OS00MWYxLTg3NzAtMzEyNDFmNDM5Yjk1In0.w4oc6alW2RVEKlUnL67UAGsCDG9O8c0cHPOj4ErxOOg
GET parameter cookie_test is set
Request method to test is: post
Logged in and setting the cookie
[Click here to test the cookie using GET]
[Click here to test the cookie using POST]
[Click here to modify a firewall rule]

---------cURL INFO-----------
Array
(
    [url] => https://192.168.0.1:443/proxy/network/api/s/default/cmd/devmgr
    [content_type] => application/json;charset=UTF-8
    [http_code] => 400
    [header_size] => 392
    [request_size] => 640
    [filetime] => -1
    [ssl_verify_result] => 18
    [redirect_count] => 0
    [total_time] => 0.014186
    [namelookup_time] => 0.000117
    [connect_time] => 0.00105
    [pretransfer_time] => 0.010975
    [size_upload] => 29
    [size_download] => 72
    [speed_download] => 5142
    [speed_upload] => 2071
    [download_content_length] => 72
    [upload_content_length] => 29
    [starttransfer_time] => 0.011436
    [redirect_time] => 0
    [redirect_url] => 
    [primary_ip] => 192.168.0.1
    [certinfo] => Array
        (
        )

    [primary_port] => 443
    [local_ip] => 192.168.0.252
    [local_port] => 62289
    [http_version] => 3
    [protocol] => 2
    [ssl_verifyresult] => 0
    [scheme] => HTTPS
    [appconnect_time_us] => 10470
    [connect_time_us] => 1050
    [namelookup_time_us] => 117
    [pretransfer_time_us] => 10975
    [redirect_time_us] => 0
    [starttransfer_time_us] => 11436
    [total_time_us] => 14186
)

-------URL & PAYLOAD---------
https://192.168.0.1:443/proxy/network/api/s/default/cmd/devmgr
{"cmd":"set-locate","mac":""}
----------RESPONSE-----------
{"meta":{"rc":"error","mac":"","msg":"api.err.UnknownDevice"},"data":[]}
-----------------------------

Locating results: 

[Click here to go back]
apadgett01 commented 6 months ago

At last, success! I've successfully incorporated the following code into your client.php file, and it's functioning as expected. I want to express my sincere gratitude for your assistance. I'm eager to delve deeper into the matter to understand why my function is returning a 403 error, whereas utilizing your curl calls in client.php yields a 200 response with the anticipated results.

    public function modifyFirewallRule($firewallRuleId, $enableRule)
    {
        $this->curl_method = 'PUT';
        $url = "/api/s/default/rest/firewallrule/{$firewallRuleId}";
        $payload = [
            '_id'      => $firewallRuleId,
            'enabled'  => $enableRule
            // Additional payload parameters can be added here if needed
        ];

        return $this->fetch_results($url, $payload);
    }
malle-pietje commented 6 months ago

Thanks for the feedback.

If you prefer to keep the API client up to date using composer, you can use the autoloader (highly recommended) instead of requiring it directly, and call this function which allows you to issue a custom request: https://github.com/Art-of-WiFi/UniFi-API-client/blob/master/src/Client.php#L3205

That way you’re not modifying client.php file itself. As you have experienced, the API client deals with the specific behavior and requirements of the API and we keep it updated when needed. That makes life easier for the developers and allows them to separate their custom logic from the logic that interacts with the API.

malle-pietje commented 6 months ago

As a closing practical remark: issues in this repo are meant to be related to use of the PHP API Client, we cannot support issues you might be encountering using other libraries, cURL or Postman. In the future such issues will be closed immediately.

For such cases please use the discussions section.

apadgett01 commented 6 months ago

Thank you for your assistance. I did not see that function and appreciate you pointing it out. I'm now a user of the PHP API Client you developed while using the custom function, and I now understand its benefits over a standalone script. I wish I had downloaded your latest update sooner, as it would have made things much easier. This was my first time posting on GitHub, and I'm still getting the hang of its formatting, especially for code. Please excuse any inconvenience I may have caused to your repository. Your help has been invaluable, particularly with the issues I found through online searches about update problems. This was the only solution. Your advice will greatly assist me in navigating the GitHub community more effectively. If you have a PayPal or similar channel for donations, I'd like to contribute as a token of appreciation for your work and the value it adds to the Unifi community.

malle-pietje commented 6 months ago

@apadgett01 Thanks for your comments. No problem, as long as others can also learn from this conversation 😉

Thanks for your kind offer, you can always send a donation to my PayPal address: erik AT slooff DOT net 👍

malle-pietje commented 6 months ago

@apadgett01 Thanks for the generous donation! Much appreciated and helps keeping me motivated 😉 🍻