UniFi-API-client: Unable to delete radius account.

I am unable to delete radius account.

UDM Pro v. 1.8.4 UniFi Network v. 6.0.43 (Build: atag_6.0.43_14348)

List radius accounts:

-------URL & PAYLOAD---------
https://10.28.0.167/proxy/network/api/s/default/rest/account
empty payload
----------RESPONSE-----------
{"meta":{"rc":"ok"},"data":[{"_id":"5fefc567c8028604215ed7cf","name":"john.doe","x_password":"secret-password","tunnel_type":13,"tunnel_medium_type":6,"vlan":2810,"site_id":"12345679123456789123456"}]}
-----------------------------
</pre>
[
    {
        "_id": "5fefc567c8028604215ed7cf",
        "name": "john.doe",
        "x_password": "secret-password",
        "tunnel_type": 13,
        "tunnel_medium_type": 6,
        "vlan": 2810,
        "site_id": "12345679123456789123456"
    }
]

Trying to delete that account:

<pre>
---------cURL INFO-----------
Array
(
    [url] => https://10.28.0.167/proxy/network/api/s/default/rest/account/5fefc567c8028604215ed7cf
    [content_type] => text/plain; charset=utf-8
    [http_code] => 404
    [header_size] => 473
    [request_size] => 426
    [filetime] => -1
    [ssl_verify_result] => 18
    [redirect_count] => 0
    [total_time] => 0.039487
    [namelookup_time] => 1.8E-5
    [connect_time] => 0.000208
    [pretransfer_time] => 0.02417
    [size_upload] => 0
    [size_download] => 9
    [speed_download] => 227
    [speed_upload] => 0
    [download_content_length] => 9
    [upload_content_length] => -1
    [starttransfer_time] => 0.039404
    [redirect_time] => 0
    [redirect_url] => 
    [primary_ip] => 10.28.0.167
    [certinfo] => Array
        (
        )

    [primary_port] => 443
    [local_ip] => 10.28.0.51
    [local_port] => 54870
)

-------URL & PAYLOAD---------
https://10.28.0.167/proxy/network/api/s/default/rest/account/5fefc567c8028604215ed7cf
empty payload
----------RESPONSE-----------
Not Found
-----------------------------
</pre>
PHP Notice:  JSON decode error: Syntax error, malformed JSON in /path/to/git/UniFi-API-client/src/Client.php on line 3606

account deletetion FAILED ...

Used code:

/**
 * initialize the UniFi API connection class and log in to the controller and do our thing
 */
$unifi_connection = new UniFi_API\Client($controlleruser, $controllerpassword, $controllerurl, $site_id, $controllerversion);
$set_debug_mode   = $unifi_connection->set_debug($debug);
$loginresults     = $unifi_connection->login();
if ($loginresults === 400) {
        print "UniFi controller login failure, please check your credentials in config.php.\n";
        exit(-1);
} 

$data             = $unifi_connection->list_radius_accounts();
/**
 * provide feedback in json format
 */
echo json_encode($data, JSON_PRETTY_PRINT);
$account_id = '5fefc567c8028604215ed7cf';

if ($unifi_connection->delete_radius_account($account_id)) {
        echo "\naccount deletion succeeds ...\n";
}
else {
        echo "\naccount deletetion FAILED ...\n";
}

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 16

Commits related to this issue

Most upvoted comments

