mirror of
https://github.com/certbot/certbot.git
synced 2026-07-30 18:04:31 +02:00
Merge branch 'master' into renewer
This commit is contained in:
+1
-7
@@ -18,11 +18,9 @@ class Error(jose.JSONObjectWithFields, Exception):
|
|||||||
'badCSR': 'The CSR is unacceptable (e.g., due to a short key)',
|
'badCSR': 'The CSR is unacceptable (e.g., due to a short key)',
|
||||||
}
|
}
|
||||||
|
|
||||||
# TODO: Boulder omits 'type' and 'instance', spec requires, boulder#128
|
typ = jose.Field('type')
|
||||||
typ = jose.Field('type', omitempty=True)
|
|
||||||
title = jose.Field('title', omitempty=True)
|
title = jose.Field('title', omitempty=True)
|
||||||
detail = jose.Field('detail')
|
detail = jose.Field('detail')
|
||||||
instance = jose.Field('instance', omitempty=True)
|
|
||||||
|
|
||||||
@typ.encoder
|
@typ.encoder
|
||||||
def typ(value): # pylint: disable=missing-docstring,no-self-argument
|
def typ(value): # pylint: disable=missing-docstring,no-self-argument
|
||||||
@@ -227,10 +225,6 @@ class Authorization(ResourceBody):
|
|||||||
challenges = jose.Field('challenges', omitempty=True)
|
challenges = jose.Field('challenges', omitempty=True)
|
||||||
combinations = jose.Field('combinations', omitempty=True)
|
combinations = jose.Field('combinations', omitempty=True)
|
||||||
|
|
||||||
# TODO: acme-spec #92, #98
|
|
||||||
key = Registration._fields['key']
|
|
||||||
contact = Registration._fields['contact']
|
|
||||||
|
|
||||||
status = jose.Field('status', omitempty=True, decoder=Status.from_json)
|
status = jose.Field('status', omitempty=True, decoder=Status.from_json)
|
||||||
# TODO: 'expires' is allowed for Authorization Resources in
|
# TODO: 'expires' is allowed for Authorization Resources in
|
||||||
# general, but for Key Authorization '[t]he "expires" field MUST
|
# general, but for Key Authorization '[t]he "expires" field MUST
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ class ErrorTest(unittest.TestCase):
|
|||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
from acme.messages2 import Error
|
from acme.messages2 import Error
|
||||||
self.error = Error(detail='foo', typ='malformed')
|
self.error = Error(detail='foo', typ='malformed', title='title')
|
||||||
|
self.jobj = {'detail': 'foo', 'title': 'some title'}
|
||||||
|
|
||||||
def test_typ_prefix(self):
|
def test_typ_prefix(self):
|
||||||
self.assertEqual('malformed', self.error.typ)
|
self.assertEqual('malformed', self.error.typ)
|
||||||
@@ -32,15 +33,15 @@ class ErrorTest(unittest.TestCase):
|
|||||||
|
|
||||||
def test_typ_decoder_missing_prefix(self):
|
def test_typ_decoder_missing_prefix(self):
|
||||||
from acme.messages2 import Error
|
from acme.messages2 import Error
|
||||||
self.assertRaises(jose.DeserializationError, Error.from_json,
|
self.jobj['type'] = 'malformed'
|
||||||
{'detail': 'foo', 'type': 'malformed'})
|
self.assertRaises(jose.DeserializationError, Error.from_json, self.jobj)
|
||||||
self.assertRaises(jose.DeserializationError, Error.from_json,
|
self.jobj['type'] = 'not valid bare type'
|
||||||
{'detail': 'foo', 'type': 'not valid bare type'})
|
self.assertRaises(jose.DeserializationError, Error.from_json, self.jobj)
|
||||||
|
|
||||||
def test_typ_decoder_not_recognized(self):
|
def test_typ_decoder_not_recognized(self):
|
||||||
from acme.messages2 import Error
|
from acme.messages2 import Error
|
||||||
self.assertRaises(jose.DeserializationError, Error.from_json,
|
self.jobj['type'] = 'urn:acme:error:baz'
|
||||||
{'detail': 'foo', 'type': 'urn:acme:error:baz'})
|
self.assertRaises(jose.DeserializationError, Error.from_json, self.jobj)
|
||||||
|
|
||||||
def test_description(self):
|
def test_description(self):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
|||||||
@@ -115,6 +115,7 @@ class Network(object):
|
|||||||
:rtype: `requests.Response`
|
:rtype: `requests.Response`
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
logging.debug('Sending GET request to %s', uri)
|
||||||
try:
|
try:
|
||||||
response = requests.get(uri, **kwargs)
|
response = requests.get(uri, **kwargs)
|
||||||
except requests.exceptions.RequestException as error:
|
except requests.exceptions.RequestException as error:
|
||||||
@@ -133,12 +134,12 @@ class Network(object):
|
|||||||
:rtype: `requests.Response`
|
:rtype: `requests.Response`
|
||||||
|
|
||||||
"""
|
"""
|
||||||
logging.debug('Sending POST data: %s', data)
|
logging.debug('Sending POST data to %s: %s', uri, data)
|
||||||
try:
|
try:
|
||||||
response = requests.post(uri, data=data, **kwargs)
|
response = requests.post(uri, data=data, **kwargs)
|
||||||
except requests.exceptions.RequestException as error:
|
except requests.exceptions.RequestException as error:
|
||||||
raise errors.NetworkError(error)
|
raise errors.NetworkError(error)
|
||||||
logging.debug('Received response %s: %s', response, response.text)
|
logging.debug('Received response %s: %r', response, response.text)
|
||||||
|
|
||||||
self._check_response(response, content_type=content_type)
|
self._check_response(response, content_type=content_type)
|
||||||
return response
|
return response
|
||||||
@@ -247,6 +248,7 @@ class Network(object):
|
|||||||
|
|
||||||
def _authzr_from_response(self, response, identifier,
|
def _authzr_from_response(self, response, identifier,
|
||||||
uri=None, new_cert_uri=None):
|
uri=None, new_cert_uri=None):
|
||||||
|
# pylint: disable=no-self-use
|
||||||
if new_cert_uri is None:
|
if new_cert_uri is None:
|
||||||
try:
|
try:
|
||||||
new_cert_uri = response.links['next']['url']
|
new_cert_uri = response.links['next']['url']
|
||||||
@@ -257,8 +259,7 @@ class Network(object):
|
|||||||
body=messages2.Authorization.from_json(response.json()),
|
body=messages2.Authorization.from_json(response.json()),
|
||||||
uri=response.headers.get('Location', uri),
|
uri=response.headers.get('Location', uri),
|
||||||
new_cert_uri=new_cert_uri)
|
new_cert_uri=new_cert_uri)
|
||||||
if (authzr.body.key != self.key.public()
|
if authzr.body.identifier != identifier:
|
||||||
or authzr.body.identifier != identifier):
|
|
||||||
raise errors.UnexpectedUpdate(authzr)
|
raise errors.UnexpectedUpdate(authzr)
|
||||||
return authzr
|
return authzr
|
||||||
|
|
||||||
|
|||||||
@@ -276,8 +276,6 @@ class PollChallengesTest(unittest.TestCase):
|
|||||||
identifier=authzr.body.identifier,
|
identifier=authzr.body.identifier,
|
||||||
challenges=new_challbs,
|
challenges=new_challbs,
|
||||||
combinations=authzr.body.combinations,
|
combinations=authzr.body.combinations,
|
||||||
key=authzr.body.key,
|
|
||||||
contact=authzr.body.contact,
|
|
||||||
status=status_,
|
status=status_,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ class NetworkTest(unittest.TestCase):
|
|||||||
self.authz = messages2.Authorization(
|
self.authz = messages2.Authorization(
|
||||||
identifier=messages2.Identifier(
|
identifier=messages2.Identifier(
|
||||||
typ=messages2.IDENTIFIER_FQDN, value='example.com'),
|
typ=messages2.IDENTIFIER_FQDN, value='example.com'),
|
||||||
challenges=(challb,), combinations=None, key=KEY.public())
|
challenges=(challb,), combinations=None)
|
||||||
self.authzr = messages2.AuthorizationResource(
|
self.authzr = messages2.AuthorizationResource(
|
||||||
body=self.authz, uri=authzr_uri,
|
body=self.authz, uri=authzr_uri,
|
||||||
new_cert_uri='https://www.letsencrypt-demo.org/acme/new-cert')
|
new_cert_uri='https://www.letsencrypt-demo.org/acme/new-cert')
|
||||||
@@ -114,7 +114,8 @@ class NetworkTest(unittest.TestCase):
|
|||||||
|
|
||||||
def test_check_response_not_ok_jobj_error(self):
|
def test_check_response_not_ok_jobj_error(self):
|
||||||
self.response.ok = False
|
self.response.ok = False
|
||||||
self.response.json.return_value = messages2.Error(detail='foo')
|
self.response.json.return_value = messages2.Error(
|
||||||
|
detail='foo', typ='serverInternal', title='some title').to_json()
|
||||||
# pylint: disable=protected-access
|
# pylint: disable=protected-access
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
messages2.Error, self.net._check_response, self.response)
|
messages2.Error, self.net._check_response, self.response)
|
||||||
@@ -258,11 +259,10 @@ class NetworkTest(unittest.TestCase):
|
|||||||
# TODO: test POST call arguments
|
# TODO: test POST call arguments
|
||||||
|
|
||||||
# TODO: split here and separate test
|
# TODO: split here and separate test
|
||||||
authz_wrong_key = self.authz.update(key=KEY2.public())
|
self.response.json.return_value = self.authz.update(
|
||||||
self.response.json.return_value = authz_wrong_key.to_json()
|
identifier=self.identifier.update(value='foo')).to_json()
|
||||||
self.assertRaises(
|
self.assertRaises(errors.UnexpectedUpdate, self.net.request_challenges,
|
||||||
errors.UnexpectedUpdate, self.net.request_challenges,
|
self.identifier, self.authzr.uri)
|
||||||
self.identifier, self.regr)
|
|
||||||
|
|
||||||
def test_request_challenges_missing_next(self):
|
def test_request_challenges_missing_next(self):
|
||||||
self.response.status_code = httplib.CREATED
|
self.response.status_code = httplib.CREATED
|
||||||
@@ -336,6 +336,11 @@ class NetworkTest(unittest.TestCase):
|
|||||||
self.assertEqual((self.authzr, self.response),
|
self.assertEqual((self.authzr, self.response),
|
||||||
self.net.poll(self.authzr))
|
self.net.poll(self.authzr))
|
||||||
|
|
||||||
|
# TODO: split here and separate test
|
||||||
|
self.response.json.return_value = self.authz.update(
|
||||||
|
identifier=self.identifier.update(value='foo')).to_json()
|
||||||
|
self.assertRaises(errors.UnexpectedUpdate, self.net.poll, self.authzr)
|
||||||
|
|
||||||
def test_request_issuance(self):
|
def test_request_issuance(self):
|
||||||
self.response.content = CERT.as_der()
|
self.response.content = CERT.as_der()
|
||||||
self.response.headers['Location'] = self.certr.uri
|
self.response.headers['Location'] = self.certr.uri
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import string
|
|||||||
|
|
||||||
from pyparsing import (
|
from pyparsing import (
|
||||||
Literal, White, Word, alphanums, CharsNotIn, Forward, Group,
|
Literal, White, Word, alphanums, CharsNotIn, Forward, Group,
|
||||||
Optional, OneOrMore, ZeroOrMore, pythonStyleComment)
|
Optional, OneOrMore, Regex, ZeroOrMore, pythonStyleComment)
|
||||||
|
|
||||||
|
|
||||||
class RawNginxParser(object):
|
class RawNginxParser(object):
|
||||||
@@ -16,17 +16,21 @@ class RawNginxParser(object):
|
|||||||
semicolon = Literal(";").suppress()
|
semicolon = Literal(";").suppress()
|
||||||
space = White().suppress()
|
space = White().suppress()
|
||||||
key = Word(alphanums + "_/")
|
key = Word(alphanums + "_/")
|
||||||
value = CharsNotIn("{};,")
|
# Matches anything that is not a special character AND any chars in single
|
||||||
|
# or double quotes
|
||||||
|
value = Regex(r"((\".*\")?(\'.*\')?[^\{\};,]?)+")
|
||||||
location = CharsNotIn("{};," + string.whitespace)
|
location = CharsNotIn("{};," + string.whitespace)
|
||||||
# modifier for location uri [ = | ~ | ~* | ^~ ]
|
# modifier for location uri [ = | ~ | ~* | ^~ ]
|
||||||
modifier = Literal("=") | Literal("~*") | Literal("~") | Literal("^~")
|
modifier = Literal("=") | Literal("~*") | Literal("~") | Literal("^~")
|
||||||
|
|
||||||
# rules
|
# rules
|
||||||
assignment = (key + Optional(space + value) + semicolon)
|
assignment = (key + Optional(space + value) + semicolon)
|
||||||
|
location_statement = Optional(space + modifier) + Optional(space + location)
|
||||||
|
if_statement = Literal("if") + space + Regex(r"\(.+\)") + space
|
||||||
block = Forward()
|
block = Forward()
|
||||||
|
|
||||||
block << Group(
|
block << Group(
|
||||||
Group(key + Optional(space + modifier) + Optional(space + location))
|
(Group(key + location_statement) ^ Group(if_statement))
|
||||||
+ left_bracket
|
+ left_bracket
|
||||||
+ Group(ZeroOrMore(Group(assignment) | block))
|
+ Group(ZeroOrMore(Group(assignment) | block))
|
||||||
+ right_bracket)
|
+ right_bracket)
|
||||||
|
|||||||
@@ -84,6 +84,26 @@ class TestRawNginxParser(unittest.TestCase):
|
|||||||
]]]]]
|
]]]]]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_parse_from_file2(self):
|
||||||
|
parsed = load(open(util.get_data_filename('edge_cases.conf')))
|
||||||
|
self.assertEqual(
|
||||||
|
parsed,
|
||||||
|
[[['server'], [['server_name', 'simple']]],
|
||||||
|
[['server'],
|
||||||
|
[['server_name', 'with.if'],
|
||||||
|
[['location', '~', '^/services/.+$'],
|
||||||
|
[[['if', '($request_filename ~* \\.(ttf|woff)$)'],
|
||||||
|
[['add_header', 'Access-Control-Allow-Origin "*"']]]]]]],
|
||||||
|
[['server'],
|
||||||
|
[['server_name', 'with.complicated.headers'],
|
||||||
|
[['location', '~*', '\\.(?:gif|jpe?g|png)$'],
|
||||||
|
[['add_header', 'Pragma public'],
|
||||||
|
['add_header',
|
||||||
|
'Cache-Control \'public, must-revalidate, proxy-revalidate\''
|
||||||
|
' "test,;{}" foo'],
|
||||||
|
['blah', '"hello;world"'],
|
||||||
|
['try_files', '$uri @rewrites']]]]]])
|
||||||
|
|
||||||
def test_dump_as_file(self):
|
def test_dump_as_file(self):
|
||||||
parsed = load(open(util.get_data_filename('nginx.conf')))
|
parsed = load(open(util.get_data_filename('nginx.conf')))
|
||||||
parsed[-1][-1].append([['server'],
|
parsed[-1][-1].append([['server'],
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# This is not a valid nginx config file but it tests edge cases in valid nginx syntax
|
||||||
|
|
||||||
|
server {
|
||||||
|
server_name simple;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
server_name with.if;
|
||||||
|
location ~ ^/services/.+$ {
|
||||||
|
if ($request_filename ~* \.(ttf|woff)$) {
|
||||||
|
add_header Access-Control-Allow-Origin "*";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
server_name with.complicated.headers;
|
||||||
|
|
||||||
|
location ~* \.(?:gif|jpe?g|png)$ {
|
||||||
|
|
||||||
|
add_header Pragma public;
|
||||||
|
add_header Cache-Control 'public, must-revalidate, proxy-revalidate' "test,;{}" foo;
|
||||||
|
blah "hello;world";
|
||||||
|
|
||||||
|
try_files $uri @rewrites;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user