posit-dev / publisher

MIT License
3 stars 0 forks source link

Add test-credentials endpoint #1837

Closed mmarchetti closed 1 week ago

mmarchetti commented 2 weeks ago

Intent

This PR adds a new /api/test-credentials endpoint that enables the front end to verify connectivity to a Connect server URL, and optionally verify a provided API key.

Fixes #1610

Type of Change

Automated Tests

Unit tests are included.

Directions for Reviewers

$(just executable-path) ui --listen=localhost:9001 &

# should return 200 with no user and no error
curl localhost:9001/api/test-credentials -XPOST -d '{"url":"https://rsc.radixu.com"}'

# should return 200 with your user information and no error
curl localhost:9001/api/test-credentials -XPOST -d '{"url":"https://rsc.radixu.com", "apiKey":"VALID_KEY_HERE"}'

# should return 200 with no user, and an error indicating you need to check the server URL or key
curl localhost:9001/api/test-credentials -XPOST -d '{"url":"https://bogus.com"}'

# should return 200 with no user, and an error indicating that the API key is invalid
curl localhost:9001/api/test-credentials -XPOST -d '{"url":"https://rsc.radixu.com", "apiKey":"BAD_KEY_HERE"}'
sagerb commented 1 week ago

Additional debug information for my verification issues:

Valid URL, Invalid API Key:

Request:

curl localhost:9001/api/test-credentials -XPOST -d '{"url":"https://connect.localtest.me/rsc/dev-password/", "apiKey":"VcxCPx3aErzgfDFEUTJS2zANIy4cmxZ0"}' | jq

Response:

{
  "user": null,
  "error": {
    "code": "unknown",
    "msg": "could not validate credentials; check server URL and API key or token",
    "operation": "",
    "data": {}
  }
}

Log:

time=2024-06-21T15:51:35.125-07:00 level=INFO msg="Testing authentication" method="Connect API key" url=https://connect.localtest.me/rsc/dev-password/
time=2024-06-21T15:51:36.127-07:00 level=DEBUG msg="API request" method=GET path=/__api__/v1/user body="" response="" error="Get \"https://connect.localtest.me/rsc/dev-password/__api__/v1/user\": net/http: request canceled (Client.Timeout exceeded while awaiting headers)"
time=2024-06-21T15:51:36.127-07:00 level=DEBUG msg="Server responded with error" error="Get \"https://connect.localtest.me/rsc/dev-password/__api__/v1/user\": net/http: request canceled (Client.Timeout exceeded while awaiting headers)"
time=2024-06-21T15:51:36.127-07:00 level=INFO msg="Access Log" method=POST url=/api/test-credentials elapsed_ms=1002 status=200 req_size=101 resp_size=144 client_addr=127.0.0.1:64506

Valid URL, no API key

Request:

curl localhost:9001/api/test-credentials -XPOST -d '{"url":"https://connect.localtest.me/rsc/dev-password/"}' | jq 

Response:

{
  "user": null,
  "error": {
    "code": "unknown",
    "msg": "could not validate credentials; check server URL and API key or token",
    "operation": "",
    "data": {}
  }
}

Log:

time=2024-06-21T15:54:04.355-07:00 level=INFO msg="Testing authentication" method="Connect API key" url=https://connect.localtest.me/rsc/dev-password/
time=2024-06-21T15:54:05.356-07:00 level=DEBUG msg="API request" method=GET path=/__api__/v1/user body="" response="" error="Get \"https://connect.localtest.me/rsc/dev-password/__api__/v1/user\": net/http: request canceled (Client.Timeout exceeded while awaiting headers)"
time=2024-06-21T15:54:05.356-07:00 level=DEBUG msg="Server responded with error" error="Get \"https://connect.localtest.me/rsc/dev-password/__api__/v1/user\": net/http: request canceled (Client.Timeout exceeded while awaiting headers)"
time=2024-06-21T15:54:05.356-07:00 level=INFO msg="Access Log" method=POST url=/api/test-credentials elapsed_ms=1001 status=200 req_size=56 resp_size=144 client_addr=127.0.0.1:64572

Invalid URL

Request:

curl localhost:9001/api/test-credentials -XPOST -d '{"url":"https://connect.localtest.me/rsc/dev-password222/"}' | jq

Response:

{
  "user": null,
  "error": {
    "code": "unknown",
    "msg": "could not validate credentials; check server URL and API key or token",
    "operation": "",
    "data": {}
  }
}

Log

time=2024-06-21T15:56:28.442-07:00 level=INFO msg="Testing authentication" method="Connect API key" url=https://connect.localtest.me/rsc/dev-password222/
time=2024-06-21T15:56:28.450-07:00 level=DEBUG msg="API request" method=GET path=/__api__/v1/user body="" response="" error="unexpected response from the server (404)"
time=2024-06-21T15:56:28.450-07:00 level=DEBUG msg="Server responded with error" error="unexpected response from the server (404)"
time=2024-06-21T15:56:28.450-07:00 level=INFO msg="Access Log" method=POST url=/api/test-credentials elapsed_ms=8 status=200 req_size=59 resp_size=144 client_addr=127.0.0.1:64612

Valid URL, Valid API Key

**Request:

curl localhost:9001/api/test-credentials -XPOST -d '{"url":"https://connect.localtest.me/rsc/dev-password/", "apiKey":"VcxCPx3aErzgfDFEUTJS2zANIy4cmxZb"}' | jq

**Response:

{
  "user": {
    "id": "0addad6f-25bc-46ec-b38d-afb08dc3415e",
    "username": "admin",
    "first_name": "admin",
    "last_name": "",
    "email": "admin@connect.com"
  },
  "error": null
}

**Log

time=2024-06-21T16:01:42.570-07:00 level=INFO msg="Testing authentication" method="Connect API key" url=https://connect.localtest.me/rsc/dev-password/
time=2024-06-21T16:01:42.580-07:00 level=DEBUG msg="API request" method=GET path=/__api__/v1/user body="" response="{\"email\":\"admin@connect.com\",\"username\":\"admin\",\"first_name\":\"admin\",\"last_name\":\"\",\"user_role\":\"administrator\",\"created_time\":\"2023-02-03T17:59:26Z\",\"updated_time\":\"2023-02-03T18:00:54Z\",\"active_time\":\"2024-06-21T22:56:52Z\",\"confirmed\":true,\"locked\":false,\"guid\":\"0addad6f-25bc-46ec-b38d-afb08dc3415e\"}" error=<nil>
time=2024-06-21T16:01:42.580-07:00 level=INFO msg="Access Log" method=POST url=/api/test-credentials elapsed_ms=10 status=200 req_size=101 resp_size=151 client_addr=127.0.0.1:64757
mmarchetti commented 1 week ago

I updated the default timeout so the errors you're seeing should be resolved.

mmarchetti commented 1 week ago

Is this to verify the URL and API before creating a Credential at all?

Yes. And, since we don't reveal API keys to the VSCode extension once created, we will eventually need a separate endpoint like /api/credentials/$NAME/test to re-verify existing credentials.

sagerb commented 1 week ago

Test cases work for me!