@paciks Can you see whether this version of the exec_curl function/method fixes the issue?

    /**
     * Execute the cURL request
     *
     * @param  string       $path    path for the request
     * @param  object|array $payload optional, payload to pass with the request
     * @return bool|array            response returned by the controller API
     */
    protected function exec_curl($path, $payload = null)
    {
        if (!in_array($this->request_type, $this->request_types_allowed)) {
            trigger_error('an invalid HTTP request type was used: ' . $this->request_type);
        }

        if (!($ch = $this->get_curl_resource())) {
            trigger_error('$ch as returned by get_curl_resource() is not a resource');

            return false;
        }

        /**
         * assigne default values to these vars
         */
        $json_payload = '';
        $headers      = [];

        if ($this->is_unifi_os) {
            $url = $this->baseurl . '/proxy/network' . $path;
        } else {
            $url = $this->baseurl . $path;
        }

        /**
         * prepare cURL options
         */
        $curl_options = [
            CURLOPT_URL => $url
        ];

        if (!is_null($payload)) {
            $json_payload                     = json_encode($payload, JSON_UNESCAPED_SLASHES);
            $curl_options[CURLOPT_POST]       = true;
            $curl_options[CURLOPT_POSTFIELDS] = $json_payload;

            $headers = [
                'Content-Type: application/json',
                'Content-Length: ' . strlen($json_payload)
            ];

            /**
             * we shouldn't be using GET (the default request type) or DELETE when passing a payload,
             * switch to POST instead
             */
            switch ($this->request_type) {
                case 'GET':
                    $this->request_type = 'POST';
                    break;
                case 'DELETE':
                    $this->request_type = 'POST';
                    break;
                case 'PUT':
                    $curl_options[CURLOPT_CUSTOMREQUEST] = 'PUT';
                    break;
            }
        }

        switch ($this->request_type) {
            case 'DELETE':
                $curl_options[CURLOPT_CUSTOMREQUEST] = 'DELETE';
                break;
            case 'PATCH':
                $curl_options[CURLOPT_CUSTOMREQUEST] = 'PATCH';
                break;
            case 'POST':
                $curl_options[CURLOPT_CUSTOMREQUEST] = 'POST';
                break;
        }

        if ($this->is_unifi_os && $this->request_type !== 'GET') {
            $csrf_token = $this->extract_csrf_token_from_cookie();
            if ($csrf_token) {
                $headers[] = 'x-csrf-token: ' . $csrf_token;
            }
        }

        if (count($headers) > 0) {
            $curl_options[CURLOPT_HTTPHEADER] = $headers;
        }

        curl_setopt_array($ch, $curl_options);

        /**
         * execute the cURL request
         */
        $content = curl_exec($ch);
        if (curl_errno($ch)) {
            trigger_error('cURL error: ' . curl_error($ch));
        }

        /**
         * fetch the HTTP response code
         */
        $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);

        /**
         * an HTTP response code 401 (Unauthorized) indicates the Cookie/Token has expired in which case
         * we need to login again.
         */
        if ($http_code == 401) {
            if ($this->debug) {
                error_log(__FUNCTION__ . ': needed to reconnect to UniFi controller');
            }

            if ($this->exec_retries == 0) {
                /**
                 * explicitly clear the expired Cookie/Token, update other properties and log out before logging in again
                 */
                if (isset($_SESSION['unificookie'])) {
                    $_SESSION['unificookie'] = '';
                }

                $this->is_loggedin = false;
                $this->exec_retries++;
                curl_close($ch);

                /**
                 * then login again
                 */
                $this->login();

                /**
                 * when re-login was successful, simply execute the same cURL request again
                 */
                if ($this->is_loggedin) {
                    if ($this->debug) {
                        error_log(__FUNCTION__ . ': re-logged in, calling exec_curl again');
                    }

                    return $this->exec_curl($path, $payload);
                }

                if ($this->debug) {
                    error_log(__FUNCTION__ . ': re-login failed');
                }
            }

            return false;
        }

        if ($this->debug) {
            print PHP_EOL . '<pre>';
            print PHP_EOL . '---------cURL INFO-----------' . PHP_EOL;
            print_r(curl_getinfo($ch));
            print PHP_EOL . '-------URL & PAYLOAD---------' . PHP_EOL;
            print $url . PHP_EOL;
            if (empty($json_payload)) {
                print 'empty payload';
            } else {
                print $json_payload;
            }

            print PHP_EOL . '----------RESPONSE-----------' . PHP_EOL;
            print $content;
            print PHP_EOL . '-----------------------------' . PHP_EOL;
            print '</pre>' . PHP_EOL;
        }

        curl_close($ch);

        /**
         * set request_type value back to default, just in case
         */
        $this->request_type = 'GET';

        return $content;
    }

I am not an expert but I was able to reproduce “NOT FOUND” response playing with BurpSuite, to do that I had to modify x-csrf-token in repeated request:

