Repository: pycontribs/jira

Issue:
Need help authenticating properly with an API token
I am unable to create a simple script, sorry.

Everything I need works fine in the `jirashell`.
```
$ jirashell -s https://my-comp.atlassian.net -u 'user@my-comp.com' -p "plaintextAPItoken"

*** JIRA shell active; client is in 'jira'. Press Ctrl-D to exit.

In [1]: issue = jira.issue('DSLE-21230')

In [2]: print(issue.fields.summary)
Common: merge dcpc_snmp_rfc2662 into snmp_common_util
```
But my simple script:
```
$ cat jira-turbo.py
#!/usr/bin/python3
from collections import Counter
from jira import JIRA

# By default, the client will connect to a Jira instance started from the Atlassian Plugin SDK.
# See
# https://developer.atlassian.com/display/DOCS/Installing+the+Atlassian+Plugin+SDK
# for details.
jac = JIRA('https://my-comp.atlassian.net', basic_auth=('user@my-comp.net', "plaintextAPItoken"))

issue=jac.issue('DSLE-21230')
print(issue.fields.summary)
```
does not have permissions for that:
```
$ ./jira-turbo.py
Traceback (most recent call last):
  File "./jira-turbo.py", line 18, in <module>
    issue=jac.issue('DSLE-21230')
  File "/home/user/.local/lib/python3.6/site-packages/jira/client.py", line 1071, in issue
    issue.find(id, params=params)
  File "/home/user/.local/lib/python3.6/site-packages/jira/resources.py", line 201, in find
    self._load(url, params=params)
  File "/home/user/.local/lib/python3.6/site-packages/jira/resources.py", line 316, in _load
    r = self._session.get(url, headers=headers, params=params)
  File "/home/user/.local/lib/python3.6/site-packages/jira/resilientsession.py", line 151, in get
    return self.__verb('GET', url, **kwargs)
  File "/home/user/.local/lib/python3.6/site-packages/jira/resilientsession.py", line 147, in __verb
    raise_on_error(response, verb=verb, **kwargs)
  File "/home/user/.local/lib/python3.6/site-packages/jira/resilientsession.py", line 57, in raise_on_error
    r.status_code, error, r.url, request=request, response=r, **kwargs)
jira.exceptions.JIRAError: JiraError HTTP 404 url: https://my-comp.atlassian.net/rest/api/2/issue/DSLE-21230
        text: Issue does not exist or you do not have permission to see it.

        response headers = {'Server': 'AtlassianProxy/1.15.8.1', 'Vary': 'Accept-Encoding', 'Cache-Control': 'no-cache, no-store, no-transform', 'Content-Type': 'application/json;charset=UTF-8', 'Content-Encoding': 'gzip', 'Strict-Transport-Security': 'max-age=315360000; includeSubDomains; preload', 'Date': 'Tue, 07 Jul 2020 08:47:25 GMT', 'ATL-TraceId': '0915199bd5734db8', 'X-AREQUESTID': '06c237e4-b3a5-4586-a62d-850a27cf97b9', 'X-XSS-Protection': '1; mode=block', 'Transfer-Encoding': 'chunked', 'Timing-Allow-Origin': '*', 'X-Content-Type-Options': 'nosniff', 'Micros-Issuer': 'micros/edge-authenticator', 'Connection': 'keep-alive', 'Expect-CT': 'report-uri="https://web-security-reports.services.atlassian.com/expect-ct-report/global-proxy", enforce, max-age=86400'}
        response text = {"errorMessages":["Issue does not exist or you do not have permission to see it."],"errors":{}}
```
Changing the password makes it fail with:
```
ARNING:root:Got recoverable error from GET https://my-comp.atlassian.net/rest/api/2/serverInfo, will retry [1/3] in 14.01382243613401s. Err: 401 Unauthorized
```
So I have no idea what is happening.

Thank you very much in advance.


Discussion:

Resolving patch:
diff --git a/jira/resources.py b/jira/resources.py
--- a/jira/resources.py
+++ b/jira/resources.py
@@ -48,6 +48,7 @@ def emit(self, record):
     'SecurityLevel',
     'Status',
     'User',
+    'Group',
     'CustomFieldOption',
     'RemoteLink',
     'Customer',
@@ -764,6 +765,23 @@ def __eq__(self, other):
         return str(self.name) == str(other.name)
 
 
+class Group(Resource):
+    """A JIRA user group."""
+
+    def __init__(self, options, session, raw=None):
+        Resource.__init__(self, 'group?groupname={0}', options, session)
+        if raw:
+            self._parse_raw(raw)
+
+    def __hash__(self):
+        """Hash calculation."""
+        return hash(str(self.name))
+
+    def __eq__(self, other):
+        """Equality by name."""
+        return str(self.name) == str(other.name)
+
+
 class Version(Resource):
     """A version of a project."""
 
@@ -953,16 +971,26 @@ def dict2resource(raw, top=None, options=None, session=None):
     r'securitylevel/[^/]+$': SecurityLevel,
     r'status/[^/]+$': Status,
     r'user\?username.+$': User,
+    r'group\?groupname.+$': Group,
     r'version/[^/]+$': Version,
     # GreenHopper specific resources
     r'sprints/[^/]+$': Sprint,
     r'views/[^/]+$': Board}
 
 
+class UnknownResource(Resource):
+    """A Resource from JIRA that is not (yet) supported."""
+
+    def __init__(self, options, session, raw=None):
+        Resource.__init__(self, 'unknown{0}', options, session)
+        if raw:
+            self._parse_raw(raw)
+
+
 def cls_for_resource(resource_literal):
     for resource in resource_class_map:
         if re.search(resource, resource_literal):
             return resource_class_map[resource]
     else:
-        # Generic Resource without specialized update/delete behavior
-        return Resource
+        # Generic Resource cannot directly be used b/c of different constructor signature
+        return UnknownResource


Test patch:
diff --git a/tests/tests.py b/tests/tests.py
--- a/tests/tests.py
+++ b/tests/tests.py
@@ -56,7 +56,7 @@
 
 import jira  # noqa
 from jira import Role, Issue, JIRA, JIRAError, Project  # noqa
-from jira.resources import Resource, cls_for_resource   # noqa
+from jira.resources import Resource, cls_for_resource, Group, UnknownResource  # noqa
 
 TEST_ROOT = os.path.dirname(__file__)
 TEST_ICON_PATH = os.path.join(TEST_ROOT, 'icon.png')
@@ -417,7 +417,9 @@ def test_cls_for_resource(self):
         self.assertEqual(cls_for_resource('http://imaginary-jira.com/rest/\
                 api/latest/project/IMG/role/10002'), Role)
         self.assertEqual(cls_for_resource('http://customized-jira.com/rest/\
-                plugin-resource/4.5/json/getMyObject'), Resource)
+                plugin-resource/4.5/json/getMyObject'), UnknownResource)
+        self.assertEqual(cls_for_resource('http://customized-jira.com/rest/\
+                group?groupname=bla'), Group)
 
 
 @flaky