Skip to content

Commit

Permalink
feat: add option to use license file as API key
Browse files Browse the repository at this point in the history
  • Loading branch information
vasarhelyi committed Dec 10, 2024
1 parent 96f36bd commit 9aa37d7
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 15 deletions.
14 changes: 11 additions & 3 deletions doc/modules/ROOT/pages/install.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ image::install_blender_addons.jpg[Install Blender Add-ons]

6. Setup add-on preferences if you are using a version of *Skybrush Studio for Blender* other than the free community edition:

API Key:: Enter the API key received that is used when communicating with the Skybrush Studio server.
API Key:: If you have received an API Key for communicating with the Skybrush Studio server, paste it here.

License file:: If you have received a License file for communicating with the Skybrush Studio server, give its full path here to use it as your API Key.

Server URL:: Enter the URL of a dedicated Skybrush Studio server if you are using a dedicated server.

Expand All @@ -50,11 +52,17 @@ To turn on online access, open Blender and go to Edit -> Preferences -> System,

=== 5. Install Skybrush Studio Server

==== 5.1 Cloud server for community users

If you are a community user, you do _not_ need to install the *Skybrush Studio Server* as you can rely on our cloud-based community server.

If you are using a paid, professional version of *Skybrush Studio*, you will receive a precompiled copy of *Skybrush Studio Server* and a corresponding license file upon purchase of the software.
==== 5.2 Cloud server for pro users

If you are using a paid, professional version of *Skybrush Studio*, you will receive a license file upon purchase of the software. This license file shall be used as an API key that enables your pro features while using the cloud-based *Skybrush Studio Server* (see setup instructions above on how to use your License file as your API Key).

==== 5.3 Local server for VIP users

After successful installation of *Skybrush Studio Server*, your licence file named `skybrush-studio-server.cml` has to be copied to the folder where you installed the server itself. You might need system administrator privileges to write into this folder.
Upon special agreement, you may also receive a precompiled copy of *Skybrush Studio Server* for local use. In this case, after successful installation, your license file named `skybrush-studio-server.cml` has to be copied to the folder where you installed the server itself. You might need system administrator privileges to write into this folder.

The terminal log of *Skybrush Studio Server* will give you instant feedback about the presence and content of your currently used license file upon execution of the server.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ The validation report starts with a summary of flight statistics and safety test
.IMPORTANT
****
The validation .pdf is created by a local or remote instance of *Skybrush Studio Server* running in the background. As creating the validation report is resource intensive, access to this feature might be disabled on our public server and might be only available through a paid license option. If you need but do not have access, mailto:[email protected][contact us] for obtaining the proper *Skybrush Studio Server* licence.
The validation .pdf is created by a local or remote instance of *Skybrush Studio Server* running in the background. As creating the validation report is resource intensive, access to this feature might be disabled on our public server and might be only available through a paid license option. If you need but do not have access, mailto:[email protected][contact us] for obtaining the proper *Skybrush Studio Server* license.
****
If you press the btn:[Export to validation .pdf] button, you have to choose the path and filename of your output file. There are also some parameters you can setup conveniently:
Expand Down
45 changes: 42 additions & 3 deletions src/modules/sbstudio/api/base.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import json
import re

from base64 import b64encode
from contextlib import contextmanager
from gzip import compress
from http.client import HTTPResponse
Expand Down Expand Up @@ -106,6 +107,9 @@ def save_to_file(self, filename: Path) -> None:


_API_KEY_REGEXP = re.compile(r"^[a-zA-Z0-9-_.]*$")
_LICENSE_API_KEY_REGEXP = re.compile(
r"^License [A-Za-z0-9+/=]{4,}([A-Za-z0-9+/=]{2})*$"
)


