controller-api-model.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794
  1. /*
  2. * A JavaScript class based model for the ZeroTier controller microservice API
  3. * Copyright (C) 2011-2017 ZeroTier, Inc. https://www.zerotier.com/
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. *
  18. * --
  19. *
  20. * You can be released from the requirements of the license by purchasing
  21. * a commercial license. Buying such a license is mandatory as soon as you
  22. * develop commercial closed-source software that incorporates or links
  23. * directly against ZeroTier software without disclosing the source code
  24. * of your own application.
  25. */
  26. 'use strict';
  27. /**
  28. * @param {string} IP with optional /netmask|port section
  29. * @return 4, 6, or 0 if invalid
  30. */
  31. function ipClassify(ip)
  32. {
  33. if ((!ip)||(typeof ip !== 'string'))
  34. return 0;
  35. let ips = ip.split('/');
  36. if (ips.length > 0) {
  37. if (ips.length > 1) {
  38. if (ips[1].length === 0)
  39. return 0;
  40. for(let i=0;i<ips[1].length;++i) {
  41. if ('0123456789'.indexOf(ips[1].charAt(i)) < 0)
  42. return 0;
  43. }
  44. }
  45. if (ips[0].indexOf(':') > 0) {
  46. for(let i=0;i<ips[0].length;++i) {
  47. if ('0123456789abcdefABCDEF:'.indexOf(ips[0].charAt(i)) < 0)
  48. return 0;
  49. }
  50. return 6;
  51. } else if (ips[0].indexOf('.') > 0) {
  52. for(let i=0;i<ips[0].length;++i) {
  53. if ('0123456789.'.indexOf(ips[0].charAt(i)) < 0)
  54. return 0;
  55. }
  56. return 4;
  57. }
  58. }
  59. return 0;
  60. }
  61. exports.ipClassify = ipClassify;
  62. /**
  63. * Make sure a string is lower case hex and optionally left pad
  64. *
  65. * @param x {string} String to format/canonicalize
  66. * @param l {number} Length of desired string or 0/null to not left pad
  67. * @return Padded string
  68. */
  69. function formatZeroTierIdentifier(x,l)
  70. {
  71. x = (x) ? x.toString().toLowerCase() : '';
  72. l = ((typeof l !== 'number')||(l < 0)) ? 0 : l;
  73. let r = '';
  74. for(let i=0;i<x.length;++i) {
  75. let c = x.charAt(i);
  76. if ('0123456789abcdef'.indexOf(c) >= 0) {
  77. r += c;
  78. if (r.length === l)
  79. break;
  80. }
  81. }
  82. while (r.length < l)
  83. r = '0' + r;
  84. return r;
  85. };
  86. exports.formatZeroTierIdentifier = formatZeroTierIdentifier;
  87. /**
  88. * Goes through a rule set array and makes sure it's valid, returning a canonicalized version
  89. *
  90. * @param {array[object]} rules Array of ZeroTier rules
  91. * @return New array of canonicalized rules
  92. * @throws {Error} Rule set is invalid
  93. */
  94. function formatRuleSetArray(rules)
  95. {
  96. let r = [];
  97. if ((rules)&&(Array.isArray(rules))) {
  98. for(let a=0;a<rules.length;++a) {
  99. let rule = rules[a];
  100. if (rule.type) {
  101. let nr = null;
  102. switch(rule.type) {
  103. case 'ACTION_DROP':
  104. case 'ACTION_ACCEPT':
  105. case 'ACTION_BREAK':
  106. break;
  107. case 'ACTION_TEE':
  108. case 'ACTION_WATCH':
  109. nr = { 'type': rule['type'],'not': !!rule['not'],'or': !!rule['or'] };
  110. nr.address = formatZeroTierIdentifier(rule.address,10);
  111. nr.flags = parseInt(rule.flags)||0;
  112. nr['length'] = parseInt(rule['length'])||0;
  113. break;
  114. case 'ACTION_REDIRECT':
  115. nr = { 'type': rule['type'],'not': !!rule['not'],'or': !!rule['or'] };
  116. nr.address = formatZeroTierIdentifier(rule.address,10);
  117. nr.flags = parseInt(rule.flags)||0;
  118. break;
  119. case 'MATCH_SOURCE_ZEROTIER_ADDRESS':
  120. case 'MATCH_DEST_ZEROTIER_ADDRESS':
  121. nr = { 'type': rule['type'],'not': !!rule['not'],'or': !!rule['or'] };
  122. nr.zt = formatZeroTierIdentifier(rule.zt,10);
  123. break;
  124. case 'MATCH_VLAN_ID':
  125. nr = { 'type': rule['type'],'not': !!rule['not'],'or': !!rule['or'] };
  126. nr.vlanId = parseInt(rule.vlanId)||0;
  127. break;
  128. case 'MATCH_VLAN_PCP':
  129. nr = { 'type': rule['type'],'not': !!rule['not'],'or': !!rule['or'] };
  130. nr.vlanPcp = parseInt(rule.vlanPcp)||0;
  131. break;
  132. case 'MATCH_VLAN_DEI':
  133. nr = { 'type': rule['type'],'not': !!rule['not'],'or': !!rule['or'] };
  134. nr.vlanDei = parseInt(rule.vlanDei)||0;
  135. break;
  136. case 'MATCH_MAC_SOURCE':
  137. case 'MATCH_MAC_DEST':
  138. nr = { 'type': rule['type'],'not': !!rule['not'],'or': !!rule['or'] };
  139. nr.mac = formatZeroTierIdentifier(rule.mac,12);
  140. nr.mac = (nr.mac.substr(0,2)+':'+nr.mac.substr(2,2)+':'+nr.mac.substr(4,2)+':'+nr.mac.substr(6,2)+':'+nr.mac.substr(8,2)+':'+nr.mac.substr(10,2));
  141. break;
  142. case 'MATCH_IPV4_SOURCE':
  143. case 'MATCH_IPV4_DEST':
  144. if (ipClassify(rule.ip) !== 4)
  145. continue;
  146. nr = { 'type': rule['type'],'not': !!rule['not'],'or': !!rule['or'] };
  147. nr.ip = rule.ip;
  148. break;
  149. case 'MATCH_IPV6_SOURCE':
  150. case 'MATCH_IPV6_DEST':
  151. if (ipClassify(rule.ip) !== 6)
  152. continue;
  153. nr = { 'type': rule['type'],'not': !!rule['not'],'or': !!rule['or'] };
  154. nr.ip = rule.ip;
  155. break;
  156. case 'MATCH_IP_TOS':
  157. nr = { 'type': rule['type'],'not': !!rule['not'],'or': !!rule['or'] };
  158. nr.mask = parseInt(rule.mask)||0;
  159. nr.start = parseInt(rule.start)||0;
  160. nr.end = parseInt(rule.end)||0;
  161. break;
  162. case 'MATCH_IP_PROTOCOL':
  163. nr = { 'type': rule['type'],'not': !!rule['not'],'or': !!rule['or'] };
  164. nr.ipProtocol = parseInt(rule.ipProtocol)||0;
  165. break;
  166. case 'MATCH_ETHERTYPE':
  167. nr = { 'type': rule['type'],'not': !!rule['not'],'or': !!rule['or'] };
  168. nr.etherType = parseInt(rule.etherType)||0;
  169. break;
  170. case 'MATCH_ICMP':
  171. nr = { 'type': rule['type'],'not': !!rule['not'],'or': !!rule['or'] };
  172. nr.icmpType = parseInt(rule.icmpType)||0;
  173. nr.icmpCode = ('icmpCode' in rule) ? ((rule.icmpCode === null) ? null : (parseInt(rule.icmpCode)||0)) : null;
  174. break;
  175. case 'MATCH_IP_SOURCE_PORT_RANGE':
  176. case 'MATCH_IP_DEST_PORT_RANGE':
  177. nr = { 'type': rule['type'],'not': !!rule['not'],'or': !!rule['or'] };
  178. nr.start = parseInt(rule.start)||0;
  179. nr.end = parseInt(rule.end)||0;
  180. break;
  181. case 'MATCH_CHARACTERISTICS':
  182. nr = { 'type': rule['type'],'not': !!rule['not'],'or': !!rule['or'] };
  183. nr.mask = formatZeroTierIdentifier(rule.mask,16); // hex number, so this will work
  184. break;
  185. case 'MATCH_FRAME_SIZE_RANGE':
  186. nr = { 'type': rule['type'],'not': !!rule['not'],'or': !!rule['or'] };
  187. nr.start = parseInt(rule.start)||0;
  188. nr.end = parseInt(rule.end)||0;
  189. break;
  190. case 'MATCH_RANDOM':
  191. nr = { 'type': rule['type'],'not': !!rule['not'],'or': !!rule['or'] };
  192. nr.probability = parseInt(rule.probability)||0;
  193. break;
  194. case 'MATCH_TAGS_DIFFERENCE':
  195. case 'MATCH_TAGS_BITWISE_AND':
  196. case 'MATCH_TAGS_BITWISE_OR':
  197. case 'MATCH_TAGS_BITWISE_XOR':
  198. case 'MATCH_TAGS_EQUAL':
  199. case 'MATCH_TAG_SENDER':
  200. case 'MATCH_TAG_RECEIVER':
  201. nr = { 'type': rule['type'],'not': !!rule['not'],'or': !!rule['or'] };
  202. nr.id = parseInt(rule.id)||0;
  203. nr.value = parseInt(rule.value)||0;
  204. break;
  205. default:
  206. continue;
  207. }
  208. r.push(nr);
  209. }
  210. }
  211. }
  212. return r;
  213. }
  214. exports.formatRuleSetArray = formatRuleSetArray;
  215. // Internal container classes
  216. class _V4AssignMode
  217. {
  218. get zt() { return (this._zt)||false; }
  219. set zt(b) { this._zt = !!b; }
  220. toJSON()
  221. {
  222. return { zt: this.zt };
  223. }
  224. };
  225. class _v6AssignMode
  226. {
  227. get ['6plane']() { return (this._6plane)||false; }
  228. set ['6plane'](b) { this._6plane = !!b; }
  229. get zt() { return (this._zt)||false; }
  230. set zt(b) { this._zt = !!b; }
  231. get rfc4193() { return (this._rfc4193)||false; }
  232. set rfc4193(b) { this._rfc4193 = !!b; }
  233. toJSON()
  234. {
  235. return {
  236. zt: this.zt,
  237. rfc4193: this.rfc4193,
  238. '6plane': this['6plane']
  239. };
  240. }
  241. }
  242. class Network
  243. {
  244. constructor(obj)
  245. {
  246. this.clear();
  247. this.patch(obj);
  248. }
  249. get objtype() { return 'network'; }
  250. get id() { return this._id; }
  251. set id(x) { return (this._id = formatZeroTierIdentifier(x,16)); }
  252. get nwid() { return this._id; } // legacy
  253. get authTokens() { return this._authTokens; }
  254. set authTokens(at)
  255. {
  256. this._authTokens = {};
  257. if ((at)&&(typeof at === 'object')&&(!Array.isArray(at))) {
  258. for(let k in at) {
  259. let exp = parseInt(at[k])||0;
  260. if (exp >= 0)
  261. this._authTokens[k] = exp;
  262. }
  263. }
  264. return this._authTokens;
  265. }
  266. get capabilities() { return this._capabilities; }
  267. set capabilities(c)
  268. {
  269. let ca = [];
  270. let ids = {};
  271. if ((c)&&(Array.isArray(c))) {
  272. for(let a=0;a<c.length;++a) {
  273. let cap = c[a];
  274. if ((cap)&&(typeof cap === 'object')&&(!Array.isArray(cap))) {
  275. let capId = parseInt(cap.id)||-1;
  276. if ((capId >= 0)&&(capId <= 0xffffffff)&&(!ids[capId])) {
  277. ids[capId] = true;
  278. let capDefault = !!cap['default'];
  279. let capRules = formatRuleSetArray(cap.rules);
  280. ca.push({
  281. id: capId,
  282. 'default': capDefault,
  283. rules: capRules
  284. });
  285. }
  286. }
  287. }
  288. }
  289. ca.sort(function(a,b) {
  290. a = a.id;
  291. b = b.id;
  292. return ((a > b) ? 1 : ((a < b) ? -1 : 0));
  293. });
  294. this._capabilities = ca;
  295. return ca;
  296. }
  297. get ipAssignmentPools() { return this._ipAssignmentPools; }
  298. set ipAssignmentPools(ipp)
  299. {
  300. let pa = [];
  301. let ranges = {};
  302. if ((ipp)&&(Array.isArray(ipp))) {
  303. for(let a=0;a<ipp.length;++a) {
  304. let range = ipp[a];
  305. if ((range)&&(typeof range === 'object')&&(!Array.isArray(range))) {
  306. let start = range.ipRangeStart;
  307. let end = range.ipRangeEnd;
  308. if ((start)&&(end)) {
  309. let stype = ipClassify(start);
  310. if ((stype > 0)&&(stype === ipClassify(end))&&(!ranges[start+'_'+end])) {
  311. ranges[start+'_'+end] = true;
  312. pa.push({ ipRangeStart: start, ipRangeEnd: end });
  313. }
  314. }
  315. }
  316. }
  317. }
  318. pa.sort(function(a,b) { return a.ipRangeStart.localeCompare(b.ipRangeStart); });
  319. this._ipAssignmentPools = pa;
  320. return pa;
  321. }
  322. get multicastLimit() { return this._multicastLimit; }
  323. set multicastLimit(n)
  324. {
  325. try {
  326. let nn = parseInt(n)||0;
  327. this._multicastLimit = (nn >= 0) ? nn : 0;
  328. } catch (e) {
  329. this._multicastLimit = 0;
  330. }
  331. return this._multicastLimit;
  332. }
  333. get routes() { return this._routes; }
  334. set routes(r)
  335. {
  336. let ra = [];
  337. let targets = {};
  338. if ((r)&&(Array.isArray(r))) {
  339. for(let a=0;a<r.length;++a) {
  340. let route = r[a];
  341. if ((route)&&(typeof route === 'object')&&(!Array.isArray(route))) {
  342. let routeTarget = route.target;
  343. let routeVia = route.via||null;
  344. let rtt = ipClassify(routeTarget);
  345. if ((rtt > 0)&&((routeVia === null)||(ipClassify(routeVia) === rtt))&&(!targets[routeTarget])) {
  346. targets[routeTarget] = true;
  347. ra.push({ target: routeTarget, via: routeVia });
  348. }
  349. }
  350. }
  351. }
  352. ra.sort(function(a,b) { return a.routeTarget.localeCompare(b.routeTarget); });
  353. this._routes = ra;
  354. return ra;
  355. }
  356. get tags() { return this._tags; }
  357. set tags(t)
  358. {
  359. let ta = [];
  360. if ((t)&&(Array.isArray(t))) {
  361. for(let a=0;a<t.length;++a) {
  362. let tag = t[a];
  363. if ((tag)&&(typeof tag === 'object')&&(!Array.isArray(tag))) {
  364. let tagId = parseInt(tag.id)||-1;
  365. if ((tagId >= 0)||(tagId <= 0xffffffff)) {
  366. let tagDefault = tag.default;
  367. if (typeof tagDefault !== 'number')
  368. tagDefault = parseInt(tagDefault)||null;
  369. if ((tagDefault < 0)||(tagDefault > 0xffffffff))
  370. tagDefault = null;
  371. ta.push({ 'id': tagId, 'default': tagDefault });
  372. }
  373. }
  374. }
  375. }
  376. ta.sort(function(a,b) {
  377. a = a.id;
  378. b = b.id;
  379. return ((a > b) ? 1 : ((a < b) ? -1 : 0));
  380. });
  381. this._tags = ta;
  382. return ta;
  383. }
  384. get v4AssignMode() { return this._v4AssignMode; }
  385. set v4AssignMode(m)
  386. {
  387. if ((m)&&(typeof m === 'object')&&(!Array.isArray(m))) {
  388. this._v4AssignMode.zt = m.zt;
  389. } else if (m === 'zt') { // legacy
  390. this._v4AssignMode.zt = true;
  391. } else {
  392. this._v4AssignMode.zt = false;
  393. }
  394. }
  395. get v6AssignMode() { return this._v6AssignMode; }
  396. set v6AssignMode(m)
  397. {
  398. if ((m)&&(typeof m === 'object')&&(!Array.isArray(m))) {
  399. this._v6AssignMode.zt = m.zt;
  400. this._v6AssignMode.rfc4193 = m.rfc4193;
  401. this._v6AssignMode['6plane'] = m['6plane'];
  402. } else if (typeof m === 'string') { // legacy
  403. let ms = m.split(',');
  404. this._v6AssignMode.zt = false;
  405. this._v6AssignMode.rfc4193 = false;
  406. this._v6AssignMode['6plane'] = false;
  407. for(let i=0;i<ms.length;++i) {
  408. switch(ms[i]) {
  409. case 'zt':
  410. this._v6AssignMode.zt = true;
  411. break;
  412. case 'rfc4193':
  413. this._v6AssignMode.rfc4193 = true;
  414. break;
  415. case '6plane':
  416. this._v6AssignMode['6plane'] = true;
  417. break;
  418. }
  419. }
  420. } else {
  421. this._v6AssignMode.zt = false;
  422. this._v6AssignMode.rfc4193 = false;
  423. this._v6AssignMode['6plane'] = false;
  424. }
  425. }
  426. get rules() { return this._rules; }
  427. set rules(r) { this._rules = formatRuleSetArray(r); }
  428. get enableBroadcast() { return this._enableBroadcast; }
  429. set enableBroadcast(b) { this._enableBroadcast = !!b; }
  430. get mtu() { return this._mtu; }
  431. set mtu(n)
  432. {
  433. let mtu = parseInt(n)||0;
  434. if (mtu <= 1280) mtu = 1280; // minimum as per IPv6 spec
  435. if (mtu >= 10000) mtu = 10000; // maximum as per ZT spec
  436. this._mtu = mtu;
  437. }
  438. get name() { return this._name; }
  439. set name(n)
  440. {
  441. if (typeof n === 'string')
  442. this._name = n;
  443. else if (typeof n === 'number')
  444. this._name = n.toString();
  445. else this._name = '';
  446. }
  447. get private() { return this._private; }
  448. set private(b)
  449. {
  450. // This is really meaningful for security, so make true unless explicitly set to false.
  451. this._private = (b !== false);
  452. }
  453. get activeMemberCount() { return this.__activeMemberCount; }
  454. get authorizedMemberCount() { return this.__authorizedMemberCount; }
  455. get totalMemberCount() { return this.__totalMemberCount; }
  456. get clock() { return this.__clock; }
  457. get creationTime() { return this.__creationTime; }
  458. get revision() { return this.__revision; }
  459. toJSONExcludeControllerGenerated()
  460. {
  461. return {
  462. id: this.id,
  463. objtype: 'network',
  464. nwid: this.nwid,
  465. authTokens: this.authTokens,
  466. capabilities: this.capabilities,
  467. ipAssignmentPools: this.ipAssignmentPools,
  468. multicastLimit: this.multicastLimit,
  469. routes: this.routes,
  470. tags: this.tags,
  471. v4AssignMode: this._v4AssignMode.toJSON(),
  472. v6AssignMode: this._v6AssignMode.toJSON(),
  473. rules: this.rules,
  474. enableBroadcast: this.enableBroadcast,
  475. mtu: this.mtu,
  476. name: this.name,
  477. 'private': this['private']
  478. };
  479. }
  480. toJSON()
  481. {
  482. var j = this.toJSONExcludeControllerGenerated();
  483. j.activeMemberCount = this.activeMemberCount;
  484. j.authorizedMemberCount = this.authorizedMemberCount;
  485. j.totalMemberCount = this.totalMemberCount;
  486. j.clock = this.clock;
  487. j.creationTime = this.creationTime;
  488. j.revision = this.revision;
  489. return j;
  490. }
  491. clear()
  492. {
  493. this._id = '';
  494. this._authTokens = {};
  495. this._capabilities = [];
  496. this._ipAssignmentPools = [];
  497. this._multicastLimit = 32;
  498. this._routes = [];
  499. this._tags = [];
  500. this._v4AssignMode = new _V4AssignMode();
  501. this._v6AssignMode = new _v6AssignMode();
  502. this._rules = [];
  503. this._enableBroadcast = true;
  504. this._mtu = 2800;
  505. this._name = '';
  506. this._private = true;
  507. this.__activeMemberCount = 0;
  508. this.__authorizedMemberCount = 0;
  509. this.__totalMemberCount = 0;
  510. this.__clock = 0;
  511. this.__creationTime = 0;
  512. this.__revision = 0;
  513. }
  514. patch(obj)
  515. {
  516. if (obj instanceof Network)
  517. obj = obj.toJSON();
  518. if ((obj)&&(typeof obj === 'object')&&(!Array.isArray(obj))) {
  519. for(var k in obj) {
  520. try {
  521. switch(k) {
  522. case 'id':
  523. case 'authTokens':
  524. case 'capabilities':
  525. case 'ipAssignmentPools':
  526. case 'multicastLimit':
  527. case 'routes':
  528. case 'tags':
  529. case 'rules':
  530. case 'enableBroadcast':
  531. case 'mtu':
  532. case 'name':
  533. case 'private':
  534. case 'v4AssignMode':
  535. case 'v6AssignMode':
  536. this[k] = obj[k];
  537. break;
  538. case 'activeMemberCount':
  539. case 'authorizedMemberCount':
  540. case 'totalMemberCount':
  541. case 'clock':
  542. case 'creationTime':
  543. case 'revision':
  544. this['__'+k] = parseInt(obj[k])||0;
  545. break;
  546. }
  547. } catch (e) {}
  548. }
  549. }
  550. }
  551. };
  552. exports.Network = Network;
  553. class Member
  554. {
  555. constructor(obj)
  556. {
  557. this.clear();
  558. this.patch(obj);
  559. }
  560. get objtype() { return 'member'; }
  561. get id() { return this._id; }
  562. set id(x) { this._id = formatZeroTierIdentifier((typeof x === 'number') ? x.toString(16) : x,10); }
  563. get address() { return this._id; } // legacy
  564. get nwid() { return this._nwid; }
  565. set nwid(x) { this._nwid = formatZeroTierIdentifier(x,16); }
  566. get controllerId() { return this.nwid.substr(0,10); }
  567. get authorized() { return this._authorized; }
  568. set authorized(b) { this._authorized = (b === true); } // security critical so require explicit set to true
  569. get activeBridge() { return this._activeBridge; }
  570. set activeBridge(b) { this._activeBridge = !!b; }
  571. get capabilities() { return this._capabilities; }
  572. set capabilities(c)
  573. {
  574. let caps = {};
  575. let ca = [];
  576. if ((c)&&(Array.isArray(c))) {
  577. for(let a=0;a<c.length;++a) {
  578. let capId = parseInt(c[a])||0;
  579. if ((capId >= 0)&&(capId <= 0xffffffff)&&(!caps[capId])) {
  580. caps[capId] = true;
  581. ca.push(capId);
  582. }
  583. }
  584. }
  585. ca.sort();
  586. this._capabilities = ca;
  587. return ca;
  588. }
  589. get identity() { return this._identity; }
  590. set identity(istr)
  591. {
  592. if ((istr)&&(typeof istr === 'string'))
  593. this._identity = istr;
  594. else this._identity = null;
  595. }
  596. get ipAssignments() { return this._ipAssignments; }
  597. set ipAssignments(ipa)
  598. {
  599. let ips = {};
  600. if ((ipa)&&(Array.isArray(ipa))) {
  601. for(let a=0;a<ipa.length;++a) {
  602. let ip = ipa[a];
  603. if (ipClassify(ip) > 0)
  604. ips[ip] = true;
  605. }
  606. }
  607. this._ipAssignments = Object.keys(ips);
  608. this._ipAssignments.sort();
  609. return this._ipAssignments;
  610. }
  611. get noAutoAssignIps() { return this._noAutoAssignIps; }
  612. set noAutoAssignIps(b) { this._noAutoAssignIps = !!b; }
  613. get tags() { return this._tags; }
  614. set tags(t)
  615. {
  616. let ta = [];
  617. let pairs = {};
  618. if ((t)&&(Array.isArray(t))) {
  619. for(let a=0;a<t.length;++a) {
  620. let tag = a[t];
  621. if ((tag)&&(Array.isArray(tag))&&(tag.length === 2)) {
  622. let tagId = parseInt(tag[0])||0;
  623. let tagValue = parseInt(tag[1])||0;
  624. let pk = tagId.toString()+'_'+tagValue.toString();
  625. if ((tagId >= 0)&&(tagId <= 0xffffffff)&&(tagValue >= 0)&&(tagValue <= 0xffffffff)&&(!pairs[pk])) {
  626. pairs[pk] = true;
  627. ta.push([ tagId,tagValue ]);
  628. }
  629. }
  630. }
  631. }
  632. ta.sort(function(a,b) {
  633. return ((a[0] < b[0]) ? -1 : ((a[0] > b[0]) ? 1 : 0));
  634. });
  635. this._tags = ta;
  636. return ta;
  637. }
  638. get creationTime() { return this.__creationTime; }
  639. get lastAuthorizedTime() { return this.__lastAuthorizedTime; }
  640. get lastAuthorizedCredentialType() { return this.__lastAuthorizedCredentialType; }
  641. get lastAuthorizedCredential() { return this.__lastAuthorizedCredential; }
  642. get lastDeauthorizedTime() { return this.__lastDeauthorizedTime; }
  643. get physicalAddr() { return this.__physicalAddr; }
  644. get revision() { return this.__revision; }
  645. get vMajor() { return this.__vMajor; }
  646. get vMinor() { return this.__vMinor; }
  647. get vRev() { return this.__vRev; }
  648. get vProto() { return this.__vProto; }
  649. toJSONExcludeControllerGenerated()
  650. {
  651. return {
  652. id: this.id,
  653. nwid: this.nwid,
  654. objtype: 'member',
  655. address: this.id,
  656. authorized: this.authorized,
  657. activeBridge: this.activeBridge,
  658. capabilities: this.capabilities,
  659. identity: this.identity,
  660. ipAssignments: this.ipAssignments,
  661. noAutoAssignIps: this.noAutoAssignIps,
  662. tags: this.tags
  663. };
  664. }
  665. toJSON()
  666. {
  667. let j = this.toJSONExcludeControllerGenerated();
  668. j.creationTime = this.creationTime;
  669. j.lastAuthorizedTime = this.lastAuthorizedTime;
  670. j.lastAuthorizedCredentialType = this.lastAuthorizedCredentialType;
  671. j.lastAuthorizedCredential = this.lastAuthorizedCredential;
  672. j.lastDeauthorizedTime = this.lastDeauthorizedTime;
  673. j.physicalAddr = this.physicalAddr;
  674. j.revision = this.revision;
  675. j.vMajor = this.vMajor;
  676. j.vMinor = this.vMinor;
  677. j.vRev = this.vRev;
  678. j.vProto = this.vProto;
  679. return j;
  680. }
  681. clear()
  682. {
  683. this._id = '';
  684. this._nwid = '';
  685. this._authorized = false;
  686. this._activeBridge = false;
  687. this._capabilities = [];
  688. this._identity = '';
  689. this._ipAssignments = [];
  690. this._noAutoAssignIps = false;
  691. this._tags = [];
  692. this.__creationTime = 0;
  693. this.__lastAuthorizedTime = 0;
  694. this.__lastAuthorizedCredentialType = null;
  695. this.__lastAuthorizedCredential = null;
  696. this.__lastDeauthorizedTime = 0;
  697. this.__physicalAddr = '';
  698. this.__revision = 0;
  699. this.__vMajor = 0;
  700. this.__vMinor = 0;
  701. this.__vRev = 0;
  702. this.__vProto = 0;
  703. }
  704. patch(obj)
  705. {
  706. if (obj instanceof Member)
  707. obj = obj.toJSON();
  708. if ((obj)&&(typeof obj === 'object')&&(!Array.isArray(obj))) {
  709. for(var k in obj) {
  710. try {
  711. switch(k) {
  712. case 'id':
  713. case 'nwid':
  714. case 'authorized':
  715. case 'activeBridge':
  716. case 'capabilities':
  717. case 'identity':
  718. case 'ipAssignments':
  719. case 'noAutoAssignIps':
  720. case 'tags':
  721. this[k] = obj[k];
  722. break;
  723. case 'creationTime':
  724. case 'lastAuthorizedTime':
  725. case 'lastAuthorizedCredentialType':
  726. case 'lastAuthorizedCredential':
  727. case 'lastDeauthorizedTime':
  728. case 'physicalAddr':
  729. case 'revision':
  730. case 'vMajor':
  731. case 'vMinor':
  732. case 'vRev':
  733. case 'vProto':
  734. this['__'+k] = parseInt(obj[k])||0;
  735. break;
  736. }
  737. } catch (e) {}
  738. }
  739. }
  740. }
  741. };
  742. exports.Member = Member;