66591f0bed73be53c8385e7a05618854190abfa4
[project/luci.git] / applications / luci-app-mosquitto / luasrc / model / cbi / mosquitto.lua
1 --[[
2 LuCI model for mosquitto MQTT broker configuration management
3 Copyright eTactica ehf, 2018
4
5 Licensed under the Apache License, Version 2.0 (the "License");
6 you may not use this file except in compliance with the License.
7 You may obtain a copy of the License at
8
9 http://www.apache.org/licenses/LICENSE-2.0
10
11 ]]--
12
13 local datatypes = require("luci.cbi.datatypes")
14 local _ = luci.i18n.translate
15
16 --- Like a Flag, but with an option to remove/set to default.
17 local function OptionalFlag(section, key, title, description)
18 local o = section:option(ListValue, key, title, description)
19 o.optional = true
20 o:value("", "Default")
21 o:value("1", "Enabled")
22 o:value("0", "Disabled")
23 return o
24 end
25
26 m = Map("mosquitto", _("Mosquitto MQTT Broker"),
27 _([[mosquitto - the <a href='http://www.mosquitto.org'>blood thirsty</a>
28 MQTT messaging broker. Note, only some of the available configuration files
29 are supported at this stage, use the checkbox below to use config generated
30 by this page, or the stock mosquitto configuration file in
31 /etc/mosquitto/mosquitto.conf]]))
32
33 s = m:section(TypedSection, "owrt", "OpenWRT")
34 s.anonymous = true
35 p = s:option(Flag, "use_uci", _("Use this LuCI configuration page"),
36 _([[If checked, mosquitto runs with a config generated
37 from this page. (Or from UCI directly) If unchecked, mosquitto
38 runs with the config in /etc/mosquitto/mosquitto.conf
39 (and this page is ignored)]]))
40
41 s = m:section(TypedSection, "mosquitto", "Mosquitto")
42 s.anonymous = true
43
44 p = s:option(MultiValue, "log_dest", _("Log destination"),
45 _("You can have multiple, but 'none' will override all others"))
46 p:value("stderr", "stderr")
47 p:value("stdout", "stdout")
48 p:value("syslog", "syslog")
49 p:value("topic", "$SYS/broker/log/[severity]")
50 p:value("none", "none")
51
52 OptionalFlag(s, "no_remote_access", _("Disallow remote access to this broker"),
53 _([[Outbound bridges will still work, but this will make the primary listener
54 only available from localhost]]))
55
56 local o
57 o = s:option(Value, "sys_interval", _("Time in seconds between updates of the $SYS tree"), _("Set to zero to disable"))
58 o.datatype = "uinteger"
59 o.optional = true
60
61 OptionalFlag(s, "allow_anonymous", _("Allow anonymous connections"), _("Allow to connect without providing a username and password"))
62 o = s:option(Value, "max_inflight_messages", _("Max Inflight Messages"), _("Limit for message allowed inflight"))
63 o.datatype = "uinteger"
64 o.optional = true
65 o = s:option(Value, "max_queued_messages", _("Max Queued Messages"), _("Limit for message queue when offline"))
66 o.datatype = "uinteger"
67 o.optional = true
68 o = s:option(Value, "max_queued_bytes", _("Max Queued bytes"), _("Limit for message queue when offline, zero to disable)"))
69 o.datatype = "uinteger"
70 o.optional = true
71
72
73 s = m:section(TypedSection, "persistence", _("Persistence"))
74 s.anonymous = true
75 s.addremove = false
76 s:option(Flag, "persistence", _("Persistence enabled"), _("Should persistence to disk be enabled at all")).rmempty = false
77 o = s:option(Value, "client_expiration", _("Client expiration"), _("Remove persistent clients if they haven't reconnected in this period, eg 6h, 3d, 2w"))
78 o.optional = true
79 o:depends("persistence", true)
80 o = OptionalFlag(s, "autosave_on_changes", _("Autosave on changes"), _("Autosave interval applies to change counts instead of time"))
81 o:depends("persistence", true)
82 o = s:option(Value, "autosave_interval", _("Autosave interval"), _("Save persistence file after this many seconds or changes"))
83 o.optional = true
84 o:depends("persistence", true)
85 o = s:option(Value, "file", _("Persistent file name"))
86 o.optional = true
87 o:depends("persistence", true)
88 o = s:option(Value, "location", _("Persistent file path (with trailing/)"), _("Path to persistent file"))
89 o.optional = true
90 o:depends("persistence", true)
91
92 s = m:section(TypedSection, "listener", _("Listeners"), _("You can configure additional listeners here"))
93 s.addremove = true
94 s.anonymous = true
95 s:option(Value, "port", _("Port")).datatype = "port"
96
97 o = s:option(ListValue, "protocol", _("Protocol to use when listening"))
98 o:value("", "Default")
99 o:value("mqtt", _("MQTT"))
100 o:value("websockets", _("WebSockets"))
101
102 s:option(Value, "http_dir", _("http_dir to serve on websockets listeners")).optional = true
103 OptionalFlag(s, "use_username_as_clientid", "use_username_as_clientid")
104 o = s:option(Value, "cafile", _("CA file path"))
105 o.optional = true
106 o.datatype = "file"
107 o = s:option(Value, "capath", _("CA path to search"))
108 o.optional = true
109 o.datatype = "directory"
110 o = s:option(Value, "certfile", _("server certificate file (PEM encoded)"))
111 o.optional = true
112 o.datatype = "file"
113 o = s:option(Value, "keyfile", _("keyfile (PEM encoded)"))
114 o.optional = true
115 o.datatype = "file"
116
117 o = s:option(ListValue, "tls_version", _("TLS Version"),
118 _("Depends on your openssl version, empty to support all"))
119 o.optional = true
120 o:value("", "Default")
121 o:value("tlsv1.1")
122 o:value("tlsv1.2")
123 o:value("tlsv1.3")
124
125 OptionalFlag(s, "require_certificate", _("Require clients to present a certificate"))
126 OptionalFlag(s, "use_identity_as_username", "use_identity_as_username")
127 s:option(Value, "crlfile", _("CRL to use if require_certificate is enabled")).optional = true
128 s:option(Value, "ciphers", _("Ciphers control. Should match 'openssl ciphers' format")).optional = true
129 s:option(Value, "psk_hint", _("PSK Hint to provide to connecting clients")).optional = true
130
131 -- we want to allow multiple bridge sections
132 s = m:section(TypedSection, "bridge", _("Bridges"),
133 _("You can configure multiple bridge connections here"))
134 s.anonymous = true
135 s.addremove = true
136
137 conn = s:option(Value, "connection", _("Connection name"),
138 _("unique name for this bridge configuration"))
139
140 local function validate_address(self, value)
141 local host, port = unpack(luci.util.split(value, ":"))
142 if (datatypes.host(host)) then
143 if port and #port then
144 if not datatypes.port(port) then
145 return nil, _("Please enter a valid port after the :")
146 end
147 end
148 return value
149 end
150 return nil, _("Please enter a hostname or an IP address")
151 end
152
153 addr = s:option(Value, "address", _("address"), _("address[:port] of remote broker"))
154 addr.datatype = "string"
155 addr.validate = validate_address
156
157 -- TODO - make the in/out/both a dropdown/radio or something....
158 topics = s:option(DynamicList, "topic", _("topic"),
159 _("full topic string for mosquitto.conf, eg: 'power/# out 2'"))
160
161 OptionalFlag(s, "cleansession", _("Clean session"))
162 OptionalFlag(s, "notifications", _("notifications"),
163 _("Attempt to notify the local and remote broker of connection status, defaults to $SYS/broker/connections/<clientid>/state"))
164 s:option(Value, "notification_topic", _("Topic to use for local+remote remote for notifications.")).optional = true
165 OptionalFlag(s, "notification_local_only", _("Notifications local only"), _("Bridge connection states should only be published locally"))
166
167 s:option(Value, "remote_clientid", _("Client id to use on remote end of this bridge connection")).optional = true
168 s:option(Value, "local_clientid", _("Client id to use locally. Important when bridging to yourself")).optional = true
169 o = s:option(Value, "keepalive_interval", _("Keepalive interval for this bridge"))
170 o.datatype = "uinteger"
171 o.optional = true
172 o = s:option(ListValue, "start_type", _("How should this bridge be started"))
173 o.optional = true
174 o:value("", "Default")
175 o:value("automatic", _("Automatic, includes restarts"))
176 o:value("lazy", _("Automatic, but stopped when not used"))
177 o:value("once", _("Automatic, but no restarts"))
178 o = s:option(Value, "restart_timeout", _("How long to wait before reconnecting"))
179 o.datatype = "uinteger"
180 o.optional = true
181 o = s:option(Value, "idle_timeout", _("How long to wait before disconnecting"))
182 o.datatype = "uinteger"
183 o.optional = true
184 o = s:option(Value, "threshold", _("How many messages to queue before restarting lazy bridge"))
185 o.datatype = "uinteger"
186 o.optional = true
187
188 OptionalFlag(s, "try_private", "try_private",
189 _("attempt to notify the remote broker that this is a bridge, not all brokers support this."))
190 s:option(Value, "remote_username", _("Remote username")).optional = true
191 o = s:option(Value, "remote_password", _("Remote password"))
192 o.optional = true
193 o.password = true
194
195 s:option(Value, "identity", _("PSK Bridge Identity"), _("Identity for TLS-PSK")).optional = true
196
197 -- no hex validation available in datatypes
198 local function validate_psk_key(self, value)
199 if (value:match("^[a-fA-F0-9]+$")) then
200 return value
201 end
202 return nil, _("Only hex numbers are allowed (use A-F characters and 0-9 digits)")
203 end
204
205 psk_key = s:option(Value, "psk", _("Bridge PSK"), _("Key for TLS-PSK"))
206 psk_key.password = true
207 psk_key.optional = true
208 psk_key.datatype = "string"
209 psk_key.validate = validate_psk_key
210
211 b_tls_version = s:option(ListValue, "tls_version", _("TLS Version"),
212 _("The remote broker must support the same version of TLS for the connection to succeed."))
213 b_tls_version:value("", "Default")
214 b_tls_version:value("tlsv1.1")
215 b_tls_version:value("tlsv1.2")
216 b_tls_version:value("tlsv1.3")
217 b_tls_version.optional = true
218
219 o = s:option(Value, "cafile", _("Path to CA file"))
220 o.optional = true
221 o.datatype = "file"
222 o = s:option(Value, "capath", _("Directory to search for CA files"))
223 o.optional = true
224 o.datatype = "directory"
225 o = s:option(Value, "certfile", _("Path to PEM encoded server certificate file"))
226 o.optional = true
227 o.datatype = "file"
228 o = s:option(Value, "keyfile", _("Path to PEM encoded keyfile"))
229 o.optional = true
230 o.datatype = "file"
231
232 return m