auth.lua 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. -- Licensed to the public under the Apache License 2.0.
  2. local rad2 = luci.controller.radicale2
  3. local fs = require("nixio.fs")
  4. local util = require("luci.util")
  5. local m = Map("radicale2", translate("Radicale 2.x"),
  6. translate("A lightweight CalDAV/CardDAV server"))
  7. local s = m:section(NamedSection, "auth", "section", translate("Authentication"))
  8. s.addremove = true
  9. s.anonymous = false
  10. local at = s:option(ListValue, "type", translate("Authentication Type"))
  11. at:value("", translate("Default (htpasswd file from users below)"))
  12. at:value("htpasswd", translate("htpasswd file (manually populated)"))
  13. at:value("none", translate("No authentication"))
  14. at:value("remote_user", translate("REMOTE_USER from web server"))
  15. at:value("http_x_remote_user", translate("X-Remote-User from web server"))
  16. at.default = ""
  17. at.rmempty = true
  18. local o = s:option(Value, "htpasswd_filename", translate("Filename"), translate("htpasswd-formatted file filename"))
  19. o:depends("type", "htpasswd")
  20. o.rmempty = true
  21. o.placeholder = "/etc/radicale2/users"
  22. o.default = ""
  23. local hte = s:option(ListValue, "htpasswd_encryption", translate("Encryption"), translate("Password encryption method"))
  24. hte:depends("type", "htpasswd")
  25. hte:depends("type", "")
  26. hte:value("plain", translate("Plaintext"))
  27. hte:value("sha1", translate("SHA1"))
  28. hte:value("ssha", translate("SSHA"))
  29. hte:value("crypt", translate("crypt"))
  30. if rad2.pymodexists("passlib") then
  31. hte:value("md5", translate("md5"))
  32. if rad2.pymodexists("bcrypt") then
  33. hte:value("bcrypt", translate("bcrypt"))
  34. end
  35. end
  36. hte.default = "plain"
  37. hte.rmempty = true
  38. if not rad2.pymodexists("bcrypt") then
  39. o = s:option(DummyValue, "nobcrypt", translate("Insecure hashes"), translate("Install python3-passlib and python3-bcrypt to enable a secure hash"))
  40. else
  41. o = s:option(DummyValue, "nobcrypt", translate("Insecure hashes"), translate("Select bcrypt above to enable a secure hash"))
  42. o:depends("htpasswd_encrypt","")
  43. o:depends("htpasswd_encrypt","plain")
  44. o:depends("htpasswd_encrypt","sha1")
  45. o:depends("htpasswd_encrypt","ssha")
  46. o:depends("htpasswd_encrypt","crypt")
  47. o:depends("htpasswd_encrypt","md5")
  48. end
  49. o = s:option(Value, "delay", translate("Retry Delay"), translate("Required time between a failed authentication attempt and trying again"))
  50. o.rmempty = true
  51. o.default = 1
  52. o.datatype = "uinteger"
  53. o:depends("type", "")
  54. o:depends("type", "htpasswd")
  55. o:depends("type", "remote_user")
  56. o:depends("type", "http_x_remote_user")
  57. s = m:section(TypedSection, "user", translate("User"), translate("Users and Passwords"))
  58. s.addremove = true
  59. s.anonymous = true
  60. o = s:option(Value, "name", translate("Username"))
  61. o.rmempty = true
  62. o.placeholder = "johndoe"
  63. if rad2.pymodexists("passlib") then
  64. local plainpass = s:option(Value, "plain_pass", translate("Plaintext Password"))
  65. plainpass.placeholder = "Example password"
  66. plainpass.password = true
  67. local ppconfirm = s:option(Value, "plain_pass_confirm", translate("Confirm Plaintext Password"))
  68. ppconfirm.placeholder = "Example password"
  69. ppconfirm.password = true
  70. plainpass.cfgvalue = function(self, section)
  71. return self:formvalue(section)
  72. end
  73. plainpass.write = function(self, section)
  74. return true
  75. end
  76. ppconfirm.cfgvalue = plainpass.cfgvalue
  77. ppconfirm.write = plainpass.write
  78. plainpass.validate = function(self, value, section)
  79. if self:cfgvalue(section) ~= ppconfirm:cfgvalue(section) then
  80. return nil, translate("Password and confirmation do not match")
  81. end
  82. return AbstractValue.validate(self, value, section)
  83. end
  84. ppconfirm.validate = function(self, value, section)
  85. if self:cfgvalue(section) ~= plainpass:cfgvalue(section) then
  86. return nil, translate("Password and confirmation do not match")
  87. end
  88. return AbstractValue.validate(self, value, section)
  89. end
  90. local pass = s:option(Value, "password", translate("Encrypted Password"), translate("If 'Plaintext Password' filled and matches 'Confirm Plaintext Password' then this field becomes of hash of that password, otherwise this field remains the existing hash (you can also put your own hash value for the type of hash listed above)."))
  91. pass.password = true
  92. pass.rmempty = false
  93. function encpass(self, section)
  94. local plainvalue = plainpass:cfgvalue(section)
  95. local pvc = ppconfirm:cfgvalue(section)
  96. local encvalue, err
  97. if not plainvalue or not pvc or plainvalue == "" or pvc == "" or plainvalue ~= pvc then
  98. return nil
  99. end
  100. local enctype = hte:formvalue("auth")
  101. if not enctype then
  102. enctype = hte:cfgvalue("auth")
  103. end
  104. if not enctype or enctype == "" or enctype == "plain" then
  105. return plainvalue
  106. end
  107. encvalue, err = util.ubus("rad2-enc", "encrypt", { type = enctype, plainpass = plainvalue })
  108. if not encvalue then
  109. return nil
  110. end
  111. return encvalue and encvalue.encrypted_password
  112. end
  113. pass.cfgvalue = function(self, section)
  114. if not plainpass:formvalue(section) then
  115. return Value.cfgvalue(self, section)
  116. else
  117. return Value.formvalue(self, section)
  118. end
  119. end
  120. pass.formvalue = function(self, section)
  121. if not plainpass:formvalue(section) then
  122. return Value.formvalue(self, section)
  123. else
  124. return encpass(self, section) or Value.formvalue(self, section)
  125. end
  126. end
  127. else
  128. local pass = s:option(Value, "password", translate("Encrypted Password"), translate("Generate this field using an generator for Apache htpasswd-style authentication files (for the hash format you have chosen above), or install python3-passlib to enable the ability to create the hash by entering the plaintext in a field that will appear on this page if python3-passlib is installed."))
  129. pass.password = true
  130. pass.rmempty = false
  131. end -- python3-passlib installed
  132. -- TODO: Allow configuration of rights file from this page
  133. local s = m:section(NamedSection, "section", "rights", translate("Rights"), translate("User-based ACL Settings"))
  134. s.addremove = true
  135. s.anonymous = false
  136. o = s:option(ListValue, "type", translate("Rights Type"))
  137. o:value("", translate("Default (owner only)"))
  138. o:value("owner_only", translate("RO: None, RW: Owner"))
  139. o:value("authenticated", translate("RO: None, RW: Authenticated Users"))
  140. o:value("owner_write", translate("RO: Authenticated Users, RW: Owner"))
  141. o:value("from_file", translate("Based on settings in 'Rights File'"))
  142. o:value("none", translate("RO: All, RW: All"))
  143. o.default = ""
  144. o.rmempty = true
  145. rights_file = s:option(FileUpload, "file", translate("Rights File"))
  146. rights_file.rmempty = true
  147. rights_file:depends("type", "from_file")
  148. o = s:option(Button, "remove_conf",
  149. translate("Remove configuration for rights file"),
  150. translate("This permanently deletes the rights file and configuration to use same."))
  151. o.inputstyle = "remove"
  152. o:depends("type", "from_file")
  153. function o.write(self, section)
  154. if cert_file:cfgvalue(section) and fs.access(o:cfgvalue(section)) then fs.unlink(rights_file:cfgvalue(section)) end
  155. self.map:del(section, "file")
  156. self.map:del(section, "rights_file")
  157. luci.http.redirect(luci.dispatcher.build_url("admin", "services", "radicale2", "auth"))
  158. end
  159. -- TODO: Allow configuration rights file from this page
  160. return m