Merge pull request #128 from CanonicalLtd/identity_ux_update

Identity ux update
This commit is contained in:
Adam Stokes 2015-11-20 17:17:13 -05:00
commit 2bd7c2a00a
5 changed files with 101 additions and 181 deletions

View File

@ -1,36 +0,0 @@
# Copyright 2015 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/>.
from subiquity.controller import ControllerPolicy
from subiquity.models import HostnameModel
from subiquity.ui.views import HostnameView
class HostnameController(ControllerPolicy):
def __init__(self, common):
super().__init__(common)
self.model = HostnameModel()
def hostname(self):
title = "Set hostname"
excerpt = ("Set the systems hostname")
footer = ""
self.ui.set_header(title, excerpt)
self.ui.set_footer(footer, 40)
self.ui.set_body(HostnameView(self.model, self.signal))
def hostname_finish(self, hostname):
# Do curtin hostname setup
self.signal.emit_signal('identity:show')

View File

@ -94,6 +94,8 @@ def curtin_userinfo_to_config(userinfo):
' passwd: {password}\\n' + \
' shell: /bin/bash\\n' + \
' groups: admin\\n' + \
' ssh_import_id: [{ssh_import_id}]\\n' + \
' hostname: {hostname}\\n' + \
' lock-passwd: false\\n'
return user_template.format(**userinfo)

View File

@ -1,50 +0,0 @@
# Copyright 2015 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 subiquity.model import ModelPolicy
log = logging.getLogger('subiquity.models.hostname')
class HostnameModel(ModelPolicy):
""" Model representing system hostname
"""
prev_signal = ('Back to filesystem view',
'filesystem:show',
'filesystem')
signals = [
("Hostname View",
'hostname:show',
'hostname')
]
hostname_menu = []
def get_signals(self):
return self.signals
def get_menu(self):
return self.hostname_menu
def get_signal_by_name(self, selection):
for x, y, z in self.get_menu():
if x == selection:
return y
def __repr__(self):
return "<Hostname: {}>".format(self.hostname)

View File

@ -1,75 +0,0 @@
# Copyright 2015 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/>.
""" Hostname
Sets system hostname
"""
import logging
from urwid import (Pile, Columns, Text, ListBox)
from subiquity.ui.buttons import done_btn, cancel_btn
from subiquity.ui.interactive import StringEditor
from subiquity.ui.utils import Padding, Color
from subiquity.view import ViewPolicy
log = logging.getLogger("subiquity.views.hostname")
class HostnameView(ViewPolicy):
def __init__(self, model, signal):
self.model = model
self.signal = signal
self.hostname = StringEditor(caption="")
body = [
Padding.center_50(self._build_model_inputs()),
Padding.line_break(""),
Padding.fixed_10(self._build_buttons()),
]
super().__init__(ListBox(body))
def _build_buttons(self):
cancel = cancel_btn(on_press=self.cancel)
done = done_btn(on_press=self.done)
buttons = [
Color.button(done, focus_map='button focus'),
Color.button(cancel, focus_map='button focus')
]
return Pile(buttons)
def _build_model_inputs(self):
sl = [
Columns(
[
("weight", 0.2, Text("Hostname", align="right")),
("weight", 0.3,
Color.string_input(self.hostname,
focus_map="string_input focus"))
],
)
]
return Pile(sl)
def done(self, result):
result = {
"hostname": self.hostname.value
}
self.signal.emit_signal('hostname:finish', result)
def cancel(self, button):
self.signal.emit_signal(self.model.get_previous_signal)

View File

