Skip to content

Commit

Permalink
Merge pull request #108 from ImMin5/master
Browse files Browse the repository at this point in the history
Add App apis except generate_api_key
  • Loading branch information
ImMin5 authored Dec 7, 2023
2 parents 892b5a1 + 5d5a0fd commit 34fbc4f
Show file tree
Hide file tree
Showing 14 changed files with 663 additions and 33 deletions.
4 changes: 4 additions & 0 deletions src/spaceone/identity/error/error_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,7 @@ class ERROR_UNABLE_TO_RESET_PASSWORD_IN_EXTERNAL_AUTH(ERROR_UNABLE_TO_RESET_PASS

class ERROR_UNABLE_TO_RESET_PASSWORD_WITHOUT_EMAIL(ERROR_UNABLE_TO_RESET_PASSWORD):
_message = "Unable to reset password without email. (user_id = {user_id})"


class ERROR_LAST_ADMIN_CANNOT_DISABLE(ERROR_INVALID_ARGUMENT):
_message = "The last admin cannot be disabled. (user_id = {user_id})"
4 changes: 4 additions & 0 deletions src/spaceone/identity/interface/grpc/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,12 @@
from spaceone.identity.interface.grpc.role_binding import RoleBinding
from spaceone.identity.interface.grpc.user import User
from spaceone.identity.interface.grpc.workspace_user import WorkspaceUser

# from spaceone.identity.interface.grpc.user_group import UserGroup
from spaceone.identity.interface.grpc.app import App
from spaceone.identity.interface.grpc.api_key import APIKey
from spaceone.identity.interface.grpc.token import Token

# from spaceone.identity.interface.grpc.authorization import Authorization

_all_ = ["app"]
Expand All @@ -36,6 +39,7 @@
app.add_service(WorkspaceUser)
app.add_service(User)
# app.add_service(UserGroup)
app.add_service(App)
app.add_service(APIKey)
app.add_service(Token)
# app.add_service(Authorization)
62 changes: 62 additions & 0 deletions src/spaceone/identity/interface/grpc/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
from spaceone.core.pygrpc import BaseAPI
from spaceone.api.identity.v2 import app_pb2, app_pb2_grpc
from spaceone.identity.service.app_service import AppService


class App(BaseAPI, app_pb2_grpc.AppServicer):
pb2 = app_pb2
pb2_grpc = app_pb2_grpc

def create(self, request, context):
params, metadata = self.parse_request(request, context)
app_svc = AppService(metadata)
response: dict = app_svc.create(params)
return self.dict_to_message(response)

def update(self, request, context):
params, metadata = self.parse_request(request, context)
app_svc = AppService(metadata)
response: dict = app_svc.update(params)
return self.dict_to_message(response)

def generate_api_key(self, request, context):
params, metadata = self.parse_request(request, context)
app_svc = AppService(metadata)
response: dict = app_svc.generate_api_key(params)
return self.dict_to_message(response)

def enable(self, request, context):
params, metadata = self.parse_request(request, context)
app_svc = AppService(metadata)
response: dict = app_svc.enable(params)
return self.dict_to_message(response)

def disable(self, request, context):
params, metadata = self.parse_request(request, context)
app_svc = AppService(metadata)
response: dict = app_svc.disable(params)
return self.dict_to_message(response)

def delete(self, request, context):
params, metadata = self.parse_request(request, context)
app_svc = AppService(metadata)
app_svc.delete(params)
return self.empty()

def get(self, request, context):
params, metadata = self.parse_request(request, context)
app_svc = AppService(metadata)
response: dict = app_svc.get(params)
return self.dict_to_message(response)

def list(self, request, context):
params, metadata = self.parse_request(request, context)
app_svc = AppService(metadata)
response: dict = app_svc.list(params)
return self.dict_to_message(response)

def stat(self, request, context):
params, metadata = self.parse_request(request, context)
app_svc = AppService(metadata)
response: dict = app_svc.stat(params)
return self.dict_to_message(response)
31 changes: 29 additions & 2 deletions src/spaceone/identity/manager/api_key_manager.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import logging
from typing import Tuple
from mongoengine import QuerySet

from spaceone.core.cache import cacheable
from spaceone.core.manager import BaseManager

from spaceone.identity.lib.key_generator import KeyGenerator
from spaceone.identity.model.app.database import App
from spaceone.identity.model.api_key.database import APIKey
from spaceone.identity.model.domain.database import DomainSecret
from spaceone.identity.model.user.database import User
Expand All @@ -17,7 +20,7 @@ def __init__(self, *args, **kwargs):
self.api_key_model = APIKey
self.domain_secret_model = DomainSecret

def create_api_key(self, user_vo: User, params: dict) -> (APIKey, str):
def create_api_key_by_user_vo(self, user_vo: User, params: dict) -> (APIKey, str):
def _rollback(api_key_vo):
_LOGGER.info(
f"[create_api_key._rollback] Delete api_key : {api_key_vo.api_key_id}"
Expand All @@ -37,6 +40,27 @@ def _rollback(api_key_vo):
api_key = key_gen.generate_api_key(api_key_vo.api_key_id)
return api_key_vo, api_key

def create_api_key_by_app_vo(self, app_vo: App, params: dict) -> (APIKey, str):
def _rollback(api_key_vo, app_vo):
_LOGGER.info(
f"[create_api_key._rollback] Delete app and api_key : {app_vo.app_id} {api_key_vo.api_key_id}"
)
app_vo.delete()
api_key_vo.delete()

params["app"] = app_vo
api_key_vo: APIKey = self.api_key_model.create(params)
self.transaction.add_rollback(_rollback, api_key_vo)

prv_jwk = self._query_domain_secret(params["domain_id"])

key_gen = KeyGenerator(
prv_jwk=prv_jwk, domain_id=params["domain_id"], audience=app_vo.app_id
)

api_key = key_gen.generate_api_key(api_key_vo.api_key_id)
return api_key_vo, api_key

def update_api_key_by_vo(self, params: dict, api_key_vo: APIKey) -> APIKey:
def _rollback(old_data):
_LOGGER.info(
Expand Down Expand Up @@ -81,10 +105,13 @@ def _rollback(old_data):
def get_api_key(self, api_key_id: str, domain_id: str) -> APIKey:
return self.api_key_model.get(api_key_id=api_key_id, domain_id=domain_id)

def filter_api_keys(self, **conditions) -> QuerySet:
return self.api_key_model.filter(**conditions)

def list_api_keys(self, query: dict) -> Tuple[list, int]:
return self.api_key_model.query(**query)

def stat_api_keys(self, query):
def stat_api_keys(self, query: dict) -> dict:
return self.api_key_model.stat(**query)

@cacheable(key="api-key:{domain_id}", expire=60)
Expand Down
79 changes: 79 additions & 0 deletions src/spaceone/identity/manager/app_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import logging
from typing import Tuple, Union

from spaceone.core.cache import cacheable
from spaceone.core.manager import BaseManager
from spaceone.identity.model.app.database import App

_LOGGER = logging.getLogger(__name__)


class AppManager(BaseManager):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.app_model = App

def create_app(self, params: dict) -> App:
def _rollback(vo: App):
_LOGGER.info(f"[create_app._rollback] Delete app: {vo.name} ({vo.app_id})")
vo.delete()

app_vo = self.app_model.create(params)
self.transaction.add_rollback(_rollback, app_vo)

return app_vo

def update_app_by_vo(self, params: dict, app_vo: App) -> App:
def _rollback(old_data):
_LOGGER.info(
f'[update_app._rollback] Revert Data: {old_data["name"]} ({old_data["app_id"]})'
)
app_vo.update(old_data)

self.transaction.add_rollback(_rollback, app_vo.to_dict())

return app_vo.update(params)

def enable_app(self, app_vo: App) -> App:
def _rollback(old_data):
_LOGGER.info(f"[enable_app._rollback] Revert Data: {old_data}")
app_vo.update(old_data)

if app_vo.state != "ENABLED":
self.transaction.add_rollback(_rollback, app_vo.to_dict())
app_vo.update({"state": "ENABLED"})

return app_vo

def disable_app(self, app_vo: App) -> App:
def _rollback(old_data):
_LOGGER.info(f"[disable_app._rollback] Revert Data: {old_data}")
app_vo.update(old_data)

if app_vo.state != "DISABLED":
self.transaction.add_rollback(_rollback, app_vo.to_dict())
app_vo.update({"state": "DISABLED"})

return app_vo

@staticmethod
def delete_app_by_vo(app_vo: App) -> None:
app_vo.delete()

def get_app(
self, app_id: str, workspace_id: Union[str, None], domain_id: str
) -> App:
conditions = {
"app_id": app_id,
"domain_id": domain_id,
}
if workspace_id:
conditions["workspace_id"] = workspace_id

return self.app_model.get(**conditions)

def list_apps(self, query: dict) -> Tuple[list, int]:
return self.app_model.query(**query)

def stat_apps(self, query: dict) -> dict:
return self.app_model.stat(**query)
7 changes: 4 additions & 3 deletions src/spaceone/identity/model/api_key/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ class APIKey(MongoModel):
max_length=20, default="ENABLED", choices=("ENABLED", "DISABLED", "EXPIRED")
)
user = ReferenceField("User", null=True, default=None, reverse_delete_rule=CASCADE)
# app = ReferenceField("App", null=True, default=None, reverse_delete_rule=CASCADE)
user_id = StringField(max_length=40, required=True)
app = ReferenceField("App", null=True, default=None, reverse_delete_rule=CASCADE)
user_id = StringField(max_length=40, default=None, null=True)
app_id = StringField(max_length=40, default=None, null=True)
domain_id = StringField(max_length=40, required=True)
last_accessed_at = DateTimeField(default=None, null=True)
created_at = DateTimeField(auto_now_add=True)

meta = {
"updatable_fields": ["state", "nane", "last_accessed_at"],
"updatable_fields": ["state", "name", "last_accessed_at"],
"minimal_fields": ["api_key_id", "state", "user_id"],
"ordering": ["api_key_id"],
"indexes": ["state", "user_id", "domain_id", "last_accessed_at"],
Expand Down
2 changes: 2 additions & 0 deletions src/spaceone/identity/model/api_key/response.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@ class APIKeyResponse(BaseModel):
domain_id: Union[str, None] = None
created_at: Union[datetime, None] = None
last_accessed_at: Union[datetime, None] = None
expired_at: Union[datetime, None] = None

def dict(self, *args, **kwargs):
data = super().dict(*args, **kwargs)
data["created_at"] = utils.datetime_to_iso8601(data["created_at"])
data["last_accessed_at"] = utils.datetime_to_iso8601(data["last_accessed_at"])
data["expired_at"] = utils.datetime_to_iso8601(data["expired_at"])
return data


Expand Down
Empty file.
35 changes: 35 additions & 0 deletions src/spaceone/identity/model/app/database.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from mongoengine import *
from spaceone.core.model.mongo_model import MongoModel


class App(MongoModel):
app_id = StringField(max_length=40, generate_id="app", unique=True)
name = StringField(max_length=40, required=True)
state = StringField(
max_length=20, default="ENABLED", choices=("ENABLED", "DISABLED", "EXPIRED")
)
role_type = StringField(
max_length=20,
default="WORKSPACE_MEMBER",
choices=(
"SYSTEM",
"SYSTEM_ADMIN",
"DOMAIN_ADMIN",
"WORKSPACE_OWNER",
"WORKSPACE_MEMBER",
),
)
api_key_id = StringField(max_length=40, default=None, null=True)
role_id = StringField(max_length=40, required=True)

domain_id = StringField(max_length=40, required=True)
created_at = DateTimeField(auto_now_add=True)
last_accessed_at = DateTimeField(default=None, null=True)
expired_at = DateTimeField(required=True)

meta = {
"updatable_fields": ["name", "state", "api_key_id", "tags", "last_accessed_at"],
"minimal_fields": ["app_id", "state", "expired_at", "api_key_id"],
"ordering": ["app_id"],
"indexes": ["state", "domain_id", "last_accessed_at", "expired_at"],
}
91 changes: 91 additions & 0 deletions src/spaceone/identity/model/app/request.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
from datetime import datetime
from typing import Union, Literal
from pydantic import BaseModel

__all__ = [
"AppCreateRequest",
"AppUpdateRequest",
"AppGenerateAPIKeyRequest",
"AppEnableRequest",
"AppDisableRequest",
"AppDeleteRequest",
"AppGetRequest",
"AppSearchQueryRequest",
"AppStatQueryRequest",
"State",
"PermissionGroup",
"RoleType",
]

State = Literal["ENABLED", "DISABLED", "EXPIRED"]
PermissionGroup = Literal["DOMAIN", "WORKSPACE"]
RoleType = Literal[
"SYSTEM", "SYSTEM_ADMIN", "DOMAIN_ADMIN", "WORKSPACE_OWNER", "WORKSPACE_MEMBER"
]


class AppCreateRequest(BaseModel):
name: str
role_id: str
tags: Union[dict, None] = None
expired_at: Union[datetime, None] = None
permission_group: PermissionGroup
workspace_id: Union[str, None] = None
domain_id: str


class AppUpdateRequest(BaseModel):
app_id: str
name: Union[str, None] = None
tags: Union[dict, None] = None
workspace_id: Union[str, None] = None
domain_id: str


class AppGenerateAPIKeyRequest(BaseModel):
app_id: str
workspace_id: Union[str, None] = None
domain_id: str


class AppEnableRequest(BaseModel):
app_id: str
workspace_id: Union[str, None] = None
domain_id: str


class AppDisableRequest(BaseModel):
app_id: str
workspace_id: Union[str, None] = None
domain_id: str


class AppDeleteRequest(BaseModel):
app_id: str
workspace_id: Union[str, None] = None
domain_id: str


class AppGetRequest(BaseModel):
app_id: str
workspace_id: Union[str, None] = None
domain_id: str


class AppSearchQueryRequest(BaseModel):
query: Union[dict, None] = None
app_id: Union[str, None] = None
name: Union[str, None] = None
state: Union[State, None] = None
role_type: Union[str, None] = None
role_id: Union[str, None] = None
api_key_id: Union[str, None] = None
permission_group: Union[PermissionGroup, None] = None
workspace_id: Union[str, None] = None
domain_id: str


class AppStatQueryRequest(BaseModel):
query: dict
workspace_id: Union[str, None] = None
domain_id: str
Loading

0 comments on commit 34fbc4f

Please sign in to comment.