DELETE /proxy/network/api/s/default/rest/account/5ff33eaf1cba35042641e806 HTTP/1.1
Host: 10.28.0.167
Connection: close
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36
x-csrf-token: 8ba0c82d-f803-4c89-b8ea-67a31a906b6c
Accept: */*
Origin: https://10.28.0.167
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://10.28.0.167/network/site/default/settings/services/radius/users/list
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Cookie: _delighted_web={%22pEYwuKMrCvrhUQyO%22:{%22_delighted_fst%22:{%22t%22:%221609774625325%22}}}; TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjc3JmVG9rZW4iOiI4YmEwYzgyZC1mODAzLTRjODktYjhlYS02N2EzMWE5MDZiNmIiLCJ1c2VySWQiOiJhZTM5YTk1Yi1hMzdlLTRiOGUtOTc5Yi0zOTBkOWM2M2E0NmQiLCJwYXNzd29yZFJldmlzaW9uIjoxNjA4MDM4NDA4LCJpYXQiOjE2MDk3NzQ5ODEsImV4cCI6MTYwOTc3ODU4MX0.CXv_czQu-WSC4wvyJrzNKM-vtCCa13JBQZsa8QvT9cQ

HTTP/1.1 404 Not Found
Vary: Origin
Access-Control-Allow-Origin: https://10.28.0.167
X-DNS-Prefetch-Control: off
X-Frame-Options: SAMEORIGIN
Strict-Transport-Security: max-age=15552000; includeSubDomains
X-Download-Options: noopen
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Accept-Ranges: bytes
X-CSRF-Token: 8ba0c82d-f803-4c89-b8ea-67a31a906b6b
X-Response-Time: 4ms
Content-Type: text/plain; charset=utf-8
Content-Length: 9
Date: Mon, 04 Jan 2021 16:13:45 GMT
Connection: close

Not Found

And with the correct value of x-csfr-token:

DELETE /proxy/network/api/s/default/rest/account/5ff33eaf1cba35042641e806 HTTP/1.1
Host: 10.28.0.167
Connection: close
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36
x-csrf-token: 8ba0c82d-f803-4c89-b8ea-67a31a906b6b
Accept: */*
Origin: https://10.28.0.167
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://10.28.0.167/network/site/default/settings/services/radius/users/list
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Cookie: _delighted_web={%22pEYwuKMrCvrhUQyO%22:{%22_delighted_fst%22:{%22t%22:%221609774625325%22}}}; TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjc3JmVG9rZW4iOiI4YmEwYzgyZC1mODAzLTRjODktYjhlYS02N2EzMWE5MDZiNmIiLCJ1c2VySWQiOiJhZTM5YTk1Yi1hMzdlLTRiOGUtOTc5Yi0zOTBkOWM2M2E0NmQiLCJwYXNzd29yZFJldmlzaW9uIjoxNjA4MDM4NDA4LCJpYXQiOjE2MDk3NzQ5ODEsImV4cCI6MTYwOTc3ODU4MX0.CXv_czQu-WSC4wvyJrzNKM-vtCCa13JBQZsa8QvT9cQ

HTTP/1.1 200 OK
Vary: Origin
Access-Control-Allow-Origin: https://10.28.0.167
X-DNS-Prefetch-Control: off
x-frame-options: DENY
Strict-Transport-Security: max-age=15552000; includeSubDomains
X-Download-Options: noopen
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Accept-Ranges: bytes
content-type: application/json;charset=UTF-8
content-length: 30
date: Mon, 04 Jan 2021 16:14:13 GMT
connection: close
Set-Cookie: TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjc3JmVG9rZW4iOiI4YmEwYzgyZC1mODAzLTRjODktYjhlYS02N2EzMWE5MDZiNmIiLCJ1c2VySWQiOiJhZTM5YTk1Yi1hMzdlLTRiOGUtOTc5Yi0zOTBkOWM2M2E0NmQiLCJwYXNzd29yZFJldmlzaW9uIjoxNjA4MDM4NDA4LCJpYXQiOjE2MDk3NzY4NTQsImV4cCI6MTYwOTc4MDQ1NH0.N8KpTqXTLbWS2p3wbAPWYkp4ELeoKB6keDIzWIQDnC0; path=/; secure; httponly

{"meta":{"rc":"ok"},"data":[]}

Modified value: x-csrf-token: 8ba0c82d-f803-4c89-b8ea-67a31a906b6c Correct value: x-csrf-token: 8ba0c82d-f803-4c89-b8ea-67a31a906b6b

When I was trying to DELETE already (deleted above) non-existed account the response was:

DELETE /proxy/network/api/s/default/rest/account/5ff33eaf1cba35042641e806 HTTP/1.1
HTTP/1.1 400 Bad Request
Vary: Origin
Access-Control-Allow-Origin: https://10.28.0.167
X-DNS-Prefetch-Control: off
x-frame-options: DENY
Strict-Transport-Security: max-age=15552000; includeSubDomains
X-Download-Options: noopen
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Accept-Ranges: bytes
content-type: application/json;charset=UTF-8
content-length: 59
date: Mon, 04 Jan 2021 16:19:36 GMT
connection: close
Set-Cookie: TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjc3JmVG9rZW4iOiI4YmEwYzgyZC1mODAzLTRjODktYjhlYS02N2EzMWE5MDZiNmIiLCJ1c2VySWQiOiJhZTM5YTk1Yi1hMzdlLTRiOGUtOTc5Yi0zOTBkOWM2M2E0NmQiLCJwYXNzd29yZFJldmlzaW9uIjoxNjA4MDM4NDA4LCJpYXQiOjE2MDk3NzcxNzUsImV4cCI6MTYwOTc4MDc3NX0.LjmPw1XfMRPWGjCmCCmEgJYBxSO8HnR1IrXLj7H4mcQ; path=/; secure; httponly

{"meta":{"rc":"error","msg":"api.err.IdInvalid"},"data":[]}

I hope it is helpful.