Repository: pallets/werkzeug

Issue:
Unauthorized() overrides www_authenticate=None
### Unauthorized in werkzeug==0.15.2
```python
class Unauthorized(HTTPException):
    """*401* ``Unauthorized``

    Raise if the user is not authorized to access a resource.

    The ``www_authenticate`` argument should be used to set the
    ``WWW-Authenticate`` header. This is used for HTTP basic auth and
    other schemes. Use :class:`~werkzeug.datastructures.WWWAuthenticate`
    to create correctly formatted values. Strictly speaking a 401
    response is invalid if it doesn't provide at least one value for
    this header, although real clients typically don't care.

    :param description: Override the default message used for the body
        of the response.
    :param www-authenticate: A single value, or list of values, for the
        WWW-Authenticate header.

    .. versionchanged:: 0.15.1
        ``description`` was moved back as the first argument, restoring
         its previous position.

    .. versionchanged:: 0.15.0
        ``www_authenticate`` was added as the first argument, ahead of
        ``description``.
    """

    code = 401
    description = (
        "The server could not verify that you are authorized to access"
        " the URL requested. You either supplied the wrong credentials"
        " (e.g. a bad password), or your browser doesn't understand"
        " how to supply the credentials required."
    )

    def __init__(self, description=None, www_authenticate=None):
        HTTPException.__init__(self, description)
        if not isinstance(www_authenticate, (tuple, list)):
            www_authenticate = (www_authenticate,)
        self.www_authenticate = www_authenticate

    def get_headers(self, environ=None):
        headers = HTTPException.get_headers(self, environ)
        if self.www_authenticate:
            headers.append(
                ("WWW-Authenticate", ", ".join([str(x) for x in self.www_authenticate]))
            )
        return headers
```
When ```www_authenticate=None```, self.www_authenticate will still be overridden and initialized. 

### Unauthorized in werkzeug==0.14.1
```python
class Unauthorized(HTTPException):

    """*401* `Unauthorized`
    Raise if the user is not authorized.  Also used if you want to use HTTP
    basic auth.
    """
    code = 401
    description = (
        'The server could not verify that you are authorized to access '
        'the URL requested.  You either supplied the wrong credentials (e.g. '
        'a bad password), or your browser doesn\'t understand how to supply '
        'the credentials required.'
    )
```
Unauthorized() overrides www_authenticate=None
### Unauthorized in werkzeug==0.15.2
```python
class Unauthorized(HTTPException):
    """*401* ``Unauthorized``

    Raise if the user is not authorized to access a resource.

    The ``www_authenticate`` argument should be used to set the
    ``WWW-Authenticate`` header. This is used for HTTP basic auth and
    other schemes. Use :class:`~werkzeug.datastructures.WWWAuthenticate`
    to create correctly formatted values. Strictly speaking a 401
    response is invalid if it doesn't provide at least one value for
    this header, although real clients typically don't care.

    :param description: Override the default message used for the body
        of the response.
    :param www-authenticate: A single value, or list of values, for the
        WWW-Authenticate header.

    .. versionchanged:: 0.15.1
        ``description`` was moved back as the first argument, restoring
         its previous position.

    .. versionchanged:: 0.15.0
        ``www_authenticate`` was added as the first argument, ahead of
        ``description``.
    """

    code = 401
    description = (
        "The server could not verify that you are authorized to access"
        " the URL requested. You either supplied the wrong credentials"
        " (e.g. a bad password), or your browser doesn't understand"
        " how to supply the credentials required."
    )

    def __init__(self, description=None, www_authenticate=None):
        HTTPException.__init__(self, description)
        if not isinstance(www_authenticate, (tuple, list)):
            www_authenticate = (www_authenticate,)
        self.www_authenticate = www_authenticate

    def get_headers(self, environ=None):
        headers = HTTPException.get_headers(self, environ)
        if self.www_authenticate:
            headers.append(
                ("WWW-Authenticate", ", ".join([str(x) for x in self.www_authenticate]))
            )
        return headers
```
When ```www_authenticate=None```, self.www_authenticate will still be overridden and initialized. 

### Unauthorized in werkzeug==0.14.1
```python
class Unauthorized(HTTPException):

    """*401* `Unauthorized`
    Raise if the user is not authorized.  Also used if you want to use HTTP
    basic auth.
    """
    code = 401
    description = (
        'The server could not verify that you are authorized to access '
        'the URL requested.  You either supplied the wrong credentials (e.g. '
        'a bad password), or your browser doesn\'t understand how to supply '
        'the credentials required.'
    )
```


Discussion:

Resolving patch:
diff --git a/src/werkzeug/exceptions.py b/src/werkzeug/exceptions.py
--- a/src/werkzeug/exceptions.py
+++ b/src/werkzeug/exceptions.py
@@ -251,7 +251,12 @@ class Unauthorized(HTTPException):
     :param www-authenticate: A single value, or list of values, for the
         WWW-Authenticate header.
 
-    .. versionchanged:: 0.15.2
+    .. versionchanged:: 0.15.3
+        If the ``www_authenticate`` argument is not set, the
+        ``WWW-Authenticate`` header is not set.
+
+    .. versionchanged:: 0.15.3
+        The ``response`` argument was restored.
 
     .. versionchanged:: 0.15.1
         ``description`` was moved back as the first argument, restoring
@@ -272,8 +277,11 @@ class Unauthorized(HTTPException):
 
     def __init__(self, description=None, response=None, www_authenticate=None):
         HTTPException.__init__(self, description, response)
-        if not isinstance(www_authenticate, (tuple, list)):
-            www_authenticate = (www_authenticate,)
+
+        if www_authenticate is not None:
+            if not isinstance(www_authenticate, (tuple, list)):
+                www_authenticate = (www_authenticate,)
+
         self.www_authenticate = www_authenticate
 
     def get_headers(self, environ=None):


Test patch:
diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py
--- a/tests/test_exceptions.py
+++ b/tests/test_exceptions.py
@@ -110,3 +110,7 @@ def test_unauthorized_www_authenticate():
     exc = exceptions.Unauthorized(www_authenticate=[digest, basic])
     h = dict(exc.get_headers({}))
     assert h["WWW-Authenticate"] == ", ".join((str(digest), str(basic)))
+
+    exc = exceptions.Unauthorized()
+    h = dict(exc.get_headers({}))
+    assert "WWW-Authenticate" not in h