accounts.scm 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2019 Ludovic Courtès <ludo@gnu.org>
  3. ;;;
  4. ;;; This file is part of GNU Guix.
  5. ;;;
  6. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  7. ;;; under the terms of the GNU General Public License as published by
  8. ;;; the Free Software Foundation; either version 3 of the License, or (at
  9. ;;; your option) any later version.
  10. ;;;
  11. ;;; GNU Guix is distributed in the hope that it will be useful, but
  12. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. ;;; GNU General Public License for more details.
  15. ;;;
  16. ;;; You should have received a copy of the GNU General Public License
  17. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  18. (define-module (test-accounts)
  19. #:use-module (gnu build accounts)
  20. #:use-module (gnu system accounts)
  21. #:use-module (srfi srfi-19)
  22. #:use-module (srfi srfi-64)
  23. #:use-module (ice-9 vlist)
  24. #:use-module (ice-9 match))
  25. (define %passwd-sample
  26. "\
  27. root:x:0:0:Admin:/root:/bin/sh
  28. charlie:x:1000:998:Charlie:/home/charlie:/bin/sh\n")
  29. (define %group-sample
  30. "\
  31. root:x:0:
  32. wheel:x:999:alice,bob
  33. hackers:x:65000:alice,charlie\n")
  34. (define %shadow-sample
  35. (string-append "\
  36. root:" (crypt "secret" "$6$abc") ":17169::::::
  37. charlie:" (crypt "hey!" "$6$abc") ":17169::::::
  38. nobody:!:0::::::\n"))
  39. (test-begin "accounts")
  40. (test-equal "write-passwd"
  41. %passwd-sample
  42. (call-with-output-string
  43. (lambda (port)
  44. (write-passwd (list (password-entry
  45. (name "root")
  46. (uid 0) (gid 0)
  47. (real-name "Admin")
  48. (directory "/root")
  49. (shell "/bin/sh"))
  50. (password-entry
  51. (name "charlie")
  52. (uid 1000) (gid 998)
  53. (real-name "Charlie")
  54. (directory "/home/charlie")
  55. (shell "/bin/sh")))
  56. port))))
  57. (test-equal "write-passwd with duplicate entry"
  58. %passwd-sample
  59. (call-with-output-string
  60. (lambda (port)
  61. (let ((charlie (password-entry
  62. (name "charlie")
  63. (uid 1000) (gid 998)
  64. (real-name "Charlie")
  65. (directory "/home/charlie")
  66. (shell "/bin/sh"))))
  67. (write-passwd (list (password-entry
  68. (name "root")
  69. (uid 0) (gid 0)
  70. (real-name "Admin")
  71. (directory "/root")
  72. (shell "/bin/sh"))
  73. charlie charlie)
  74. port)))))
  75. (test-equal "read-passwd + write-passwd"
  76. %passwd-sample
  77. (call-with-output-string
  78. (lambda (port)
  79. (write-passwd (call-with-input-string %passwd-sample
  80. read-passwd)
  81. port))))
  82. (test-equal "write-group"
  83. %group-sample
  84. (call-with-output-string
  85. (lambda (port)
  86. (write-group (list (group-entry
  87. (name "root") (gid 0))
  88. (group-entry
  89. (name "wheel") (gid 999)
  90. (members '("alice" "bob")))
  91. (group-entry
  92. (name "hackers") (gid 65000)
  93. (members '("alice" "charlie"))))
  94. port))))
  95. (test-equal "read-group + write-group"
  96. %group-sample
  97. (call-with-output-string
  98. (lambda (port)
  99. (write-group (call-with-input-string %group-sample
  100. read-group)
  101. port))))
  102. (test-equal "write-shadow"
  103. %shadow-sample
  104. (call-with-output-string
  105. (lambda (port)
  106. (write-shadow (list (shadow-entry
  107. (name "root")
  108. (password (crypt "secret" "$6$abc"))
  109. (last-change 17169))
  110. (shadow-entry
  111. (name "charlie")
  112. (password (crypt "hey!" "$6$abc"))
  113. (last-change 17169))
  114. (shadow-entry
  115. (name "nobody")))
  116. port))))
  117. (test-equal "read-shadow + write-shadow"
  118. %shadow-sample
  119. (call-with-output-string
  120. (lambda (port)
  121. (write-shadow (call-with-input-string %shadow-sample
  122. read-shadow)
  123. port))))
  124. (define allocate-groups (@@ (gnu build accounts) allocate-groups))
  125. (define allocate-passwd (@@ (gnu build accounts) allocate-passwd))
  126. (test-equal "allocate-groups"
  127. ;; Allocate GIDs in a stateless fashion.
  128. (list (group-entry (name "s") (gid %system-id-max))
  129. (group-entry (name "x") (gid 900))
  130. (group-entry (name "t") (gid 899))
  131. (group-entry (name "a") (gid %id-min) (password "foo")
  132. (members '("alice" "bob")))
  133. (group-entry (name "b") (gid (+ %id-min 1))
  134. (members '("charlie"))))
  135. (allocate-groups (list (user-group (name "s") (system? #t))
  136. (user-group (name "x") (id 900))
  137. (user-group (name "t") (system? #t))
  138. (user-group (name "a") (password "foo"))
  139. (user-group (name "b")))
  140. (alist->vhash `(("a" . "bob")
  141. ("a" . "alice")
  142. ("b" . "charlie")))))
  143. (test-equal "allocate-groups with requested GIDs"
  144. ;; Make sure the requested GID for "b" is honored.
  145. (list (group-entry (name "a") (gid (+ 1 %id-min)))
  146. (group-entry (name "b") (gid %id-min))
  147. (group-entry (name "c") (gid (+ 2 %id-min))))
  148. (allocate-groups (list (user-group (name "a"))
  149. (user-group (name "b") (id %id-min))
  150. (user-group (name "c")))
  151. vlist-null))
  152. (test-equal "allocate-groups with previous state"
  153. ;; Make sure bits of state are preserved: password, GID, no reuse of
  154. ;; previously-used GIDs.
  155. (list (group-entry (name "s") (gid (- %system-id-max 1)))
  156. (group-entry (name "t") (gid (- %system-id-max 2)))
  157. (group-entry (name "a") (gid 30000) (password #f)
  158. (members '("alice" "bob")))
  159. (group-entry (name "b") (gid 30001) (password "bar")
  160. (members '("charlie"))))
  161. (allocate-groups (list (user-group (name "s") (system? #t))
  162. (user-group (name "t") (system? #t))
  163. (user-group (name "a") (password "foo"))
  164. (user-group (name "b")))
  165. (alist->vhash `(("a" . "bob")
  166. ("a" . "alice")
  167. ("b" . "charlie")))
  168. (list (group-entry (name "a") (gid 30000))
  169. (group-entry (name "b") (gid 30001)
  170. (password "bar"))
  171. (group-entry (name "removed")
  172. (gid %system-id-max)))))
  173. (test-equal "allocate-groups with previous state, looping"
  174. ;; Check that allocation starts after the highest previously-used GID, and
  175. ;; loops back to the lowest GID.
  176. (list (group-entry (name "a") (gid (- %id-max 1)))
  177. (group-entry (name "b") (gid %id-min))
  178. (group-entry (name "c") (gid (+ 1 %id-min))))
  179. (allocate-groups (list (user-group (name "a"))
  180. (user-group (name "b"))
  181. (user-group (name "c")))
  182. vlist-null
  183. (list (group-entry (name "d")
  184. (gid (- %id-max 2))))))
  185. (test-equal "allocate-passwd"
  186. ;; Allocate UIDs in a stateless fashion.
  187. (list (password-entry (name "alice") (uid %id-min) (gid 1000)
  188. (real-name "Alice") (shell "/bin/sh")
  189. (directory "/home/alice"))
  190. (password-entry (name "bob") (uid (+ 1 %id-min)) (gid 1001)
  191. (real-name "Bob") (shell "/bin/gash")
  192. (directory "/home/bob"))
  193. (password-entry (name "sshd") (uid %system-id-max) (gid 500)
  194. (real-name "sshd") (shell "/nologin")
  195. (directory "/var/empty"))
  196. (password-entry (name "guix") (uid 30000) (gid 499)
  197. (real-name "Guix") (shell "/nologin")
  198. (directory "/var/empty")))
  199. (allocate-passwd (list (user-account (name "alice")
  200. (comment "Alice")
  201. (shell "/bin/sh")
  202. (group "users"))
  203. (user-account (name "bob")
  204. (comment "Bob")
  205. (shell "/bin/gash")
  206. (group "wheel"))
  207. (user-account (name "sshd") (system? #t)
  208. (comment "sshd")
  209. (home-directory "/var/empty")
  210. (shell "/nologin")
  211. (group "sshd"))
  212. (user-account (name "guix") (system? #t)
  213. (comment "Guix")
  214. (home-directory "/var/empty")
  215. (shell "/nologin")
  216. (group "guix")
  217. (uid 30000)))
  218. (list (group-entry (name "users") (gid 1000))
  219. (group-entry (name "wheel") (gid 1001))
  220. (group-entry (name "sshd") (gid 500))
  221. (group-entry (name "guix") (gid 499)))))
  222. (test-equal "allocate-passwd with previous state"
  223. ;; Make sure bits of state are preserved: UID, no reuse of previously-used
  224. ;; UIDs, and shell.
  225. (list (password-entry (name "alice") (uid 1234) (gid 1000)
  226. (real-name "Alice Smith") (shell "/bin/sh")
  227. (directory "/home/alice"))
  228. (password-entry (name "charlie") (uid 1236) (gid 1000)
  229. (real-name "Charlie") (shell "/bin/sh")
  230. (directory "/home/charlie")))
  231. (allocate-passwd (list (user-account (name "alice")
  232. (comment "Alice")
  233. (shell "/bin/sh") ;honored
  234. (group "users"))
  235. (user-account (name "charlie")
  236. (comment "Charlie")
  237. (shell "/bin/sh")
  238. (group "users")))
  239. (list (group-entry (name "users") (gid 1000)))
  240. (list (password-entry (name "alice") (uid 1234) (gid 9999)
  241. (real-name "Alice Smith")
  242. (shell "/gnu/.../bin/gash") ;ignored
  243. (directory "/home/alice"))
  244. (password-entry (name "bob") (uid 1235) (gid 1001)
  245. (real-name "Bob") (shell "/bin/sh")
  246. (directory "/home/bob")))))
  247. (test-equal "user+group-databases"
  248. ;; The whole shebang.
  249. (list (list (group-entry (name "a") (gid %id-min)
  250. (members '("bob")))
  251. (group-entry (name "b") (gid (+ 1 %id-min))
  252. (members '("alice")))
  253. (group-entry (name "s") (gid %system-id-max)))
  254. (list (password-entry (name "alice") (real-name "Alice")
  255. (uid %id-min) (gid %id-min)
  256. (directory "/a"))
  257. (password-entry (name "bob") (real-name "Bob")
  258. (uid (+ 1 %id-min)) (gid (+ 1 %id-min))
  259. (directory "/b"))
  260. (password-entry (name "nobody")
  261. (uid 65534) (gid %system-id-max)
  262. (directory "/var/empty")))
  263. (list (shadow-entry (name "alice") (last-change 100)
  264. (password (crypt "initial pass" "$6$")))
  265. (shadow-entry (name "bob") (last-change 50)
  266. (password (crypt "foo" "$6$")))
  267. (shadow-entry (name "nobody") (last-change 100))))
  268. (call-with-values
  269. (lambda ()
  270. (user+group-databases (list (user-account
  271. (name "alice")
  272. (comment "Alice")
  273. (home-directory "/a")
  274. (group "a")
  275. (supplementary-groups '("b"))
  276. (password (crypt "initial pass" "$6$")))
  277. (user-account
  278. (name "bob")
  279. (comment "Bob")
  280. (home-directory "/b")
  281. (group "b")
  282. (supplementary-groups '("a")))
  283. (user-account
  284. (name "nobody")
  285. (group "s")
  286. (uid 65534)
  287. (home-directory "/var/empty")))
  288. (list (user-group (name "a"))
  289. (user-group (name "b"))
  290. (user-group (name "s") (system? #t)))
  291. #:current-passwd '()
  292. #:current-shadow
  293. (list (shadow-entry (name "bob")
  294. (password (crypt "foo" "$6$"))
  295. (last-change 50)))
  296. #:current-groups '()
  297. #:current-time
  298. (lambda (type)
  299. (make-time type 0 (* 24 3600 100)))))
  300. list))
  301. (test-end "accounts")