Skip to content

Commit

Permalink
ACMEClient, types, and exceptions have been renamed to snake-case.
Browse files Browse the repository at this point in the history
  • Loading branch information
dvolodin7 committed Nov 17, 2023
1 parent db454b6 commit 97ef42c
Show file tree
Hide file tree
Showing 24 changed files with 349 additions and 350 deletions.
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/bug-report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ body:
should be self-contained, i.e., can be copy-pasted into the Python
interpreter or run as-is via `python myproblem.py`.
placeholder: |
from gufo.acme.clients.base import ACMEClient
from gufo.acme.clients.base import AcmeClient
<< your code here >>
render: python
validations:
Expand Down
10 changes: 5 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ To see unreleased changes, please see the [CHANGELOG on the master branch](https

## Added

* DAVACMEClient: http-01 fulfillment using WebDAV
* DavAcmeClient: http-01 fulfillment using WebDAV
* PowerDnsAcmeClient: dns-01 fulfillment using PowerDNS.
* WEBACMEClient: http-01 fulfillment using static files.
* WEBAcmeClient: http-01 fulfillment using static files.

## Changed

* ACMEClient has been moved into `gufo.acme.clients.base`.
* AcmeClient has been moved into `gufo.acme.clients.base`.

## Fixed

Expand All @@ -30,8 +30,8 @@ To see unreleased changes, please see the [CHANGELOG on the master branch](https

### Fixed

* Fixed `ACMEClient.from_state()` to return a proper subclass.
* Fixed type annotation for `ACMEClient.__aenter__()` in subclasses.
* Fixed `AcmeClient.from_state()` to return a proper subclass.
* Fixed type annotation for `AcmeClient.__aenter__()` in subclasses.

## 0.1.0 - 2023-11-15

Expand Down
22 changes: 11 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ simplifies the protocol complexity with a straightforward and robust API.

Gufo ACME contains various clients which can be applied to your tasks:

* ACMEClient - base client to implement any fulfillment functionality
* AcmeClient - base client to implement any fulfillment functionality
by creating subclasses.
* DAVACMEClient - http-01 fulfillment using WebDAV methods.
* DavAcmeClient - http-01 fulfillment using WebDAV methods.
* PowerDnsAcmeClient - dns-01 PowerDNS fulfillment.
* WebACMEClient - http-01 static file fulfillment.
* WebAcmeClient - http-01 static file fulfillment.

## Supported Certificate Authorities

Expand All @@ -40,8 +40,8 @@ Gufo ACME contains various clients which can be applied to your tasks:

Create an account and store state to the file.
``` python
client_key = ACMEClient.get_key()
async with ACMEClient(DIRECTORY, key=client_key) as client:
client_key = AcmeClient.get_key()
async with AcmeClient(DIRECTORY, key=client_key) as client:
await client.new_account(email)
state = client.get_state()
with open(client_state_path, "wb") as fp:
Expand All @@ -52,14 +52,14 @@ with open(client_state_path, "wb") as fp:

To generate a private key in PEM format.
``` python
private_key = ACMEClient.get_domain_private_key()
private_key = AcmeClient.get_domain_private_key()
```

### Generate CSR

To generate a certificate signing request.
``` python
csr = ACMEClient.get_domain_csr(domain, private_key)
csr = AcmeClient.get_domain_csr(domain, private_key)
```

### Sign Certificate
Expand All @@ -70,22 +70,22 @@ Sign the certificate using `http-01` challenge:
CHALLENGE_DIR = "/www/acme/"


class SignACMEClient(ACMEClient):
class SignAcmeClient(AcmeClient):
async def fulfill_http_01(
self, domain: str, challenge: ACMEChallenge
self, domain: str, challenge: AcmeChallenge
) -> bool:
v = self.get_key_authorization(challenge)
with open(os.path.join(CHALLENGE_DIR, challenge.token), "wb") as fp:
fp.write(v)
return True

async def clear_http_01(
self: ACMEClient, domain: str, challenge: ACMEChallenge
self: AcmeClient, domain: str, challenge: AcmeChallenge
) -> None:
os.unlink(os.path.join(CHALLENGE_DIR, challenge.token))

...
async with SignACMEClient.from_state(state) as client:
async with SignAcmeClient.from_state(state) as client:
cert = await client.sign(domain, csr)
```

Expand Down
8 changes: 4 additions & 4 deletions docs/examples/acme_register.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ The code is straightforward:
--8<-- "examples/acme_register.py"
```

ACMEClient is an asynchronous client, so we
AcmeClient is an asynchronous client, so we
need `asyncio.run()` function to launch it.


Expand All @@ -37,7 +37,7 @@ Import `sys` module to parse the CLI argument.
--8<-- "examples/acme_register.py"
```

Then we import an `ACMEClient` itself.
Then we import an `AcmeClient` itself.

``` py title="acme_register.py" linenums="1" hl_lines="6"
--8<-- "examples/acme_register.py"
Expand Down Expand Up @@ -71,13 +71,13 @@ the following parameters:
```
The client uses secret key to sign all communications to
the server. Later, this key will be bound to account.
We use `ACMEClient.get_key()` function to generate
We use `AcmeClient.get_key()` function to generate
a new key.

``` py title="acme_register.py" linenums="1" hl_lines="11"
--8<-- "examples/acme_register.py"
```
`ACMEClient` requires two mandatory parameters:
`AcmeClient` requires two mandatory parameters:

* ACME Directory URL.
* The client key.
Expand Down
22 changes: 11 additions & 11 deletions docs/examples/acme_sign.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ The code is straightforward:
--8<-- "examples/acme_sign.py::6"
```

ACMEClient is an asynchronous client, so we
AcmeClient is an asynchronous client, so we
need `asyncio.run()` function to launch it.

``` py title="acme_sign.py" linenums="1" hl_lines="2"
Expand All @@ -74,12 +74,12 @@ Import `sys` module to parse the CLI argument.
--8<-- "examples/acme_sign.py::6"
```

Then we import an `ACMEClient` itself.
Then we import an `AcmeClient` itself.

``` py title="acme_sign.py" linenums="1" hl_lines="6"
--8<-- "examples/acme_sign.py::6"
```
We also need an `ACMEChallenge` type.
We also need an `AcmeChallenge` type.

``` py title="acme_sign.py" linenums="6" hl_lines="3"
--8<-- "examples/acme_sign.py:6:11"
Expand All @@ -101,7 +101,7 @@ We need to provide the implementation of the challenge
fulfillment. The Gufo ACME's API provides special
methods which can be overriden in subclasses to
implement the desired behavior. So we are creating
a subclass of `ACMEClient`.
a subclass of `AcmeClient`.

``` py title="acme_sign.py" linenums="11" hl_lines="2 3 4"
--8<-- "examples/acme_sign.py:11:23"
Expand All @@ -111,7 +111,7 @@ We're implementing `http-01` challenge, so we need to override
so we use any async function inside. It accepts two parameters:

* `domain` - a domain name.
* `challenge` - a ACMEChallenge structure, which has
* `challenge` - a AcmeChallenge structure, which has
a `token` field, containing challenge token.

Function returns `True` when fulfillment has beed processed correctly,
Expand All @@ -123,7 +123,7 @@ or `False`, if we wan't provide a fulfillment.
According the ACME protocol, we need to place a specially formed
data to prove our authority. The data contain challenge token and
the fingerprint of the client's key. The calculation may be tricky,
but Gufo ACME provides a `ACMEClient.get_key_authorization()` method,
but Gufo ACME provides a `AcmeClient.get_key_authorization()` method,
which performs all necessary calculations. So we pass `challenge`
parameter and grab an authorization data as value of the variable `v`.

Expand All @@ -143,13 +143,13 @@ and ready to start validation.
--8<-- "examples/acme_sign.py:11:23"
```
The ACME protocol definition exlicitly notes that client may
clean up prepared data after the validation. `ACMEClient`
clean up prepared data after the validation. `AcmeClient`
allows to add own cleanup code by overriding `cleanup_*`
methods. In our case we're overriding `clear_http_01` method.
Just like `fulfill_http_01`, it accepts two parameters:

* `domain` - a domain name.
* `challenge` - a ACMEChallenge structure, which has
* `challenge` - a AcmeChallenge structure, which has
a `token` field, containing challenge token.

``` py title="acme_sign.py" linenums="11" hl_lines="13"
Expand Down Expand Up @@ -189,17 +189,17 @@ is binary so we're opening the file in `rb` mode.
``` py title="acme_sign.py" linenums="26" hl_lines="8"
--8<-- "examples/acme_sign.py:26:36"
```
We're instantiating `ACMEClient` directly from state by
We're instantiating `AcmeClient` directly from state by
using `from_state()` method. Note, we're restoring state
not into the `ACMEClient`, but in our `SignACMEClient` subclass.
not into the `AcmeClient`, but in our `SignAcmeClient` subclass.
The new `client` instance
loads the private key, directory, and account information
directly from state.

``` py title="acme_sign.py" linenums="26" hl_lines="9"
--8<-- "examples/acme_sign.py:26:36"
```
And finally, we call an `ACMEClient.sign` method, which
And finally, we call an `AcmeClient.sign` method, which
accepts domain name and CSR. The `sign` method simple
hides all the protocol's complexity and simply returns
us a signed certificate in PEM format.
Expand Down
4 changes: 2 additions & 2 deletions docs/examples/get_csr.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Import `sys` module to parse the CLI argument.
--8<-- "examples/get_csr.py"
```

Then we import an `ACMEClient` itself.
Then we import an `AcmeClient` itself.

``` py title="get_csr.py" linenums="1" hl_lines="6"
--8<-- "examples/get_csr.py"
Expand All @@ -53,7 +53,7 @@ The `pk` variable contains our private key.
``` py title="get_csr.py" linenums="1" hl_lines="9"
--8<-- "examples/get_csr.py"
```
`ACMEClient.get_domain_csr()` function generates
`AcmeClient.get_domain_csr()` function generates
a CSR in PEM format. It aceepts requred parameters:

* `domain` - domain name
Expand Down
6 changes: 3 additions & 3 deletions docs/examples/get_private_key.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Import `sys` module to parse the CLI argument.
--8<-- "examples/get_private_key.py"
```

Then we import an `ACMEClient` itself.
Then we import an `AcmeClient` itself.

``` py title="get_private_key.py" linenums="1" hl_lines="6"
--8<-- "examples/get_private_key.py"
Expand All @@ -39,12 +39,12 @@ a private key.
``` py title="get_private_key.py" linenums="1" hl_lines="7"
--8<-- "examples/get_private_key.py"
```
`ACMEClient.get_domain_private_key()` function generates
`AcmeClient.get_domain_private_key()` function generates
a private key in PEM format. It assepts an optional parameter
which defines a RSA key length. The default is 4096, which is
suitable for our applications. This function is the
static method, so we don't need to instantiate an
`ACMEClient`.
`AcmeClient`.

``` py title="get_private_key.py" linenums="1" hl_lines="8 9"
--8<-- "examples/get_private_key.py"
Expand Down
22 changes: 11 additions & 11 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ simplifies the protocol complexity with a straightforward and robust API.

Gufo ACME contains various clients which can be applied to your tasks:

* [ACMEClient][gufo.acme.clients.base.ACMEClient] - base client to implement any fulfillment functionality
* [AcmeClient][gufo.acme.clients.base.AcmeClient] - base client to implement any fulfillment functionality
by creating subclasses.
* [DAVACMEClient][gufo.acme.clients.dav.DAVACMEClient] - http-01 fulfillment using WebDAV methods.
* [DavAcmeClient][gufo.acme.clients.dav.DavAcmeClient] - http-01 fulfillment using WebDAV methods.
* [PowerDnsAcmeClient][gufo.acme.clients.powerdns.PowerDnsAcmeClient] - dns-01 PowerDNS fulfillment.
* [WebACMEClient][gufo.acme.clients.web.WebACMEClient] - http-01 static file fulfillment.
* [WebAcmeClient][gufo.acme.clients.web.WebAcmeClient] - http-01 static file fulfillment.

## Supported Certificate Authorities

Expand All @@ -37,8 +37,8 @@ Gufo ACME contains various clients which can be applied to your tasks:

Create an account and store state to the file.
``` python
client_key = ACMEClient.get_key()
async with ACMEClient(DIRECTORY, key=client_key) as client:
client_key = AcmeClient.get_key()
async with AcmeClient(DIRECTORY, key=client_key) as client:
await client.new_account(email)
state = client.get_state()
with open(client_state_path, "wb") as fp:
Expand All @@ -49,14 +49,14 @@ with open(client_state_path, "wb") as fp:

To generate a private key in PEM format.
``` python
private_key = ACMEClient.get_domain_private_key()
private_key = AcmeClient.get_domain_private_key()
```

### Generate CSR

To generate a certificate signing request.
``` python
csr = ACMEClient.get_domain_csr(domain, private_key)
csr = AcmeClient.get_domain_csr(domain, private_key)
```

### Sign Certificate
Expand All @@ -67,22 +67,22 @@ Sign the certificate using `http-01` challenge:
CHALLENGE_DIR = "/www/acme/"


class SignACMEClient(ACMEClient):
class SignAcmeClient(AcmeClient):
async def fulfill_http_01(
self, domain: str, challenge: ACMEChallenge
self, domain: str, challenge: AcmeChallenge
) -> bool:
v = self.get_key_authorization(challenge)
with open(os.path.join(CHALLENGE_DIR, challenge.token), "wb") as fp:
fp.write(v)
return True

async def clear_http_01(
self: ACMEClient, domain: str, challenge: ACMEChallenge
self: AcmeClient, domain: str, challenge: AcmeChallenge
) -> None:
os.unlink(os.path.join(CHALLENGE_DIR, challenge.token))

...
async with SignACMEClient.from_state(state) as client:
async with SignAcmeClient.from_state(state) as client:
cert = await client.sign(domain, csr)
```

Expand Down
2 changes: 1 addition & 1 deletion docs/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ to run tests in the CI environment. On local environments, the test is skipped b
To enable the test in your local environment, additional
infrastructure is needed.

### DAVACMEClient
### DavAcmeClient

1. Have control over a DNS zone (later `<mydomain>`).
2. Set up an Nginx server.
Expand Down
6 changes: 3 additions & 3 deletions examples/acme_register.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import asyncio
import sys

from gufo.acme.clients.base import ACMEClient
from gufo.acme.clients.base import AcmeClient

DIRECTORY = "https://acme-staging-v02.api.letsencrypt.org/directory"


async def main(email: str, client_state_path: str) -> None:
client_key = ACMEClient.get_key()
async with ACMEClient(DIRECTORY, key=client_key) as client:
client_key = AcmeClient.get_key()
async with AcmeClient(DIRECTORY, key=client_key) as client:
await client.new_account(email)
state = client.get_state()
with open(client_state_path, "wb") as fp:
Expand Down
Loading

0 comments on commit 97ef42c

Please sign in to comment.