Skip to content

Commit

Permalink
Merge pull request #511 from simondeziel/client-token
Browse files Browse the repository at this point in the history
Add create_token method to get a token usable with lxc remote add
  • Loading branch information
stgraber authored Jun 3, 2022
2 parents a44deea + a4639bc commit bf2e3dd
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 0 deletions.
36 changes: 36 additions & 0 deletions pylxd/models/certificate.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import json
from base64 import b64encode

from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.serialization import Encoding
Expand Down Expand Up @@ -75,6 +78,39 @@ def create(
fingerprint = location.split("/")[-1]
return cls.get(client, fingerprint)

@classmethod
def create_token(
cls,
client,
name="",
projects=None,
restricted=False,
):
"""Create a new token."""
data = {
"password": "",
"certificate": "",
"type": "client",
"token": True,
"name": name,
"restricted": restricted,
"projects": projects,
}
response = client.api.certificates.post(json=data)
metadata = response.json()["metadata"]["metadata"]

# Assemble a token from the returned metadata
token = {
"name": name,
"fingerprint": metadata["fingerprint"],
"addresses": metadata["addresses"],
"secret": metadata["secret"],
}

# Convert to (compact) JSON and base64 encode it
token = json.dumps(token, separators=(",", ":"))
return b64encode(token.encode()).decode()

@property
def api(self):
return self.client.api.certificates[self.fingerprint]
62 changes: 62 additions & 0 deletions pylxd/models/tests/test_certificate.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import json
import os

from pylxd import models
Expand Down Expand Up @@ -46,6 +47,67 @@ def test_create(self):
an_certificate.fingerprint,
)

def test_create_token(self):
"""A token is returned."""
self.add_rule(
{
"text": json.dumps(
{
"type": "sync",
"status": "Operation created",
"status_code": 100,
"operation": "/1.0/operations/a1d77f1b-7dfb-44e0-a3a3-1dd18bd5c15a",
"error_code": 0,
"error": "",
"metadata": {
"id": "a1d77f1b-7dfb-44e0-a3a3-1dd18bd5c15a",
"class": "token",
"description": "Executing operation",
"created_at": "2022-06-01T19:22:21.778204449Z",
"updated_at": "2022-06-01T19:22:21.778204449Z",
"status": "Running",
"status_code": 103,
"resources": None,
"metadata": {
"addresses": [
"127.0.0.1:8443",
"[::1]:8443",
],
"fingerprint": "eddaa6023f9064f94dd6fadb36c01d9af9de935efff76f4ebada5a2fda4753be",
"request": {
"name": "foo",
"type": "client",
"restricted": True,
"projects": ["default"],
"certificate": "",
"password": "",
"token": True,
},
"secret": "6efac2f5de066103dc9798414e916996a8ffe3b9818608d4fe3ba175fae618ad",
},
"may_cancel": True,
"err": "",
"location": "foo",
},
},
),
"method": "POST",
"url": r"^http://pylxd.test/1.0/certificates$",
"headers": {
"location": "/1.0/operations/a1d77f1b-7dfb-44e0-a3a3-1dd18bd5c15a",
},
},
)

a_token = self.client.certificates.create_token(
name="foo", projects=["default"], restricted=True
)

self.assertEqual(
"eyJuYW1lIjoiZm9vIiwiZmluZ2VycHJpbnQiOiJlZGRhYTYwMjNmOTA2NGY5NGRkNmZhZGIzNmMwMWQ5YWY5ZGU5MzVlZmZmNzZmNGViYWRhNWEyZmRhNDc1M2JlIiwiYWRkcmVzc2VzIjpbIjEyNy4wLjAuMTo4NDQzIiwiWzo6MV06ODQ0MyJdLCJzZWNyZXQiOiI2ZWZhYzJmNWRlMDY2MTAzZGM5Nzk4NDE0ZTkxNjk5NmE4ZmZlM2I5ODE4NjA4ZDRmZTNiYTE3NWZhZTYxOGFkIn0=",
a_token,
)

def test_fetch(self):
"""A partial object is fully fetched."""
an_certificate = models.Certificate(self.client, fingerprint="an-certificate")
Expand Down

0 comments on commit bf2e3dd

Please sign in to comment.