class SkybrushStudioAPI:
Expand All @@ -126,27 +130,42 @@ def validate_api_key(key: str) -> str:
Raises:
ValueError: if the key cannot be a valid API key
"""
if not _API_KEY_REGEXP.match(key):
raise ValueError("Invalid API key")
if key.startswith("License"):
if not _LICENSE_API_KEY_REGEXP.match(key):
raise ValueError("Invalid license-type API key")
else:
if not _API_KEY_REGEXP.match(key):
raise ValueError("Invalid API key")
return key

def __init__(
self,
url: str = COMMUNITY_SERVER_URL,
api_key: Optional[str] = None,
license_file: Optional[str] = None,
):
"""Constructor.
Parameters:
url: the root URL of the Skybrush Studio API; defaults to the public
online service
api_key: the API key used to authenticate with the server
license_file: the path to a license file to be used as the API Key
"""
self._api_key = None
self._root = None # type: ignore
self._request_context = create_default_context()

self.api_key = api_key
if api_key and license_file:
raise SkybrushStudioAPIError(
"Cannot use API key and license file at the same time"
)

if license_file:
self.api_key = self._convert_license_file_to_api_key(license_file)
else:
self.api_key = api_key

self.url = url

@property
Expand All @@ -158,6 +177,26 @@ def api_key(self) -> Optional[str]:
def api_key(self, value: Optional[str]) -> None:
self._api_key = self.validate_api_key(value) if value else None

def _convert_license_file_to_api_key(self, file: str) -> str:
"""Parses a license file and transforms it to an API key.
Returns:
the license file parsed as an API key
Raises:
ValueError: if the file cannot be read as an API key
"""
if not Path(file).exists():
raise ValueError("License file does not exist")

try:
with open(file, "rb") as fp:
api_key = f"License {b64encode(fp.read()).decode('ascii')}"
except Exception:
raise ValueError("Could not read license file") from None

return api_key

@property
def url(self) -> str:
"""The URL where the API can be accessed."""
Expand Down
34 changes: 26 additions & 8 deletions src/modules/sbstudio/plugin/api.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import logging

from contextlib import contextmanager
from functools import lru_cache
from typing import Iterator, Optional, TypeVar
Expand All @@ -13,21 +15,35 @@

T = TypeVar("T")

#############################################################################
# configure logger

log = logging.getLogger(__name__)


@lru_cache(maxsize=1)
def _get_api_from_url_and_key(url: str, key: str):
"""Constructs a Skybrush Studio API object from a root URL and an API key.
def _get_api_from_url_and_key_or_license(url: str, key: str, license_file: str):
"""Constructs a Skybrush Studio API object from a root URL and an API key
or a license file.
Memoized so we do not need to re-construct the same instance as long as
the user does not change the add-on settings.
"""
global _fallback_api_key

result = SkybrushStudioAPI()

result.api_key = key or _fallback_api_key
if url:
result.url = url
try:
result = SkybrushStudioAPI(
api_key=key or (None if license_file else _fallback_api_key),
license_file=license_file or None,
)
if url:
result.url = url
except ValueError as ex:
log.error(f"Could not initialize Skybrush Studio API: {str(ex)}")
raise
except Exception as ex:
log.error(f"Unhandled exception in Skybrush Studio API initialization: {ex!r}")
raise

# This is bad practice, but the default installation of Blender does not find
# the SSL certificates on macOS and there are reports about similar problems
Expand All @@ -48,12 +64,14 @@ def get_api() -> SkybrushStudioAPI:

api_key: str
server_url: str
license_file: str

prefs = get_preferences()
api_key = str(prefs.api_key).strip()
license_file = str(prefs.license_file).strip()
server_url = str(prefs.server_url).strip()

return _get_api_from_url_and_key(server_url, api_key)
return _get_api_from_url_and_key_or_license(server_url, api_key, license_file)


@contextmanager
Expand Down
7 changes: 7 additions & 0 deletions src/modules/sbstudio/plugin/model/global_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ class DroneShowAddonGlobalSettings(AddonPreferences):

bl_idname = "ui_skybrush_studio"

license_file = StringProperty(
name="License file",
description="Full path to the license file to be used as the API Key",
subtype="FILE_PATH",
)

api_key = StringProperty(
name="API Key",
description="API Key that is used when communicating with the Skybrush Studio server",
Expand Down Expand Up @@ -45,6 +51,7 @@ def draw(self, context):
layout = self.layout

layout.prop(self, "api_key")
layout.prop(self, "license_file")
layout.prop(self, "server_url")

row = layout.row()
Expand Down

0 comments on commit 9aa37d7

Please sign in to comment.