user.scm 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2018 Mathieu Othacehe <m.othacehe@gmail.com>
  3. ;;; Copyright © 2019, 2020 Ludovic Courtès <ludo@gnu.org>
  4. ;;; Copyright © 2019 Tobias Geerinckx-Rice <me@tobias.gr>
  5. ;;;
  6. ;;; This file is part of GNU Guix.
  7. ;;;
  8. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  9. ;;; under the terms of the GNU General Public License as published by
  10. ;;; the Free Software Foundation; either version 3 of the License, or (at
  11. ;;; your option) any later version.
  12. ;;;
  13. ;;; GNU Guix is distributed in the hope that it will be useful, but
  14. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  15. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. ;;; GNU General Public License for more details.
  17. ;;;
  18. ;;; You should have received a copy of the GNU General Public License
  19. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  20. (define-module (gnu installer newt user)
  21. #:use-module (gnu installer user)
  22. #:use-module ((gnu installer steps) #:select (&installer-step-abort))
  23. #:use-module (gnu installer newt page)
  24. #:use-module (gnu installer newt utils)
  25. #:use-module (gnu installer utils)
  26. #:use-module (guix i18n)
  27. #:use-module (newt)
  28. #:use-module (ice-9 match)
  29. #:use-module (ice-9 receive)
  30. #:use-module (srfi srfi-1)
  31. #:use-module (srfi srfi-26)
  32. #:use-module (srfi srfi-34)
  33. #:use-module (srfi srfi-35)
  34. #:export (run-user-page))
  35. (define* (run-user-add-page #:key (name "") (real-name "")
  36. (home-directory ""))
  37. "Run a form to enter the user name, home directory, and password. Use NAME,
  38. REAL-NAME, and HOME-DIRECTORY as the initial values in the form."
  39. (define (pad-label label)
  40. (string-pad-right label 25))
  41. (let* ((label-name
  42. (make-label -1 -1 (pad-label (G_ "Name"))))
  43. (label-real-name
  44. (make-label -1 -1 (pad-label (G_ "Real name"))))
  45. (label-home-directory
  46. (make-label -1 -1 (pad-label (G_ "Home directory"))))
  47. (label-password
  48. (make-label -1 -1 (pad-label (G_ "Password"))))
  49. (entry-width 35)
  50. (entry-name (make-entry -1 -1 entry-width
  51. #:initial-value name))
  52. (entry-real-name (make-entry -1 -1 entry-width
  53. #:initial-value real-name))
  54. (entry-home-directory (make-entry -1 -1 entry-width
  55. #:initial-value home-directory))
  56. (password-visible-cb
  57. (make-checkbox -1 -1 (G_ "Show") #\space "x "))
  58. (entry-password (make-entry -1 -1 entry-width
  59. #:flags (logior FLAG-PASSWORD
  60. FLAG-SCROLL)))
  61. (entry-grid (make-grid 3 5))
  62. (button-grid (make-grid 1 1))
  63. (ok-button (make-button -1 -1 (G_ "OK")))
  64. (grid (make-grid 1 2))
  65. (title (G_ "User creation"))
  66. (set-entry-grid-field
  67. (cut set-grid-field entry-grid <> <> GRID-ELEMENT-COMPONENT <>))
  68. (form (make-form)))
  69. (set-entry-grid-field 0 0 label-name)
  70. (set-entry-grid-field 1 0 entry-name)
  71. (set-entry-grid-field 0 1 label-real-name)
  72. (set-entry-grid-field 1 1 entry-real-name)
  73. (set-entry-grid-field 0 2 label-home-directory)
  74. (set-entry-grid-field 1 2 entry-home-directory)
  75. (set-entry-grid-field 0 3 label-password)
  76. (set-entry-grid-field 1 3 entry-password)
  77. (set-grid-field entry-grid
  78. 2 3
  79. GRID-ELEMENT-COMPONENT
  80. password-visible-cb
  81. #:pad-left 1)
  82. (set-grid-field button-grid 0 0 GRID-ELEMENT-COMPONENT ok-button)
  83. (add-component-callback
  84. entry-name
  85. (lambda ()
  86. (set-entry-text entry-home-directory
  87. (string-append "/home/" (entry-value entry-name)))
  88. (when (string-null? (entry-value entry-real-name))
  89. (set-entry-text entry-real-name
  90. (string-titlecase (entry-value entry-name))))))
  91. (add-component-callback
  92. password-visible-cb
  93. (lambda ()
  94. (set-entry-flags entry-password
  95. FLAG-PASSWORD
  96. FLAG-ROLE-TOGGLE)))
  97. (add-components-to-form form
  98. label-name label-real-name
  99. label-home-directory label-password
  100. entry-name entry-real-name
  101. entry-home-directory entry-password
  102. password-visible-cb
  103. ok-button)
  104. (make-wrapped-grid-window (vertically-stacked-grid
  105. GRID-ELEMENT-SUBGRID entry-grid
  106. GRID-ELEMENT-SUBGRID button-grid)
  107. title)
  108. (let ((error-page
  109. (lambda ()
  110. (run-error-page (G_ "Empty inputs are not allowed.")
  111. (G_ "Empty input")))))
  112. (receive (exit-reason argument)
  113. (run-form form)
  114. (dynamic-wind
  115. (const #t)
  116. (lambda ()
  117. (when (eq? exit-reason 'exit-component)
  118. (cond
  119. ((components=? argument ok-button)
  120. (let ((name (entry-value entry-name))
  121. (real-name (entry-value entry-real-name))
  122. (home-directory (entry-value entry-home-directory))
  123. (password (entry-value entry-password)))
  124. (if (or (string=? name "")
  125. (string=? home-directory ""))
  126. (begin
  127. (error-page)
  128. (run-user-add-page))
  129. (let ((password (confirm-password password)))
  130. (if password
  131. (user
  132. (name name)
  133. (real-name real-name)
  134. (home-directory home-directory)
  135. (password password))
  136. (run-user-add-page #:name name
  137. #:real-name real-name
  138. #:home-directory
  139. home-directory)))))))))
  140. (lambda ()
  141. (destroy-form-and-pop form)))))))
  142. (define* (confirm-password password #:optional (try-again (const #f)))
  143. "Ask the user to confirm PASSWORD, a possibly empty string. Call TRY-AGAIN,
  144. a thunk, if the confirmation doesn't match PASSWORD, and return its result."
  145. (define confirmation
  146. (run-input-page (G_ "Please confirm the password.")
  147. (G_ "Password confirmation required")
  148. #:allow-empty-input? #t
  149. #:input-visibility-checkbox? #t))
  150. (if (string=? password confirmation)
  151. password
  152. (begin
  153. (run-error-page
  154. (G_ "Password mismatch, please try again.")
  155. (G_ "Password error"))
  156. (try-again))))
  157. (define (run-root-password-page)
  158. (define password
  159. ;; TRANSLATORS: Leave "root" untranslated: it refers to the name of the
  160. ;; system administrator account.
  161. (run-input-page (G_ "Please choose a password for the system \
  162. administrator (\"root\").")
  163. (G_ "System administrator password")
  164. #:input-visibility-checkbox? #t))
  165. (confirm-password password run-root-password-page))
  166. (define (run-user-page)
  167. (define (run users)
  168. (let* ((listbox (make-listbox
  169. -1 -1 10
  170. (logior FLAG-SCROLL FLAG-BORDER)))
  171. (info-textbox
  172. (make-reflowed-textbox
  173. -1 -1
  174. (G_ "Please add at least one user to system\
  175. using the 'Add' button.")
  176. 40 #:flags FLAG-BORDER))
  177. (add-button (make-compact-button -1 -1 (G_ "Add")))
  178. (del-button (make-compact-button -1 -1 (G_ "Delete")))
  179. (listbox-button-grid
  180. (apply
  181. vertically-stacked-grid
  182. GRID-ELEMENT-COMPONENT add-button
  183. `(,@(if (null? users)
  184. '()
  185. (list GRID-ELEMENT-COMPONENT del-button)))))
  186. (ok-button (make-button -1 -1 (G_ "OK")))
  187. (exit-button (make-button -1 -1 (G_ "Exit")))
  188. (title (G_ "User creation"))
  189. (grid
  190. (vertically-stacked-grid
  191. GRID-ELEMENT-COMPONENT info-textbox
  192. GRID-ELEMENT-SUBGRID (horizontal-stacked-grid
  193. GRID-ELEMENT-COMPONENT listbox
  194. GRID-ELEMENT-SUBGRID listbox-button-grid)
  195. GRID-ELEMENT-SUBGRID (horizontal-stacked-grid
  196. GRID-ELEMENT-COMPONENT ok-button
  197. GRID-ELEMENT-COMPONENT exit-button)))
  198. (sorted-users (sort users (lambda (a b)
  199. (string<= (user-name a)
  200. (user-name b)))))
  201. (listbox-elements
  202. (map
  203. (lambda (user)
  204. `((key . ,(append-entry-to-listbox listbox
  205. (user-name user)))
  206. (user . ,user)))
  207. sorted-users))
  208. (form (make-form)))
  209. (add-form-to-grid grid form #t)
  210. (make-wrapped-grid-window grid title)
  211. (if (null? users)
  212. (set-current-component form add-button)
  213. (set-current-component form ok-button))
  214. (receive (exit-reason argument)
  215. (run-form-with-clients form '(add-users))
  216. (dynamic-wind
  217. (const #t)
  218. (lambda ()
  219. (match exit-reason
  220. ('exit-component
  221. (cond
  222. ((components=? argument add-button)
  223. (run (cons (run-user-add-page) users)))
  224. ((components=? argument del-button)
  225. (let* ((current-user-key (current-listbox-entry listbox))
  226. (users
  227. (map (cut assoc-ref <> 'user)
  228. (remove (lambda (element)
  229. (equal? (assoc-ref element 'key)
  230. current-user-key))
  231. listbox-elements))))
  232. (run users)))
  233. ((components=? argument ok-button)
  234. (when (null? users)
  235. (run-error-page (G_ "Please create at least one user.")
  236. (G_ "No user"))
  237. (run users))
  238. (reverse users))
  239. ((components=? argument exit-button)
  240. (raise
  241. (condition
  242. (&installer-step-abort))))))
  243. ('exit-fd-ready
  244. ;; Read the complete user list at once.
  245. (match argument
  246. ((('user ('name names) ('real-name real-names)
  247. ('home-directory homes) ('password passwords))
  248. ..1)
  249. (map (lambda (name real-name home password)
  250. (user (name name) (real-name real-name)
  251. (home-directory home)
  252. (password password)))
  253. names real-names homes passwords))))))
  254. (lambda ()
  255. (destroy-form-and-pop form))))))
  256. ;; Add a "root" user simply to convey the root password.
  257. (cons (user (name "root")
  258. (home-directory "/root")
  259. (password (run-root-password-page)))
  260. (run '())))