Merge pull request #1550 from CarlosNihelton/ad-api-deeng-576

Active Directory API Part 1
This commit is contained in:
Carlos Nihelton 2023-02-08 09:25:17 -03:00 committed by GitHub
commit 7e2ceb9f94
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 129 additions and 0 deletions

View File

@ -25,6 +25,8 @@ from subiquitycore.models.network import (
from subiquity.common.api.defs import api, Payload, simple_endpoint
from subiquity.common.types import (
ADValidationResult,
ADConnectionInfo,
AddPartitionV2,
AnyStep,
ApplicationState,
@ -403,6 +405,16 @@ class API:
class integrity:
def GET() -> CasperMd5Results: ...
class active_directory:
def GET() -> Optional[ADConnectionInfo]: ...
# POST must validate the payload before configuring the controller,
# which may contain several errors as described in [ADValidationResult]
# simultaneously - such as invalid chars on the admin name and DC name
# starting with a hyphen or a dot. Thus this must returns a List
# of errors [ADValidationResult.OK] on success.
def POST(data: Payload[ADConnectionInfo]) \
-> List[ADValidationResult]: ...
class LinkAction(enum.Enum):
NEW = enum.auto()

View File

@ -745,3 +745,21 @@ class MirrorCheckResponse:
url: str
status: MirrorCheckStatus
output: str
@attr.s(auto_attribs=True)
class ADConnectionInfo:
admin_name: str = ""
domain_name: str = ""
password: str = attr.ib(repr=False, default="")
class ADValidationResult(enum.Enum):
OK = enum.auto()
ADMIN_NAME_BAD_FIRST_CHAR = enum.auto()
ADMIN_NAME_BAD_CHARS = enum.auto()
DCNAME_BAD_CHARS = enum.auto()
DCNAME_BAD_HYPHEN = enum.auto()
DCNAME_BAD_DOTS = enum.auto()
DCNAME_BAD_LENGTH = enum.auto()
PASSWORD_EMPTY = enum.auto()

36
subiquity/models/ad.py Normal file
View File

@ -0,0 +1,36 @@
# Copyright 2023 Canonical, Ltd.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import logging
from typing import Optional
from subiquity.common.types import ADConnectionInfo
log = logging.getLogger('subiquity.models.ad')
class ADModel:
""" Models the Active Directory feature """
def __init__(self) -> None:
self.do_join = False
self.conn_info: Optional[ADConnectionInfo] = None
async def target_packages(self):
# NOTE Those packages must be present in the target system to allow
# joining to a domain.
if self.do_join:
return ["adcli", "realmd", "sssd"]
return []

View File

@ -46,6 +46,7 @@ from subiquitycore.lsb_release import lsb_release
from subiquity.common.resources import get_users_and_groups
from subiquity.server.types import InstallerChannels
from .ad import ADModel
from .codecs import CodecsModel
from .drivers import DriversModel
from .filesystem import FilesystemModel
@ -175,6 +176,7 @@ class SubiquityModel:
self.target = root
self.chroot_prefix = []
self.ad = ADModel()
self.codecs = CodecsModel()
self.debconf_selections = DebconfSelectionsModel()
self.drivers = DriversModel()

View File

@ -13,6 +13,7 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from .ad import ADController
from .cmdlist import EarlyController, LateController, ErrorController
from .codecs import CodecsController
from .debconf import DebconfController
@ -41,6 +42,7 @@ from .userdata import UserdataController
from .zdev import ZdevController
__all__ = [
'ADController',
'CodecsController',
'DebconfController',
'DriversController',

View File

@ -0,0 +1,39 @@
# Copyright 2023 Canonical, Ltd.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import logging
from typing import List, Optional
from subiquity.common.apidef import API
from subiquity.common.types import ADConnectionInfo, ADValidationResult
from subiquity.server.controller import SubiquityController
log = logging.getLogger('subiquity.server.controllers.ad')
class ADController(SubiquityController):
""" Implements the server part of the Active Directory feature. """
model_name = "ad"
endpoint = API.active_directory
# No auto install key and schema for now due password handling uncertainty.
async def GET(self) -> Optional[ADConnectionInfo]:
"""Returns the currently configured AD settings"""
return self.model.conn_info
async def POST(self, data: ADConnectionInfo) -> List[ADValidationResult]:
self.model.conn_info = data
await self.configured()
return [ADValidationResult.OK]

View File

@ -256,6 +256,7 @@ class SubiquityServer(Application):
"Identity",
"SSH",
"SnapList",
"AD",
"Codecs",
"Drivers",
"TimeZone",

View File

@ -1632,3 +1632,22 @@ class TestWSLSetupOptions(TestAPI):
resp = await inst.get(endpoint)
self.assertFalse(resp['install_language_support_packages'])
class TestActiveDirectory(TestAPI):
@timeout()
async def test_ad(self):
async with start_server('examples/simple.json') as instance:
endpoint = '/active_directory'
ad_dict = await instance.get(endpoint)
# Starts empty
self.assertIsNone(ad_dict)
# POST succeeds
ad_dict = {
'domain_name': 'ubuntu.com',
'admin_name': 'u',
'password': 'u'
}
result = await instance.post(endpoint, ad_dict)
self.assertEqual(['OK'], result)