@ -23,6 +23,7 @@ from urwid import (Pile, Columns, Text, ListBox)
from subiquity.ui.buttons import done_btn, cancel_btn
from subiquity.ui.interactive import (PasswordEditor,
RealnameEditor,
StringEditor,
UsernameEditor)
from subiquity.ui.utils import Padding, Color
from subiquity.view import ViewPolicy
@ -31,8 +32,10 @@ from subiquity.curtin import curtin_write_postinst_config
log = logging.getLogger("subiquity.views.identity")
USERNAME_MAXLEN = 32
HOSTNAME_MAXLEN = 64
REALNAME_MAXLEN = 160
SSH_IMPORT_MAXLEN = 256 + 3 # account for lp: or gh:
USERNAME_MAXLEN = 32
class IdentityView(ViewPolicy):
@ -41,15 +44,18 @@ class IdentityView(ViewPolicy):
self.signal = signal
self.items = []
self.realname = RealnameEditor(caption="")
self.hostname = UsernameEditor(caption="")
self.username = UsernameEditor(caption="")
self.password = PasswordEditor(caption="")
self.ssh_import_id = StringEditor(caption="")
self.ssh_import_confirmed = True
self.error = Text("", align="center")
self.confirm_password = PasswordEditor(caption="")
body = [
Padding.center_50(self._build_model_inputs()),
Padding.center_90(self._build_model_inputs()),
Padding.line_break(""),
Padding.center_50(Color.info_error(self.error)),
Padding.center_90(Color.info_error(self.error)),
Padding.line_break(""),
Padding.fixed_10(self._build_buttons()),
]
@ -69,7 +75,7 @@ class IdentityView(ViewPolicy):
sl = [
Columns(
[
("weight", 0.2, Text("Real Name", align="right")),
("weight", 0.2, Text("Your name:", align="right")),
("weight", 0.3,
Color.string_input(self.realname,
focus_map="string_input focus"))
@ -78,7 +84,26 @@ class IdentityView(ViewPolicy):
),
Columns(
[
("weight", 0.2, Text("Username", align="right")),
("weight", 0.2, Text("Your server's name:",
align="right")),
("weight", 0.3,
Color.string_input(self.hostname,
focus_map="string_input focus"))
],
dividechars=4
),
Columns(
[
("weight", 0.2, Text("", align="right")),
("weight", 0.3, Color.info_minor(
Text("The name it uses when it talks to "
"other computers", align="left"))),
],
dividechars=4
),
Columns(
[
("weight", 0.2, Text("Pick a username:", align="right")),
("weight", 0.3,
Color.string_input(self.username,
focus_map="string_input focus"))
@ -87,7 +112,7 @@ class IdentityView(ViewPolicy):
),
Columns(
[
("weight", 0.2, Text("Password", align="right")),
("weight", 0.2, Text("Choose a password:", align="right")),
("weight", 0.3,
Color.string_input(self.password,
focus_map="string_input focus"))
@ -96,29 +121,42 @@ class IdentityView(ViewPolicy):
),
Columns(
[
("weight", 0.2, Text("Confirm Password", align="right")),
("weight", 0.2, Text("Confirm your password:",
align="right")),
("weight", 0.3,
Color.string_input(self.confirm_password,
focus_map="string_input focus"))
],
dividechars=4
)
),
Columns(
[
("weight", 0.2, Text("Import SSH identity:",
align="right")),
("weight", 0.3,
Color.string_input(self.ssh_import_id,
focus_map="string_input focus"))
],
dividechars=4
),
Columns(
[
("weight", 0.2, Text("", align="right")),
("weight", 0.3, Color.info_minor(
Text("Input your SSH user id from "
"Launchpad (lp:username) or "
"Github (gh:username).",
align="left"))),
],
dividechars=4
),
]
return Pile(sl)
def done(self, result):
if len(self.password.value) < 1:
self.error.set_text("Password must be set")
self.password.value = ""
self.confirm_password.value = ""
return
if self.password.value != self.confirm_password.value:
self.error.set_text("Passwords do not match.")
self.password.value = ""
self.confirm_password.value = ""
return
# check in display order:
# realname, hostname, username, password, ssh
if len(self.realname.value) < 1:
self.error.set_text("Realname missing.")
self.realname.value = ""
@ -130,6 +168,17 @@ class IdentityView(ViewPolicy):
self.realname.value = ""
return
if len(self.hostname.value) < 1:
self.error.set_text("Server name missing.")
self.hostname.value = ""
return
if len(self.hostname.value) > HOSTNAME_MAXLEN:
self.error.set_text("Server name too long, must be < " +
str(HOSTNAME_MAXLEN))
self.hostname.value = ""
return
if len(self.username.value) < 1:
self.error.set_text("Username missing.")
self.username.value = ""
@ -141,15 +190,45 @@ class IdentityView(ViewPolicy):
self.username.value = ""
return
if len(self.password.value) < 1:
self.error.set_text("Password must be set")
self.password.value = ""
self.confirm_password.value = ""
return
if self.password.value != self.confirm_password.value:
self.error.set_text("Passwords do not match.")
self.password.value = ""
self.confirm_password.value = ""
return
# ssh_id is optional
if len(self.ssh_import_id.value) > SSH_IMPORT_MAXLEN:
self.error.set_text("SSH id too long, must be < " +
str(SSH_IMPORT_MAXLEN))
self.ssh_import_id.value = ""
return
cpassword = self.model.encrypt_password(self.password.value)
log.debug("*crypted* User input: {} {} {}".format(
self.username.value, cpassword, cpassword))
result = {
"hostname": self.hostname.value,
"realname": self.realname.value,
"username": self.username.value,
"password": cpassword,
"confirm_password": cpassword,
}
# if user specifed a value, allow user to validate fingerprint
if self.ssh_import_id.value:
if self.ssh_import_confirmed is True:
result.update({'ssh_import_id': self.ssh_import_id.value})
else:
self.emit_signal('identity:confirm-ssh-id',
self.ssh_import_id.value)
return
log.debug("User input: {}".format(result))
try:
curtin_write_postinst_config(result)