index.jsx 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577
  1. import React from 'react';
  2. import {GetAuthToken, ClearAuthToken} from '../../../utils/auth.js';
  3. import {API} from '../../../utils/restClient.js';
  4. import {baseURL, endpoints} from '../../../api.js';
  5. import { Redirect } from 'react-router'
  6. import Panel from 'muicss/lib/react/panel';
  7. import Button from 'muicss/lib/react/button';
  8. import Container from 'muicss/lib/react/container';
  9. import Tabs from 'muicss/lib/react/tabs';
  10. import Tab from 'muicss/lib/react/tab';
  11. import Input from 'muicss/lib/react/input';
  12. import Modal from 'react-modal';
  13. import UserEdit from './UserEdit';
  14. import NetworkEdit from './NetworkEdit';
  15. import UserPicker from './UserPicker';
  16. const modalStyle = {
  17. content : {
  18. top : '50%',
  19. left : '50%',
  20. right : 'auto',
  21. bottom : 'auto',
  22. marginRight : '-50%',
  23. transform : 'translate(-50%, -50%)'
  24. }
  25. };
  26. const CREATINGNEWUSER = "CREATINGNEWUSER"
  27. const EDITINGUSER = "EDITINGUSER"
  28. const DEFININGNEWNETWORK = "DEFININGNEWNETWORK"
  29. const EDITINGNETWORK = "EDITINGNETWORK"
  30. const ASSOCIATINGUSER = "ASSOCIATINGUSER"
  31. const DISSOCIATINGUSER = "DISSOCIATINGUSER"
  32. let saveData = (function () {
  33. var a = document.createElement("a");
  34. document.body.appendChild(a);
  35. a.style = "display: none";
  36. return function (data, fileName) {
  37. var json = data,
  38. blob = new Blob([json], {type: "octet/stream"}),
  39. url = window.URL.createObjectURL(blob);
  40. a.href = url;
  41. a.download = fileName;
  42. a.click();
  43. window.URL.revokeObjectURL(url);
  44. };
  45. }());
  46. function dot2num(dot)
  47. {
  48. var d = dot.split('.');
  49. return ((((((+d[0])*256)+(+d[1]))*256)+(+d[2]))*256)+(+d[3]);
  50. }
  51. function num2dot(num)
  52. {
  53. var d = num%256;
  54. for (var i = 3; i > 0; i--)
  55. {
  56. num = Math.floor(num/256);
  57. d = num%256 + '.' + d;
  58. }
  59. return d;
  60. }
  61. export default class AdminDashboard extends React.Component {
  62. constructor(props) {
  63. super(props)
  64. this.state = {
  65. logout: false,
  66. users: [],
  67. networks: [],
  68. vpn: {},
  69. modal: "",
  70. self: {},
  71. editedUser: {},
  72. genConfigUsername: "",
  73. assocNetworkName: "",
  74. dissocNetworkName: "",
  75. possibleAssocUsers: [],
  76. possibleDissocUsers: [],
  77. }
  78. let authToken = GetAuthToken()
  79. this.api = new API(baseURL, endpoints, authToken)
  80. }
  81. componentWillMount() {
  82. this.refresh()
  83. }
  84. refresh() {
  85. this.getAuthStatus()
  86. this.getUserList()
  87. this.getNetworkList()
  88. this.getVPNStatus()
  89. }
  90. getAuthStatus() {
  91. this.api.call("authStatus", {}, true, this.handleGetAuthStatusSuccess.bind(this), this.handleGetAuthStatusFailure.bind(this))
  92. }
  93. getUserList() {
  94. this.api.call("userList", {}, true, this.handleGetUsersSuccess.bind(this), this.handleGetUsersFailure.bind(this))
  95. }
  96. getNetworkList() {
  97. this.api.call("networkList", {}, true, this.handleGetNetworksSuccess.bind(this), this.handleGetNetworksFailure.bind(this))
  98. }
  99. getVPNStatus() {
  100. this.api.call("vpnStatus", {}, true, this.handleGetVPNStatusSuccess.bind(this), this.handleGetVPNStatusFailure.bind(this))
  101. }
  102. handleTabChange (i, value, tab, ev) {
  103. this.refresh()
  104. }
  105. handleGetUsersSuccess(res) {
  106. this.setState({users: res.data.users})
  107. }
  108. handleGetUsersFailure(error) {
  109. if ('response' in error && error.response.status == 401) {
  110. this.handleAuthFailure(error)
  111. }
  112. this.setState({users: []})
  113. }
  114. handleGetNetworksSuccess(res) {
  115. this.setState({networks: res.data.networks})
  116. }
  117. handleGetNetworksFailure(error) {
  118. console.log(error)
  119. this.setState({networks: []})
  120. if (error.response.status == 401) {
  121. this.handleAuthFailure(error)
  122. }
  123. }
  124. handleGetVPNStatusSuccess(res) {
  125. this.setState({vpn: res.data})
  126. }
  127. handleGetVPNStatusFailure(error) {
  128. console.log(error)
  129. this.setState({vpn: {}})
  130. if (error.response.status == 401) {
  131. this.handleAuthFailure(error)
  132. }
  133. }
  134. handleGetAuthStatusSuccess(res) {
  135. this.setState({self: res.data.user})
  136. }
  137. handleGetAuthStatusFailure(error) {
  138. console.log(error)
  139. this.setState({self: {}})
  140. if (error.response.status == 401) {
  141. this.handleAuthFailure(error)
  142. }
  143. }
  144. handleAuthFailure(error) {
  145. console.log("auth failure", error)
  146. ClearAuthToken()
  147. }
  148. handleCreateNewUser(e) {
  149. this.setState({modal: CREATINGNEWUSER})
  150. }
  151. handleDefineNewNetwork(e) {
  152. this.setState({modal: DEFININGNEWNETWORK})
  153. }
  154. handleUpdateUser(username, e) {
  155. for (let i in this.state.users) {
  156. if (this.state.users[i].username === username) {
  157. this.setState({modal: EDITINGUSER, editedUser: this.state.users[i]})
  158. return
  159. }
  160. }
  161. }
  162. handleCloseModal() {
  163. this.setState({modal: ""})
  164. }
  165. handleNewUserSave(user) {
  166. let userObj = {
  167. username: user.username,
  168. password: user.password,
  169. no_gw: user.pushGW,
  170. host_id: 0, // handle this host_id problem
  171. is_admin: user.isAdmin,
  172. }
  173. userObj.no_gw = !user.pushGW
  174. userObj.admin_pref = user.isAdmin ? "ADMIN" : "NOADMIN"
  175. userObj.host_id = user.ipAllocationMethod === "static" ? dot2num(user.staticIP) : 0
  176. userObj.static_pref = user.ipAllocationMethod === "static" ? "STATIC" : "NOSTATIC"
  177. this.api.call("userCreate", userObj, true, this.handleCreateUserSuccess.bind(this), this.handleCreateUserFailure.bind(this))
  178. this.setState({modal: ""})
  179. }
  180. handleCreateUserSuccess(res) {
  181. this.refresh()
  182. }
  183. handleCreateUserFailure(error) {
  184. console.log(error)
  185. if (error.response.status == 401) {
  186. this.handleAuthFailure(error)
  187. }
  188. }
  189. handleUpdateUserSave(user) {
  190. let updatedUser = {
  191. password: "",
  192. username: user.username,
  193. gwpref: "NOPREF",
  194. admin_pref: "NOPREFADMIN",
  195. static_pref: "NOPREFSTATIC",
  196. hostid: 0,
  197. }
  198. if (user.password !== "") {
  199. updatedUser.password = user.password
  200. }
  201. updatedUser.gwpref = user.pushGW ? "GW" : "NOGW"
  202. updatedUser.admin_pref = user.isAdmin ? "ADMIN" : "NOADMIN"
  203. updatedUser.host_id = user.ipAllocationMethod === "static" ? dot2num(user.staticIP) : 0
  204. updatedUser.static_pref = user.ipAllocationMethod === "static" ? "STATIC" : "NOSTATIC"
  205. this.api.call("userUpdate", updatedUser, true, this.handleUpdateUserSuccess.bind(this), this.handleUpdateUserFailure.bind(this))
  206. this.setState({modal: ""})
  207. }
  208. handleUpdateUserSuccess(res) {
  209. this.refresh()
  210. }
  211. handleUpdateUserFailure(error) {
  212. console.log(error)
  213. if (error.response.status == 401) {
  214. this.handleAuthFailure(error)
  215. }
  216. }
  217. handleRemoveUser(username) {
  218. if (username === this.state.self.username) {
  219. // Don't remove yourself.
  220. return
  221. }
  222. this.api.call("userDelete", {username: username}, true, this.handleRemoveUserSuccess.bind(this), this.handleRemoveUserFailure.bind(this))
  223. }
  224. handleRemoveUserSuccess(res) {
  225. this.refresh()
  226. }
  227. handleRemoveUserFailure(error) {
  228. console.log(error)
  229. if (error.response.status == 401) {
  230. this.handleAuthFailure(error)
  231. }
  232. }
  233. handleDownloadProfileClick(username, e) {
  234. this.setState({genConfigUsername: username})
  235. this.api.call("genConfig", {username: username}, true, this.handleDownloadProfileSuccess.bind(this), this.handleDownloadProfileFailure.bind(this))
  236. }
  237. handleDownloadProfileSuccess(res) {
  238. let blob = res.data.client_config
  239. saveData(blob, this.state.genConfigUsername+".ovpn")
  240. }
  241. handleDownloadProfileFailure(error) {
  242. if ('response' in error && error.response.status == 401) {
  243. this.handleAuthFailure(error)
  244. }
  245. console.log(error)
  246. }
  247. handleDefineNetworkSave(network) {
  248. this.api.call("netDefine", network, true, this.handleDefineNetworkSuccess.bind(this), this.handleDefineNetworkFailure.bind(this))
  249. this.setState({modal: ""})
  250. }
  251. handleDefineNetworkSuccess(res) {
  252. this.refresh()
  253. }
  254. handleDefineNetworkFailure(error) {
  255. if ('response' in error && error.response.status == 401) {
  256. this.handleAuthFailure(error)
  257. }
  258. console.log(error)
  259. }
  260. handleUndefineNetwork(name) {
  261. this.api.call("netUndefine", {name: name}, true, this.handleUndefineNetworkSuccess.bind(this), this.handleUndefineNetworkFailure.bind(this))
  262. }
  263. handleUndefineNetworkSuccess(res) {
  264. this.refresh()
  265. }
  266. handleUndefineNetworkFailure(error) {
  267. if ('response' in error && error.response.status == 401) {
  268. this.handleAuthFailure(error)
  269. }
  270. console.log(error)
  271. }
  272. handleAssociateUser(networkName) {
  273. let assocUsers = []
  274. let network
  275. for(let i in this.state.networks) {
  276. if (this.state.networks[i].name === networkName) {
  277. network = this.state.networks[i]
  278. break
  279. }
  280. }
  281. for(let i in this.state.users) {
  282. let found = false
  283. for(let j in network.associated_usernames) {
  284. if(this.state.users[i].username === network.associated_usernames[j]) {
  285. found = true
  286. }
  287. }
  288. if (!found) {
  289. assocUsers.push(this.state.users[i].username)
  290. }
  291. }
  292. this.setState({modal: ASSOCIATINGUSER, assocNetworkName: networkName, possibleAssocUsers: assocUsers})
  293. }
  294. handleDissociateUser(networkName) {
  295. let dissocUsers = []
  296. let network
  297. for(let i in this.state.networks) {
  298. if (this.state.networks[i].name === networkName) {
  299. network = this.state.networks[i]
  300. break
  301. }
  302. }
  303. for(let i in this.state.users) {
  304. let found = false
  305. for(let j in network.associated_usernames) {
  306. if(this.state.users[i].username === network.associated_usernames[j]) {
  307. found = true
  308. }
  309. }
  310. if (found) {
  311. dissocUsers.push(this.state.users[i].username)
  312. }
  313. }
  314. this.setState({modal: DISSOCIATINGUSER, dissocNetworkName: networkName, possibleDissocUsers: dissocUsers})
  315. }
  316. handleAssociateUserSave(username) {
  317. //call
  318. //refresh
  319. this.api.call("netAssociate", {name: this.state.assocNetworkName, username : username}, true, this.handleAssociateUserSuccess.bind(this), this.handleAssociateUserFailure.bind(this))
  320. this.setState({modal: ""})
  321. }
  322. handleDissociateUserSave(username) {
  323. this.api.call("netDissociate", {name: this.state.dissocNetworkName, username : username}, true, this.handleDissociateUserSuccess.bind(this), this.handleDissociateUserFailure.bind(this))
  324. this.setState({modal: ""})
  325. }
  326. handleAssociateUserSuccess(res) {
  327. this.refresh()
  328. }
  329. handleAssociateUserFailure(error) {
  330. if ('response' in error && error.response.status == 401) {
  331. this.handleAuthFailure(error)
  332. }
  333. console.log(error)
  334. }
  335. handleDissociateUserSuccess(res) {
  336. this.refresh()
  337. }
  338. handleDissociateUserFailure(error) {
  339. if ('response' in error && error.response.status == 401) {
  340. this.handleAuthFailure(error)
  341. }
  342. console.log(error)
  343. }
  344. handleRestartVPNServer() {
  345. this.api.call("vpnRestart", {}, true, function() {
  346. this.refresh()
  347. }.bind(this), function() {
  348. if ('response' in error && error.response.status == 401) {
  349. this.handleAuthFailure(error)
  350. }
  351. console.log(error)
  352. }.bind(this))
  353. }
  354. handleLogout() {
  355. ClearAuthToken()
  356. this.setState({logout: true})
  357. }
  358. render() {
  359. if (this.state.logout) {
  360. return <Redirect to="/login" />
  361. }
  362. let users = []
  363. for (var i = 0; i < this.state.users.length; i++) {
  364. let isStatic = ""
  365. if (this.state.users[i].host_id != 0) {
  366. isStatic = (<small><span className="glyphicon glyphicon glyphicon-pushpin" data-toggle="tooltip" title="Statically Allocated IP"></span></small>)
  367. }
  368. let isAdmin
  369. if (this.state.users[i].is_admin) {
  370. isAdmin = (<small><span className="glyphicon glyphicon-asterisk" data-toggle="tooltip" title="Admin"></span></small>)
  371. }
  372. let noGW = (<span className="glyphicon glyphicon-remove" data-toggle="tooltip" title="False"></span>)
  373. if (!this.state.users[i].no_gw) {
  374. noGW = (<span className="glyphicon glyphicon-ok" data-toggle="tooltip" title="True"></span>)
  375. }
  376. users.push(
  377. <tr key={"user" + i}>
  378. <td>{i+1}</td>
  379. <td>{this.state.users[i].username} {isAdmin}</td>
  380. <td>{this.state.users[i].ip_net} {isStatic}</td>
  381. <td>{this.state.users[i].created_at}</td>
  382. <td className="mui--align-middle">{noGW}</td>
  383. <td>
  384. <a style={{"padding-left": "5px"}}><span className="glyphicon glyphicon-floppy-save" data-toggle="tooltip" title="Download VPN Profile" onClick={this.handleDownloadProfileClick.bind(this, this.state.users[i].username)}></span></a>
  385. <a style={{"padding-left": "5px"}}><span className="glyphicon glyphicon-edit" data-toggle="tooltip" title="Update User" onClick={this.handleUpdateUser.bind(this, this.state.users[i].username)}></span></a>
  386. <a style={{"padding-left": "5px"}}><span className="glyphicon glyphicon-remove" data-toggle="tooltip" title="Delete User" onClick={this.handleRemoveUser.bind(this, this.state.users[i].username)}></span></a>
  387. </td>
  388. </tr>
  389. )
  390. }
  391. let networks = []
  392. for (var i = 0; i < this.state.networks.length; i++) {
  393. let via
  394. if (this.state.networks[i].type == "ROUTE") {
  395. via = "via vpn-server"
  396. if (this.state.networks[i].via && this.state.networks[i].via != "") {
  397. via = "via " + this.state.networks[i].via
  398. }
  399. }
  400. networks.push(
  401. <tr key={"network" + i}>
  402. <td>{i+1}</td>
  403. <td>{this.state.networks[i].name}</td>
  404. <td>{this.state.networks[i].cidr} {via}</td>
  405. <td>{this.state.networks[i].type}</td>
  406. <td>{this.state.networks[i].created_at}</td>
  407. <td>{this.state.networks[i].associated_usernames.join(', ')}</td>
  408. <td>
  409. <a style={{"padding-left": "5px"}}><span className="glyphicon glyphicon-plus-sign" data-toggle="tooltip" onClick={this.handleAssociateUser.bind(this, this.state.networks[i].name)} title="Associate User"></span></a>
  410. <a style={{"padding-left": "5px"}}><span className="glyphicon glyphicon-minus-sign" data-toggle="tooltip" onClick={this.handleDissociateUser.bind(this, this.state.networks[i].name)} title="Dissociate User"></span></a>
  411. <a style={{"padding-left": "5px"}}><span className="glyphicon glyphicon-remove" data-toggle="tooltip" onClick={this.handleUndefineNetwork.bind(this, this.state.networks[i].name)} title="Undefine Network"></span></a>
  412. </td>
  413. </tr>
  414. )
  415. }
  416. return (
  417. <Container style={{"padding-top":"5%"}}>
  418. <Panel>
  419. <Button className="mui--pull-right" color="primary" onClick={this.handleLogout.bind(this)}>Logout</Button>
  420. <Container>
  421. <Modal isOpen={this.state.modal === CREATINGNEWUSER} contentLabel="Modal" style={modalStyle}>
  422. <UserEdit title="Create New User" onCancel={this.handleCloseModal.bind(this)} onSave={this.handleNewUserSave.bind(this)} isUsernameDisabled={false}/>
  423. </Modal>
  424. <Modal isOpen={this.state.modal === EDITINGUSER} contentLabel="Modal" style={modalStyle}>
  425. <UserEdit title="Update User" onCancel={this.handleCloseModal.bind(this)} onSave={this.handleUpdateUserSave.bind(this)} isUsernameDisabled={true} username={this.state.editedUser.username} isAdmin={this.state.editedUser.is_admin} pushGW={(!this.state.editedUser.no_gw)} ipAllocationMethod={this.state.editedUser.host_id == 0 ? "dynamic": "static"} staticIP={this.state.editedUser.host_id == 0 ? "": num2dot(this.state.editedUser.host_id)}/>
  426. </Modal>
  427. <Modal isOpen={this.state.modal === DEFININGNEWNETWORK} contentLabel="Modal" style={modalStyle}>
  428. <NetworkEdit title="New Network" onCancel={this.handleCloseModal.bind(this)} onSave={this.handleDefineNetworkSave.bind(this)}/>
  429. </Modal>
  430. <Modal isOpen={this.state.modal === ASSOCIATINGUSER} contentLabel="Modal" style={modalStyle}>
  431. <UserPicker title="Associate User" onCancel={this.handleCloseModal.bind(this)} onSave={this.handleAssociateUserSave.bind(this)} userNames={this.state.possibleAssocUsers} />
  432. </Modal>
  433. <Modal isOpen={this.state.modal === DISSOCIATINGUSER} contentLabel="Modal" style={modalStyle}>
  434. <UserPicker title="Dissociate User" onCancel={this.handleCloseModal.bind(this)} onSave={this.handleDissociateUserSave.bind(this)} userNames={this.state.possibleDissocUsers} />
  435. </Modal>
  436. <div>
  437. <Tabs onChange={this.handleTabChange.bind(this)} defaultSelectedIndex={0}>
  438. <Tab value="users" label="Users">
  439. <Button className="mui--pull-right" color="primary" onClick={this.handleCreateNewUser.bind(this)}>+ Create User</Button>
  440. <table className="mui-table mui-table--bordered mui--text-justify">
  441. <thead>
  442. <tr>
  443. <th>#</th>
  444. <th>USERNAME</th>
  445. <th>IP</th>
  446. <th>CREATED AT</th>
  447. <th>PUSH GATEWAY</th>
  448. <th>ACTIONS</th>
  449. </tr>
  450. </thead>
  451. <tbody>
  452. {users}
  453. </tbody>
  454. </table>
  455. </Tab>
  456. <Tab value="networks" label="Networks">
  457. <Button className="mui--pull-right" color="primary" onClick={this.handleDefineNewNetwork.bind(this)}>+ Define Net</Button>
  458. <table className="mui-table mui-table--bordered mui--text-justify">
  459. <thead>
  460. <tr>
  461. <th>#</th>
  462. <th>NAME</th>
  463. <th>CIDR</th>
  464. <th>TYPE</th>
  465. <th>CREATED AT</th>
  466. <th>ASSOC USERS</th>
  467. <th>ACTIONS</th>
  468. </tr>
  469. </thead>
  470. <tbody>
  471. {networks}
  472. </tbody>
  473. </table>
  474. </Tab>
  475. <Tab value="vpn" label="VPN">
  476. <Button className="mui--pull-right" color="primary" onClick={this.handleRestartVPNServer.bind(this)}>Restart VPN Server</Button>
  477. <table className="mui-table mui-table--bordered mui--text-justify">
  478. <thead>
  479. <tr>
  480. <th>KEY</th>
  481. <th>VALUE</th>
  482. </tr>
  483. </thead>
  484. <tbody>
  485. <tr><td>Hostname</td> <td>{this.state.vpn.hostname}</td></tr>
  486. <tr><td>Proto</td> <td>{this.state.vpn.proto}</td></tr>
  487. <tr><td>Port</td> <td>{this.state.vpn.port}</td></tr>
  488. <tr><td>Network</td> <td>{this.state.vpn.net} ({this.state.vpn.mask})</td></tr>
  489. <tr><td>DNS</td> <td>{this.state.vpn.dns}</td></tr>
  490. <tr><td>Created At</td><td>{this.state.vpn.created_at}</td></tr>
  491. </tbody>
  492. </table>
  493. </Tab>
  494. </Tabs>
  495. </div>
  496. </Container>
  497. </Panel>
  498. </Container>
  499. )
  500. }
  501. }