brn: Introduce BRN as a class

This commit is contained in:
Iain Learmonth 2022-06-15 11:50:15 +01:00
parent 8c411e39bc
commit a0da4d4641
8 changed files with 116 additions and 9 deletions

5
.pylintrc Normal file
View file

@ -0,0 +1,5 @@
[MASTER]
ignored-classes=Column
load-plugins=pylint_flask,pylint_flask_sqlalchemy
py-version=3.8
suggestion-mode=yes

3
app/brm/__init__.py Normal file
View file

@ -0,0 +1,3 @@
"""
Bypass Censorship Resource Management API.
"""

58
app/brm/brn.py Normal file
View file

@ -0,0 +1,58 @@
"""
Bypass Censorship Resource Names.
"""
from __future__ import annotations
from dataclasses import dataclass, field
from typing import Any
from flask import current_app
from app.brm.utils import is_integer
def global_namespace() -> str:
return str(current_app.config["GLOBAL_NAMESPACE"])
@dataclass
class BRN:
group_id: int
product: str
provider: str
resource_type: str
resource_id: str
global_namespace: str = field(default_factory=global_namespace)
@classmethod
def from_str(cls, s: str) -> BRN:
parts = s.split(":")
if len(parts) != 6 or parts[0].lower() != "brn" or not is_integer(parts[2]):
raise TypeError(f"Expected a valid BRN but got {repr(s)}.")
resource_parts = parts[4].split("/")
if len(resource_parts) != 5:
raise TypeError(f"Expected a valid BRN but got {repr(s)}.")
return cls(
global_namespace=parts[1],
group_id=int(parts[2]),
product=parts[3],
provider=parts[4],
resource_type=resource_parts[0],
resource_id=resource_parts[1]
)
def __eq__(self, other: Any) -> bool:
return str(self) == str(other)
def __str__(self) -> str:
return ":".join([
"brn",
self.global_namespace,
str(self.group_id),
self.product,
self.provider,
f"{self.resource_type}/{self.resource_id}"
])
def __repr__(self) -> str:
return f"<BRN {str(self)}>"

20
app/brm/utils.py Normal file
View file

@ -0,0 +1,20 @@
from __future__ import annotations
from typing import Any
def is_integer(n: Any) -> bool:
"""
Determine if a string (or other object type that can be converted automatically) represents an integer.
Thanks to https://note.nkmk.me/en/python-check-int-float/.
:param n: object to test
:return: true if it's an integer
"""
try:
float(n)
except ValueError:
return False
else:
return float(n).is_integer()

View file

@ -3,6 +3,7 @@ from datetime import datetime
from typing import Union, List, Optional, Any from typing import Union, List, Optional, Any
from app.alarms import alarms_for from app.alarms import alarms_for
from app.brm.brn import BRN
from app.extensions import db from app.extensions import db
from app.models.alarms import Alarm from app.models.alarms import Alarm
@ -73,7 +74,7 @@ class AbstractResource(db.Model): # type: ignore
@property @property
@abstractmethod @abstractmethod
def brn(self) -> str: def brn(self) -> BRN:
raise NotImplementedError() raise NotImplementedError()
def deprecate(self, *, reason: str) -> None: def deprecate(self, *, reason: str) -> None:

View file

@ -3,6 +3,7 @@ from typing import Optional, List
from flask import current_app from flask import current_app
from tldextract import extract from tldextract import extract
from app.brm.brn import BRN
from app.extensions import db from app.extensions import db
from app.models import AbstractConfiguration, AbstractResource from app.models import AbstractConfiguration, AbstractResource
from app.models.onions import Onion from app.models.onions import Onion
@ -53,8 +54,14 @@ class Proxy(AbstractResource):
origin = db.relationship("Origin", back_populates="proxies") origin = db.relationship("Origin", back_populates="proxies")
@property @property
def brn(self) -> str: def brn(self) -> BRN:
return f"brn:{current_app.config['GLOBAL_NAMESPACE']}:{self.origin.group_id}:mirror:{self.provider}:proxy/{self.id}" return BRN(
group_id=self.origin.group_id,
product="mirror",
provider=self.provider,
resource_type="proxy",
resource_id=self.id
)
@classmethod @classmethod
def csv_header(cls) -> List[str]: def csv_header(cls) -> List[str]:
@ -72,5 +79,11 @@ class SmartProxy(AbstractResource):
group = db.relationship("Group", back_populates="smart_proxies") group = db.relationship("Group", back_populates="smart_proxies")
@property @property
def brn(self) -> str: def brn(self) -> BRN:
return f"brn:{current_app.config['GLOBAL_NAMESPACE']}:{self.group_id}:mirror:{self.provider}:smart-proxy/1" return BRN(
group_id=self.group_id,
product="mirror",
provider=self.provider,
resource_type="smart_proxy",
resource_id=str(1)
)

View file

@ -1,5 +1,4 @@
from flask import current_app from app.brm.brn import BRN
from app.extensions import db from app.extensions import db
from app.models import AbstractConfiguration, AbstractResource from app.models import AbstractConfiguration, AbstractResource
@ -21,5 +20,11 @@ class Eotk(AbstractResource):
group = db.relationship("Group", back_populates="eotks") group = db.relationship("Group", back_populates="eotks")
@property @property
def brn(self) -> str: def brn(self) -> BRN:
return f"brn:{current_app.config['GLOBAL_NAMESPACE']}:{self.group_id}:eotk:{self.provider}:instance/{self.region}" return BRN(
group_id=self.group_id,
provider=self.provider,
product="eotk",
resource_type="instance",
resource_id=self.region
)

View file

@ -10,6 +10,8 @@ flask-wtf
flask~=2.0.2 flask~=2.0.2
jinja2~=3.0.2 jinja2~=3.0.2
pydantic pydantic
pylint-flask-sqlalchemy
pylint-flask
pyyaml~=6.0 pyyaml~=6.0
requests~=2.27.1 requests~=2.27.1
sqlalchemy~=1.4.32 sqlalchemy~=1.4.32