Skip to content

Commit

Permalink
Merge pull request #828 from mwhudson/simple-view-types
Browse files Browse the repository at this point in the history
convert proxy, mirror, identity, ssh views to only refer to simple types
  • Loading branch information
mwhudson committed Sep 19, 2020
2 parents 7545b91 + aaa0b9a commit 10fbd73
Show file tree
Hide file tree
Showing 11 changed files with 149 additions and 114 deletions.
37 changes: 37 additions & 0 deletions subiquity/common/types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Copyright 2020 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/>.

# This module defines types that will be used in the API when subiquity gets
# split into client and server processes. View code should only use these
# types!

from typing import List

import attr


@attr.s(auto_attribs=True)
class IdentityData:
realname: str = ''
username: str = ''
crypted_password: str = attr.ib(default='', repr=False)
hostname: str = ''


@attr.s(auto_attribs=True)
class SSHData:
install_server: bool
allow_pw: bool
authorized_keys: List[str] = attr.Factory(list)
38 changes: 24 additions & 14 deletions subiquity/controllers/identity.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

from subiquitycore.context import with_context

from subiquity.common.types import IdentityData
from subiquity.controller import SubiquityTuiController
from subiquity.ui.views import IdentityView

Expand All @@ -42,7 +43,13 @@ class IdentityController(SubiquityTuiController):

def load_autoinstall_data(self, data):
if data is not None:
self.model.add_user(data)
identity_data = IdentityData(
realname=data.get('realname', ''),
username=data['username'],
hostname=data['hostname'],
crypted_password=data['password'],
)
self.model.add_user(identity_data)

@with_context()
async def apply_autoinstall_config(self, context=None):
Expand All @@ -51,29 +58,32 @@ async def apply_autoinstall_config(self, context=None):
raise Exception("no identity data provided")

def make_ui(self):
return IdentityView(self.model, self)
data = IdentityData()
if self.model.user is not None:
data.username = self.model.user.username
data.realname = self.model.user.realname
if self.model.hostname:
data.hostname = self.model.hostname
return IdentityView(self, data)

def run_answers(self):
if all(elem in self.answers for elem in
['realname', 'username', 'password', 'hostname']):
d = {
'realname': self.answers['realname'],
'username': self.answers['username'],
'hostname': self.answers['hostname'],
'password': self.answers['password'],
}
self.done(d)
identity = IdentityData(
realname=self.answers['realname'],
username=self.answers['username'],
hostname=self.answers['hostname'],
crypted_password=self.answers['password'])
self.done(identity)

def cancel(self):
self.app.prev_screen()

def done(self, user_spec):
safe_spec = user_spec.copy()
safe_spec['password'] = '<REDACTED>'
def done(self, identity_data):
log.debug(
"IdentityController.done next_screen user_spec=%s",
safe_spec)
self.model.add_user(user_spec)
identity_data)
self.model.add_user(identity_data)
self.configured()
self.app.next_screen()

Expand Down
2 changes: 1 addition & 1 deletion subiquity/controllers/mirror.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ async def lookup(self, context):

def make_ui(self):
self.check_state = CheckState.DONE
return MirrorView(self.model, self)
return MirrorView(self, self.model.get_mirror())

def run_answers(self):
if 'mirror' in self.answers:
Expand Down
2 changes: 1 addition & 1 deletion subiquity/controllers/proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ async def apply_autoinstall_config(self, context=None):
pass

def make_ui(self):
return ProxyView(self.model, self)
return ProxyView(self, self.model.proxy)

def run_answers(self):
if 'proxy' in self.answers:
Expand Down
58 changes: 28 additions & 30 deletions subiquity/controllers/ssh.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from subiquitycore.context import with_context
from subiquitycore import utils

from subiquity.common.types import SSHData
from subiquity.controller import SubiquityTuiController
from subiquity.ui.views.ssh import SSHView

Expand Down Expand Up @@ -66,25 +67,25 @@ def load_autoinstall_data(self, data):
'allow-pw', not self.model.authorized_keys)

def make_ui(self):
return SSHView(self.model, self)
ssh_data = SSHData(
install_server=self.model.install_server,
allow_pw=self.model.pwauth)
return SSHView(self, ssh_data)

def run_answers(self):
if 'ssh-import-id' in self.answers:
import_id = self.answers['ssh-import-id']
d = {
"ssh_import_id": import_id.split(":", 1)[0],
"import_username": import_id.split(":", 1)[1],
"install_server": True,
"pwauth": True,
}
self.fetch_ssh_keys(d)
ssh = SSHData(
install_server=True,
authorized_keys=[],
allow_pw=True)
self.fetch_ssh_keys(ssh_import_id=import_id, ssh_data=ssh)
else:
d = {
"install_server": self.answers.get("install_server", False),
"authorized_keys": self.answers.get("authorized_keys", []),
"pwauth": self.answers.get("pwauth", True),
}
self.done(d)
ssh = SSHData(
install_server=self.answers.get("install_server", False),
authorized_keys=self.answers.get("authorized_keys", []),
allow_pw=self.answers.get("pwauth", True))
self.done(ssh)

def cancel(self):
self.app.prev_screen()
Expand All @@ -103,10 +104,8 @@ async def run_cmd_checked(self, cmd, *, failmsg, **kw):
return cp

