user.scm 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2018 Mathieu Othacehe <m.othacehe@gmail.com>
  3. ;;; Copyright © 2019 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 (guix i18n)
  26. #:use-module (newt)
  27. #:use-module (ice-9 match)
  28. #:use-module (ice-9 receive)
  29. #:use-module (srfi srfi-1)
  30. #:use-module (srfi srfi-26)
  31. #:use-module (srfi srfi-34)
  32. #:use-module (srfi srfi-35)
  33. #:export (run-user-page))
  34. (define* (run-user-add-page #:key (name "") (real-name "")
  35. (home-directory ""))
  36. "Run a form to enter the user name, home directory, and password. Use NAME,
  37. REAL-NAME, and HOME-DIRECTORY as the initial values in the form."
  38. (define (pad-label label)
  39. (string-pad-right label 25))
  40. (let* ((label-name
  41. (make-label -1 -1 (pad-label (G_ "Name"))))
  42. (label-real-name
  43. (make-label -1 -1 (pad-label (G_ "Real name"))))
  44. (label-home-directory
  45. (make-label -1 -1 (pad-label (G_ "Home directory"))))
  46. (label-password
  47. (make-label -1 -1 (pad-label (G_ "Password"))))
  48. (entry-width 35)
  49. (entry-name (make-entry -1 -1 entry-width
  50. #:initial-value name))
  51. (entry-real-name (make-entry -1 -1 entry-width
  52. #:initial-value real-name))
  53. (entry-home-directory (make-entry -1 -1 entry-width
  54. #:initial-value home-directory))
  55. (password-visible-cb
  56. (make-checkbox -1 -1 (G_ "Show") #\space "x "))
  57. (entry-password (make-entry -1 -1 entry-width
  58. #:flags (logior FLAG-PASSWORD
  59. FLAG-SCROLL)))
  60. (entry-grid (make-grid 3 5))
  61. (button-grid (make-grid 1 1))
  62. (ok-button (make-button -1 -1 (G_ "OK")))
  63. (grid (make-grid 1 2))
  64. (title (G_ "User creation"))
  65. (set-entry-grid-field
  66. (cut set-grid-field entry-grid <> <> GRID-ELEMENT-COMPONENT <>))
  67. (form (make-form)))
  68. (set-entry-grid-field 0 0 label-name)
  69. (set-entry-grid-field 1 0 entry-name)
  70. (set-entry-grid-field 0 1 label-real-name)
  71. (set-entry-grid-field 1 1 entry-real-name)
  72. (set-entry-grid-field 0 2 label-home-directory)
  73. (set-entry-grid-field 1 2 entry-home-directory)
  74. (set-entry-grid-field 0 3 label-password)
  75. (set-entry-grid-field 1 3 entry-password)
  76. (set-grid-field entry-grid
  77. 2 3
  78. GRID-ELEMENT-COMPONENT
  79. password-visible-cb
  80. #:pad-left 1)
  81. (set-grid-field button-grid 0 0 GRID-ELEMENT-COMPONENT ok-button)
  82. (add-component-callback
  83. entry-name
  84. (lambda (component)
  85. (set-entry-text entry-home-directory
  86. (string-append "/home/" (entry-value entry-name)))
  87. (when (string-null? (entry-value entry-real-name))
  88. (set-entry-text entry-real-name
  89. (string-titlecase (entry-value entry-name))))))
  90. (add-component-callback
  91. password-visible-cb
  92. (lambda (component)
  93. (set-entry-flags entry-password
  94. FLAG-PASSWORD
  95. FLAG-ROLE-TOGGLE)))
  96. (add-components-to-form form
  97. label-name label-real-name
  98. label-home-directory label-password
  99. entry-name entry-real-name
  100. entry-home-directory entry-password
  101. password-visible-cb
  102. ok-button)
  103. (make-wrapped-grid-window (vertically-stacked-grid
  104. GRID-ELEMENT-SUBGRID entry-grid
  105. GRID-ELEMENT-SUBGRID button-grid)
  106. title)
  107. (let ((error-page
  108. (lambda ()
  109. (run-error-page (G_ "Empty inputs are not allowed.")
  110. (G_ "Empty input")))))
  111. (receive (exit-reason argument)
  112. (run-form form)
  113. (dynamic-wind
  114. (const #t)
  115. (lambda ()
  116. (when (eq? exit-reason 'exit-component)
  117. (cond
  118. ((components=? argument ok-button)
  119. (let ((name (entry-value entry-name))
  120. (real-name (entry-value entry-real-name))
  121. (home-directory (entry-value entry-home-directory))
  122. (password (entry-value entry-password)))
  123. (if (or (string=? name "")
  124. (string=? home-directory ""))
  125. (begin
  126. (error-page)
  127. (run-user-add-page))
  128. (let ((password (confirm-password password)))
  129. (if password
  130. (user
  131. (name name)
  132. (real-name real-name)
  133. (home-directory home-directory)
  134. (password password))
  135. (run-user-add-page #:name name
  136. #:real-name real-name
  137. #:home-directory
  138. home-directory)))))))))
  139. (lambda ()
  140. (destroy-form-and-pop form)))))))
  141. (define* (confirm-password password #:optional (try-again (const #f)))
  142. "Ask the user to confirm PASSWORD, a possibly empty string. Call TRY-AGAIN,
  143. a thunk, if the confirmation doesn't match PASSWORD, and return its result."
  144. (define confirmation
  145. (run-input-page (G_ "Please confirm the password.")
  146. (G_ "Password confirmation required")
  147. #:allow-empty-input? #t
  148. #:input-visibility-checkbox? #t))
  149. (if (string=? password confirmation)
  150. password
  151. (begin
  152. (run-error-page
  153. (G_ "Password mismatch, please try again.")
  154. (G_ "Password error"))
  155. (try-again))))
  156. (define (run-root-password-page)
  157. ;; TRANSLATORS: Leave "root" untranslated: it refers to the name of the
  158. ;; system administrator account.
  159. (define password
  160. (run-input-page (G_ "Please choose a password for the system \
  161. administrator (\"root\").")
  162. (G_ "System administrator password")
  163. #:input-visibility-checkbox? #t))
  164. (confirm-password password run-root-password-page))
  165. (define (run-user-page)
  166. (define (run users)
  167. (let* ((listbox (make-listbox
  168. -1 -1 10
  169. (logior FLAG-SCROLL FLAG-BORDER)))
  170. (info-textbox
  171. (make-reflowed-textbox
  172. -1 -1
  173. (G_ "Please add at least one user to system\
  174. using the 'Add' button.")
  175. 40 #:flags FLAG-BORDER))
  176. (add-button (make-compact-button -1 -1 (G_ "Add")))
  177. (del-button (make-compact-button -1 -1 (G_ "Delete")))
  178. (listbox-button-grid
  179. (apply
  180. vertically-stacked-grid
  181. GRID-ELEMENT-COMPONENT add-button
  182. `(,@(if (null? users)
  183. '()
  184. (list GRID-ELEMENT-COMPONENT del-button)))))
  185. (ok-button (make-button -1 -1 (G_ "OK")))
  186. (exit-button (make-button -1 -1 (G_ "Exit")))
  187. (title (G_ "User creation"))
  188. (grid
  189. (vertically-stacked-grid
  190. GRID-ELEMENT-COMPONENT info-textbox
  191. GRID-ELEMENT-SUBGRID (horizontal-stacked-grid
  192. GRID-ELEMENT-COMPONENT listbox
  193. GRID-ELEMENT-SUBGRID listbox-button-grid)
  194. GRID-ELEMENT-SUBGRID (horizontal-stacked-grid
  195. GRID-ELEMENT-COMPONENT ok-button
  196. GRID-ELEMENT-COMPONENT exit-button)))
  197. (sorted-users (sort users (lambda (a b)
  198. (string<= (user-name a)
  199. (user-name b)))))
  200. (listbox-elements
  201. (map
  202. (lambda (user)
  203. `((key . ,(append-entry-to-listbox listbox
  204. (user-name user)))
  205. (user . ,user)))
  206. sorted-users))
  207. (form (make-form)))
  208. (add-form-to-grid grid form #t)
  209. (make-wrapped-grid-window grid title)
  210. (if (null? users)
  211. (set-current-component form add-button)
  212. (set-current-component form ok-button))
  213. (receive (exit-reason argument)
  214. (run-form form)
  215. (dynamic-wind
  216. (const #t)
  217. (lambda ()
  218. (when (eq? exit-reason 'exit-component)
  219. (cond
  220. ((components=? argument add-button)
  221. (run (cons (run-user-add-page) users)))
  222. ((components=? argument del-button)
  223. (let* ((current-user-key (current-listbox-entry listbox))
  224. (users
  225. (map (cut assoc-ref <> 'user)
  226. (remove (lambda (element)
  227. (equal? (assoc-ref element 'key)
  228. current-user-key))
  229. listbox-elements))))
  230. (run users)))
  231. ((components=? argument ok-button)
  232. (when (null? users)
  233. (run-error-page (G_ "Please create at least one user.")
  234. (G_ "No user"))
  235. (run users))
  236. (reverse users))
  237. ((components=? argument exit-button)
  238. (raise
  239. (condition
  240. (&installer-step-abort)))))))
  241. (lambda ()
  242. (destroy-form-and-pop form))))))
  243. ;; Add a "root" user simply to convey the root password.
  244. (cons (user (name "root")
  245. (home-directory "/root")
  246. (password (run-root-password-page)))
  247. (run '())))