PageRenderTime 25ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/src/fireedge/src/server/routes/api/files/functions.js

https://github.com/OpenNebula/one
JavaScript | 440 lines | 324 code | 23 blank | 93 comment | 48 complexity | 53eb0ab019507f982fa9ecfd25923747 MD5 | raw file
  1. /* ------------------------------------------------------------------------- *
  2. * Copyright 2002-2022, OpenNebula Project, OpenNebula Systems *
  3. * *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may *
  5. * not use this file except in compliance with the License. You may obtain *
  6. * a copy of the License at *
  7. * *
  8. * http://www.apache.org/licenses/LICENSE-2.0 *
  9. * *
  10. * Unless required by applicable law or agreed to in writing, software *
  11. * distributed under the License is distributed on an "AS IS" BASIS, *
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
  13. * See the License for the specific language governing permissions and *
  14. * limitations under the License. *
  15. * ------------------------------------------------------------------------- */
  16. const { resolve, extname, parse, sep } = require('path')
  17. const { global } = require('window-or-global')
  18. const { jwtDecode } = require('server/utils/jwt')
  19. const { existsSync, mkdirsSync, moveSync } = require('fs-extra')
  20. const { defaults, httpCodes } = require('server/utils/constants')
  21. const { Actions: ActionUser } = require('server/utils/constants/commands/user')
  22. const {
  23. httpResponse,
  24. checkValidApp,
  25. getFiles,
  26. existsFile,
  27. removeFile,
  28. } = require('server/utils/server')
  29. const { defaultEmptyFunction } = defaults
  30. const { ok, internalServerError, badRequest } = httpCodes
  31. const httpBadRequest = httpResponse(badRequest, '', '')
  32. const groupAdministrator = ['0']
  33. /**
  34. * Check if user is a administrator.
  35. *
  36. * @param {Function} oneConnection - one connection function
  37. * @param {string} id - user ID
  38. * @param {Function} success - callback success
  39. * @param {Function} error - callback error
  40. */
  41. const checkUserAdmin = (
  42. oneConnection = defaultEmptyFunction,
  43. id = '',
  44. success = defaultEmptyFunction,
  45. error = defaultEmptyFunction
  46. ) => {
  47. if (
  48. typeof oneConnection === 'function' &&
  49. id &&
  50. typeof success === 'function' &&
  51. typeof error === 'function'
  52. ) {
  53. oneConnection({
  54. action: ActionUser.USER_INFO,
  55. parameters: [parseInt(id, 10)],
  56. callback: (err, value) => {
  57. if (
  58. !err &&
  59. value &&
  60. value.USER &&
  61. value.USER.GROUPS &&
  62. value.USER.GROUPS.ID
  63. ) {
  64. let admin = false
  65. const groups = Array.isArray(value.USER.GROUPS.ID)
  66. ? value.USER.GROUPS.ID
  67. : [value.USER.GROUPS.ID]
  68. for (const group of groups) {
  69. if (groupAdministrator.includes(group)) {
  70. admin = true
  71. break
  72. }
  73. }
  74. success(admin)
  75. } else {
  76. error(err)
  77. }
  78. },
  79. fillHookResource: false,
  80. })
  81. } else {
  82. error()
  83. }
  84. }
  85. /**
  86. * Parse File path.
  87. *
  88. * @param {string} file - filename
  89. * @returns {Array | undefined} - if user is the file owner
  90. */
  91. const parseFilePath = (file = '') => {
  92. const parsedFile = parse(file)
  93. if (parsedFile && parsedFile.dir) {
  94. return parsedFile.dir.split(sep)
  95. }
  96. }
  97. /**
  98. * Check if file no have owner, but have app.
  99. *
  100. * @param {string} file - filename
  101. * @returns {boolean} - if user is the file owner
  102. */
  103. const validateFileWithoutOwner = (file = '') => {
  104. const parsedFile = parseFilePath(file)
  105. return (
  106. Array.isArray(parsedFile) &&
  107. parsedFile[0] &&
  108. checkValidApp(parsedFile[0]) &&
  109. !parsedFile[1]
  110. )
  111. }
  112. /**
  113. * Check if user is a file owner.
  114. *
  115. * @param {string} file - filename
  116. * @param {number} id - user id
  117. * @returns {boolean} - if user is the file owner
  118. */
  119. const validateFileWithOwner = (file = '', id = '') => {
  120. const parsedFile = parseFilePath(file)
  121. return (
  122. Array.isArray(parsedFile) &&
  123. parsedFile[0] &&
  124. checkValidApp(parsedFile[0]) &&
  125. parsedFile[1] &&
  126. parsedFile[1] === id
  127. )
  128. }
  129. /**
  130. * Upload File.
  131. *
  132. * @param {object} res - response http
  133. * @param {Function} next - express stepper
  134. * @param {string} params - data response http
  135. * @param {object} userData - user of http request
  136. * @param {Function} oneConnection - xmlrpc connection
  137. */
  138. const upload = (
  139. res = {},
  140. next = defaultEmptyFunction,
  141. params = {},
  142. userData = {},
  143. oneConnection = defaultEmptyFunction
  144. ) => {
  145. const { app, files, public: publicFile } = params
  146. const { id, user, password } = userData
  147. if (
  148. global.paths.CPI &&
  149. app &&
  150. checkValidApp(app) &&
  151. files &&
  152. id &&
  153. user &&
  154. password
  155. ) {
  156. const oneConnect = oneConnection(user, password)
  157. checkUserAdmin(
  158. oneConnect,
  159. id,
  160. (admin = false) => {
  161. const pathUserData =
  162. publicFile && admin ? `${app}` : `${app}${sep}${id}`
  163. const pathUser = `${global.paths.CPI}${sep}${pathUserData}`
  164. if (!existsSync(pathUser)) {
  165. mkdirsSync(pathUser)
  166. }
  167. let method = ok
  168. let message = ''
  169. const data = []
  170. for (const file of files) {
  171. if (file && file.originalname && file.path && file.filename) {
  172. const extFile = extname(file.originalname)
  173. try {
  174. const filenameApi = `${pathUserData}${sep}${file.filename}${extFile}`
  175. const filename = `${pathUser}${sep}${file.filename}${extFile}`
  176. moveSync(file.path, filename)
  177. data.push(filenameApi)
  178. } catch (error) {
  179. method = internalServerError
  180. message = error && error.message
  181. break
  182. }
  183. }
  184. }
  185. res.locals.httpCode = httpResponse(
  186. method,
  187. data.length ? data : '',
  188. message
  189. )
  190. next()
  191. },
  192. () => {
  193. res.locals.httpCode = internalServerError
  194. next()
  195. }
  196. )
  197. } else {
  198. res.locals.httpCode = httpBadRequest
  199. next()
  200. }
  201. }
  202. /**
  203. * Get default files for app.
  204. *
  205. * @param {string} app - app
  206. * @param {boolean} multiple - find multiple files
  207. * @param {string} defaultFile - default file
  208. * @returns {Array | string} - file
  209. */
  210. const getDefaultFilesforApps = (
  211. app = '',
  212. multiple = false,
  213. defaultFile = ''
  214. ) => {
  215. let rtn = ''
  216. switch (app) {
  217. case 'sunstone':
  218. if (global.paths.SUNSTONE_IMAGES) {
  219. const path = global.paths.SUNSTONE_IMAGES
  220. if (multiple) {
  221. rtn = getFiles(path, true).map((file) =>
  222. file.replace(`${path}${sep}`, '')
  223. )
  224. } else {
  225. rtn = `${path}${sep}${defaultFile}`
  226. }
  227. }
  228. break
  229. case 'provision':
  230. break
  231. default:
  232. break
  233. }
  234. return rtn
  235. }
  236. /**
  237. * List files by user.
  238. *
  239. * @param {object} res - response http
  240. * @param {Function} next - express stepper
  241. * @param {string} params - data response http
  242. * @param {object} userData - user of http request
  243. */
  244. const list = (
  245. res = {},
  246. next = defaultEmptyFunction,
  247. params = {},
  248. userData = {}
  249. ) => {
  250. const { user, password, id } = userData
  251. const { app } = params
  252. const rtn = httpBadRequest
  253. if (app && checkValidApp(app) && user && password && id) {
  254. const path = `${global.paths.CPI}${sep}`
  255. const userPath = `${app}${sep}${id}`
  256. let data = []
  257. // get defaulf files for app
  258. data = data.concat(getDefaultFilesforApps(app, true))
  259. // find root files
  260. const rootPath = `${path}${app}`
  261. data = data.concat(
  262. getFiles(rootPath, false).map((file) => file.replace(path, ''))
  263. )
  264. // find user files
  265. const pathUser = `${path}${userPath}`
  266. data = data.concat(
  267. getFiles(pathUser, true).map((file) => file.replace(path, ''))
  268. )
  269. res.locals.httpCode = httpResponse(ok, data)
  270. next()
  271. } else {
  272. res.locals.httpCode = rtn
  273. next()
  274. }
  275. }
  276. /**
  277. * Show file.
  278. *
  279. * @param {object} res - response http
  280. * @param {Function} next - express stepper
  281. * @param {string} params - data response http
  282. */
  283. const show = (res = {}, next = defaultEmptyFunction, params = {}) => {
  284. const rtn = httpBadRequest
  285. const { file, token, app } = params
  286. const userData = jwtDecode(token)
  287. if (token && file && app && checkValidApp(app) && userData) {
  288. let pathFile = getDefaultFilesforApps(app, false, file)
  289. if (
  290. validateFileWithOwner(file, userData.iss) ||
  291. validateFileWithoutOwner(file)
  292. ) {
  293. pathFile = `${global.paths.CPI}${sep}${file}`
  294. }
  295. existsFile(
  296. pathFile,
  297. () => {
  298. res.locals.httpCode = httpResponse(ok, '', '', resolve(pathFile))
  299. next()
  300. },
  301. () => {
  302. res.locals.httpCode = httpResponse(internalServerError, '', '')
  303. next()
  304. }
  305. )
  306. } else {
  307. res.locals.httpCode = rtn
  308. next()
  309. }
  310. }
  311. /**
  312. * Delete File.
  313. *
  314. * @param {object} res - response http
  315. * @param {Function} next - express stepper
  316. * @param {string} params - data response http
  317. * @param {object} userData - user of http request
  318. */
  319. const deleteFile = (
  320. res = {},
  321. next = defaultEmptyFunction,
  322. params = {},
  323. userData = {}
  324. ) => {
  325. const { file } = params
  326. const { id } = userData
  327. const rtn = httpBadRequest
  328. if (global.paths.CPI && file && id && validateFileWithOwner(file, id)) {
  329. const pathFile = `${global.paths.CPI}${sep}${file}`
  330. existsFile(
  331. pathFile,
  332. () => {
  333. res.locals.httpCode = httpResponse(
  334. removeFile(pathFile) ? ok : internalServerError,
  335. '',
  336. ''
  337. )
  338. next()
  339. },
  340. () => {
  341. res.locals.httpCode = httpResponse(internalServerError, '', '')
  342. next()
  343. }
  344. )
  345. } else {
  346. res.locals.httpCode = rtn
  347. next()
  348. }
  349. }
  350. /**
  351. * Update File.
  352. *
  353. * @param {object} res - response http
  354. * @param {Function} next - express stepper
  355. * @param {string} params - data response http
  356. * @param {object} userData - user of http request
  357. */
  358. const update = (
  359. res = {},
  360. next = defaultEmptyFunction,
  361. params = {},
  362. userData = {}
  363. ) => {
  364. const rtn = httpBadRequest
  365. const { files, name } = params
  366. const { id } = userData
  367. if (
  368. global.paths.CPI &&
  369. name &&
  370. files &&
  371. id &&
  372. validateFileWithOwner(name, id)
  373. ) {
  374. const pathFile = `${global.paths.CPI}${sep}${name}`
  375. existsFile(
  376. pathFile,
  377. () => {
  378. let method = ok
  379. let data = ''
  380. let message = ''
  381. for (const file of params.files) {
  382. if (file && file.originalname && file.path && file.filename) {
  383. try {
  384. moveSync(file.path, pathFile, { overwrite: true })
  385. data = name
  386. } catch (error) {
  387. method = internalServerError
  388. message = error && error.message
  389. break
  390. }
  391. }
  392. }
  393. res.locals.httpCode = httpResponse(
  394. method,
  395. data.length ? data : '',
  396. message
  397. )
  398. next()
  399. },
  400. () => {
  401. res.locals.httpCode = httpResponse(internalServerError, '', '')
  402. next()
  403. }
  404. )
  405. } else {
  406. res.locals.httpCode = rtn
  407. next()
  408. }
  409. }
  410. const functionRoutes = {
  411. upload,
  412. deleteFile,
  413. update,
  414. show,
  415. list,
  416. }
  417. module.exports = functionRoutes