@with_context(
name="ssh_import_id",
description="{user_spec[ssh_import_id]}:{user_spec[import_username]}")
async def _fetch_ssh_keys(self, *, context, user_spec):
ssh_import_id = "{ssh_import_id}:{import_username}".format(**user_spec)
name="ssh_import_id", description="{ssh_import_id}")
async def _fetch_ssh_keys(self, *, context, ssh_import_id, ssh_data):
with self.context.child("ssh_import_id", ssh_import_id):
try:
cp = await self.run_cmd_checked(
Expand All @@ -131,22 +130,21 @@ async def _fetch_ssh_keys(self, *, context, user_spec):
"").strip().splitlines()

if 'ssh-import-id' in self.app.answers.get("Identity", {}):
user_spec['authorized_keys'] = key_material.splitlines()
self.done(user_spec)
ssh_data.authorized_keys = key_material.splitlines()
self.done(ssh_data)
else:
self.ui.body.confirm_ssh_keys(
user_spec, ssh_import_id, key_material, fingerprints)
ssh_data, ssh_import_id, key_material, fingerprints)

def fetch_ssh_keys(self, user_spec):
def fetch_ssh_keys(self, ssh_import_id, ssh_data):
self._fetch_task = schedule_task(
self._fetch_ssh_keys(user_spec=user_spec))

def done(self, result):
log.debug("SSHController.done next_screen result=%s", result)
self.model.install_server = result['install_server']
self.model.authorized_keys = result.get('authorized_keys', [])
self.model.pwauth = result.get('pwauth', True)
self.model.ssh_import_id = result.get('ssh_import_id', None)
self._fetch_ssh_keys(
ssh_import_id=ssh_import_id, ssh_data=ssh_data))

def done(self, data: SSHData):
self.model.install_server = data.install_server
self.model.authorized_keys = data.authorized_keys
self.model.pwauth = data.allow_pw
self.configured()
self.app.next_screen()

Expand Down
20 changes: 9 additions & 11 deletions subiquity/models/identity.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@

import attr

from subiquitycore.utils import crypt_password


log = logging.getLogger('subiquity.models.identity')

Expand All @@ -38,12 +36,15 @@ def __init__(self):
self._user = None
self._hostname = None

def add_user(self, result):
result = result.copy()
self._hostname = result.pop('hostname')
if not result.get('realname'):
result['realname'] = result['username']
self._user = User(**result)
def add_user(self, identity_data):
self._hostname = identity_data.hostname
d = {}
d['realname'] = identity_data.realname
d['username'] = identity_data.username
d['password'] = identity_data.crypted_password
if not d['realname']:
d['realname'] = identity_data.username
self._user = User(**d)

@property
def hostname(self):
Expand All @@ -53,8 +54,5 @@ def hostname(self):
def user(self):
return self._user

def encrypt_password(self, passinput):
return crypt_password(passinput)

def __repr__(self):
return "<LocalUser: {} {}>".format(self.user, self.hostname)
31 changes: 14 additions & 17 deletions subiquity/ui/views/identity.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,11 @@
WantsToKnowFormField,
)
from subiquitycore.ui.utils import screen
from subiquitycore.utils import crypt_password
from subiquitycore.view import BaseView

from subiquity.common.types import IdentityData


log = logging.getLogger("subiquity.views.identity")

Expand Down Expand Up @@ -153,10 +156,8 @@ class IdentityView(BaseView):
"the system. You can configure SSH access on the next screen "
"but a password is still needed for sudo.")

def __init__(self, model, controller):
self.model = model
def __init__(self, controller, identity_data):
self.controller = controller
self.signal = controller.signal

reserved_usernames_path = (
os.path.join(os.environ.get("SNAP", "."), "reserved-usernames"))
Expand All @@ -171,14 +172,11 @@ def __init__(self, model, controller):
else:
reserved_usernames.add('root')

if model.user:
initial = {
'realname': model.user.realname,
'username': model.user.username,
'hostname': model.hostname,
initial = {
'realname': identity_data.realname,
'username': identity_data.username,
'hostname': identity_data.hostname,
}
else:
initial = {}

self.form = IdentityForm(reserved_usernames, initial)

Expand All @@ -193,10 +191,9 @@ def __init__(self, model, controller):
focus_buttons=False))

def done(self, result):
result = {
"hostname": self.form.hostname.value,
"realname": self.form.realname.value,
"username": self.form.username.value,
"password": self.model.encrypt_password(self.form.password.value),
}
self.controller.done(result)
self.controller.done(IdentityData(
hostname=self.form.hostname.value,
realname=self.form.realname.value,
username=self.form.username.value,
crypted_password=crypt_password(self.form.password.value),
))
5 changes: 2 additions & 3 deletions subiquity/ui/views/mirror.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,10 @@ class MirrorView(BaseView):
excerpt = _("If you use an alternative mirror for Ubuntu, enter its "
"details here.")

def __init__(self, model, controller):
self.model = model
def __init__(self, controller, mirror):
self.controller = controller

self.form = MirrorForm(initial={'url': self.model.get_mirror()})
self.form = MirrorForm(initial={'url': mirror})

connect_signal(self.form, 'submit', self.done)
connect_signal(self.form, 'cancel', self.cancel)
Expand Down
5 changes: 2 additions & 3 deletions subiquity/ui/views/proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,10 @@ class ProxyView(BaseView):
excerpt = _("If this system requires a proxy to connect to the internet, "
"enter its details here.")

def __init__(self, model, controller):
self.model = model
def __init__(self, controller, proxy):
self.controller = controller

self.form = ProxyForm(initial={'url': self.model.proxy})
self.form = ProxyForm(initial={'url': proxy})

connect_signal(self.form, 'submit', self.done)
connect_signal(self.form, 'cancel', self.cancel)
Expand Down
Loading

0 comments on commit 10fbd73

Please sign in to comment.