Repository: conan-io/conan

Issue:
"conan user" is broken in 1.20.0
17:52:59 alex@Totem:/ $ conan user -c
WARN: Migration: Updating settings.yml
WARN: ****************************************
WARN: settings.yml is locally modified, can't be updated
WARN: The new settings.yml has been stored in: /Users/alex/.conan/settings.yml.new
WARN: ****************************************
17:53:04 alex@Totem:/ $ conan user -r REMOTE -p PASS USER
ERROR: {
  "errors" : [ {
    "status" : 401,
    "message" : "Authentication is required"
  } ]
}. [Remote: REMOTE]

The exact same commands succeed after downgrading to 1.19.3.


Discussion:

Resolving patch:
diff --git a/conans/client/rest/rest_client.py b/conans/client/rest/rest_client.py
--- a/conans/client/rest/rest_client.py
+++ b/conans/client/rest/rest_client.py
@@ -1,7 +1,7 @@
 from conans import CHECKSUM_DEPLOY, REVISIONS, ONLY_V2, OAUTH_TOKEN
 from conans.client.rest.rest_client_v1 import RestV1Methods
 from conans.client.rest.rest_client_v2 import RestV2Methods
-from conans.errors import OnlyV2Available
+from conans.errors import OnlyV2Available, AuthenticationException
 from conans.search.search import filter_packages
 from conans.util.log import logger
 
@@ -95,7 +95,13 @@ def authenticate(self, user, password):
         if self.refresh_token and self.token:
             token, refresh_token = api_v1.refresh_token(self.token, self.refresh_token)
         else:
-            if self._capable(OAUTH_TOKEN):
+            try:
+                # Check capabilities can raise also 401 until the new Artifactory is released
+                oauth_capable = self._capable(OAUTH_TOKEN)
+            except AuthenticationException:
+                oauth_capable = False
+
+            if oauth_capable:
                 # Artifactory >= 6.13.X
                 token, refresh_token = api_v1.authenticate_oauth(user, password)
             else:


Test patch:
diff --git a/conans/test/functional/remote/auth_test.py b/conans/test/functional/remote/auth_test.py
--- a/conans/test/functional/remote/auth_test.py
+++ b/conans/test/functional/remote/auth_test.py
@@ -1,10 +1,14 @@
 import os
 import unittest
 
+from requests.models import Response
+
 from conans.client import tools
+from conans.errors import AuthenticationException
 from conans.model.ref import ConanFileReference
 from conans.paths import CONANFILE
-from conans.test.utils.tools import TestClient, TestServer
+from conans.test.utils.tools import TestClient
+from conans.test.utils.tools import TestServer
 from conans.util.files import save
 
 conan_content = """
@@ -119,3 +123,33 @@ def no_client_username_checks_test(self):
 
         # Check that login failed once before ok
         self.assertEqual(self.conan.api.app.user_io.login_index["default"], 2)
+
+
+class AuthenticationTest(unittest.TestCase):
+
+    def unauthorized_during_capabilities_test(self):
+
+        class RequesterMock(object):
+
+            def __init__(self, *args, **kwargs):
+                pass
+
+            def get(self, url, *args, **kwargs):
+                if "authenticate" in url:
+                    resp_basic_auth = Response()
+                    resp_basic_auth._content = b"TOKEN"
+                    resp_basic_auth.status_code = 200
+                    resp_basic_auth.headers = {"Content-Type": "text/plain"}
+                    return resp_basic_auth
+
+                if "ping" in url and not kwargs["auth"].token:
+                    raise AuthenticationException(
+                        "I'm an Artifactory without anonymous access that "
+                        "requires authentication for the ping endpoint and "
+                        "I don't return the capabilities")
+                raise Exception("Shouldn't be more remote calls")
+
+        self.client = TestClient(requester_class=RequesterMock, default_server_user=True)
+        self.client.run("user user -p password -r default")
+        self.assertIn("Changed user of remote 'default' from 'None' (anonymous) to 'user'",
+                      self.client.out)