EndpointSource.ts 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. /*
  2. Copyright (C) 2017 Cloudbase Solutions SRL
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU Affero General Public License as
  5. published by the Free Software Foundation, either version 3 of the
  6. License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Affero General Public License for more details.
  11. You should have received a copy of the GNU Affero General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. */
  14. import moment from 'moment'
  15. import Api from '../utils/ApiCaller'
  16. import notificationStore from '../stores/NotificationStore'
  17. import { SchemaParser } from './Schemas'
  18. import ObjectUtils from '../utils/ObjectUtils'
  19. import type { Endpoint, Validation, Storage } from '../@types/Endpoint'
  20. import configLoader from '../utils/Config'
  21. import DomUtils from '../utils/DomUtils'
  22. const getBarbicanPayload = (data: any) => ({
  23. payload: JSON.stringify(data),
  24. payload_content_type: 'text/plain',
  25. algorithm: 'aes',
  26. bit_length: 256,
  27. mode: 'cbc',
  28. content_types: {
  29. default: 'text/plain',
  30. },
  31. })
  32. class EndpointSource {
  33. async getEndpoints(skipLog?: boolean): Promise<Endpoint[]> {
  34. const response = await Api.send({
  35. url: `${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/endpoints`,
  36. skipLog,
  37. })
  38. const connections: any[] = []
  39. if (response.data.endpoints.length) {
  40. response.data.endpoints.forEach((endpoint: Endpoint) => {
  41. connections.push(SchemaParser.parseConnectionResponse(endpoint))
  42. })
  43. }
  44. connections.sort((c1, c2) => moment(c2.created_at).diff(moment(c1.created_at)))
  45. return connections
  46. }
  47. async delete(endpoint: Endpoint): Promise<string> {
  48. await Api.send({
  49. url: `${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/endpoints/${endpoint.id}`,
  50. method: 'DELETE',
  51. })
  52. if (endpoint.connection_info && endpoint.connection_info.secret_ref) {
  53. const uuidIndex = endpoint.connection_info.secret_ref.lastIndexOf('/')
  54. const uuid = endpoint.connection_info.secret_ref.substr(uuidIndex + 1)
  55. await Api.send({
  56. url: `${configLoader.config.servicesUrls.barbican}/v1/secrets/${uuid}`,
  57. method: 'DELETE',
  58. })
  59. return endpoint.id
  60. }
  61. return endpoint.id
  62. }
  63. async getConnectionInfo(endpoint: Endpoint): Promise<Endpoint['connection_info']> {
  64. const index = endpoint.connection_info.secret_ref && endpoint.connection_info.secret_ref.lastIndexOf('/')
  65. const uuid = index && endpoint.connection_info.secret_ref && endpoint
  66. .connection_info.secret_ref.substr(index + 1)
  67. if (!uuid) {
  68. return endpoint.connection_info
  69. }
  70. const response = await Api.send({
  71. url: `${configLoader.config.servicesUrls.barbican}/v1/secrets/${uuid || 'undefined'}/payload`,
  72. responseType: 'text',
  73. headers: { Accept: 'text/plain' },
  74. })
  75. return response.data
  76. }
  77. async getSecretPayload(uuid: string, count: number = 0) {
  78. const delay = () => new Promise<void>(r => { setTimeout(() => { r() }, 2000) })
  79. if (count >= 10) {
  80. return Promise.reject({ secretCustomError: `The secret '${uuid}' is not active after ${count} retries.` })
  81. }
  82. const response = await Api.send({
  83. url: `${configLoader.config.servicesUrls.barbican}/v1/secrets/${uuid}`,
  84. headers: { Accept: 'application/json' },
  85. })
  86. if (response.data.status === 'ACTIVE') {
  87. const payload = await Api.send({
  88. url: `${configLoader.config.servicesUrls.barbican}/v1/secrets/${uuid}/payload`,
  89. headers: { Accept: 'text/plain' },
  90. })
  91. return payload
  92. }
  93. await delay()
  94. const payload: any = await this.getSecretPayload(uuid, count + 1)
  95. return payload
  96. }
  97. async getConnectionsInfo(endpoints: Endpoint[]): Promise<Endpoint[]> {
  98. const result: Endpoint[] = await Promise.all(endpoints.map(async endpoint => {
  99. const index = endpoint.connection_info.secret_ref ? endpoint.connection_info.secret_ref.lastIndexOf('/') : ''
  100. const uuid = endpoint.connection_info.secret_ref && index ? endpoint.connection_info.secret_ref.substr(index + 1) : ''
  101. if (!uuid) {
  102. return { ...endpoint }
  103. }
  104. const response = await Api.send({
  105. url: `${configLoader.config.servicesUrls.barbican}/v1/secrets/${uuid}/payload`,
  106. responseType: 'text',
  107. headers: { Accept: 'text/plain' },
  108. })
  109. return { ...endpoint, connection_info: response.data }
  110. }))
  111. return result
  112. }
  113. async validate(endpoint: Endpoint): Promise<Validation> {
  114. const response = await Api.send({
  115. url: `${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/endpoints/${endpoint.id}/actions`,
  116. method: 'POST',
  117. data: { 'validate-connection': null },
  118. })
  119. return response.data['validate-connection']
  120. }
  121. async update(endpoint: Endpoint): Promise<Endpoint> {
  122. const parsedConnectionInfo = SchemaParser.connectionInfoToPayload(endpoint)
  123. if (parsedConnectionInfo
  124. && Object.keys(parsedConnectionInfo).length > 0
  125. && parsedConnectionInfo.secret_ref) {
  126. let uuidIndex = parsedConnectionInfo.secret_ref.lastIndexOf('/')
  127. let uuid = parsedConnectionInfo.secret_ref.substr(uuidIndex + 1)
  128. let newEndpoint: any = {}
  129. let connectionInfo: any = {}
  130. await Api.send({
  131. url: `${configLoader.config.servicesUrls.barbican}/v1/secrets/${uuid}`,
  132. method: 'DELETE',
  133. })
  134. const barbicanPayload = getBarbicanPayload(ObjectUtils.skipFields(parsedConnectionInfo, ['secret_ref']))
  135. const response = await Api.send({
  136. url: `${configLoader.config.servicesUrls.barbican}/v1/secrets`,
  137. method: 'POST',
  138. data: barbicanPayload,
  139. })
  140. connectionInfo = { secret_ref: response.data.secret_ref }
  141. const newPayload = {
  142. endpoint: {
  143. name: endpoint.name,
  144. mapped_regions: endpoint.mapped_regions,
  145. description: endpoint.description,
  146. connection_info: connectionInfo,
  147. },
  148. }
  149. const putResponse = await Api.send({
  150. url: `${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/endpoints/${endpoint.id}`,
  151. method: 'PUT',
  152. data: newPayload,
  153. })
  154. uuidIndex = connectionInfo.secret_ref.lastIndexOf('/')
  155. uuid = connectionInfo.secret_ref.substr(uuidIndex + 1)
  156. newEndpoint = putResponse.data.endpoint
  157. try {
  158. const conInfoResponse = await this.getSecretPayload(uuid)
  159. newEndpoint.connection_info = {
  160. ...newEndpoint.connection_info,
  161. ...conInfoResponse.data,
  162. }
  163. return newEndpoint
  164. } catch (e) {
  165. if (e.secretCustomError) {
  166. notificationStore.alert(e.secretCustomError, 'error')
  167. }
  168. throw e
  169. }
  170. }
  171. const response = await Api.send({
  172. url: `${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/endpoints/${endpoint.id}`,
  173. method: 'PUT',
  174. data: {
  175. endpoint: {
  176. name: endpoint.name,
  177. mapped_regions: endpoint.mapped_regions,
  178. description: endpoint.description,
  179. connection_info: parsedConnectionInfo,
  180. },
  181. },
  182. })
  183. return SchemaParser.parseConnectionResponse(response.data.endpoint)
  184. }
  185. async add(endpoint: Endpoint, skipSchemaParser: boolean = false): Promise<Endpoint> {
  186. const parsedConnectionInfo: any = skipSchemaParser
  187. ? { ...endpoint.connection_info } : SchemaParser.connectionInfoToPayload(endpoint)
  188. let newEndpoint: any = {}
  189. let connectionInfo: any = {}
  190. if (configLoader.config.useBarbicanSecrets
  191. && parsedConnectionInfo && Object.keys(parsedConnectionInfo).length > 0) {
  192. const response = await Api.send({
  193. url: `${configLoader.config.servicesUrls.barbican}/v1/secrets`,
  194. method: 'POST',
  195. data: getBarbicanPayload(ObjectUtils.skipFields(parsedConnectionInfo, ['secret_ref'])),
  196. })
  197. connectionInfo = { secret_ref: response.data.secret_ref }
  198. const newPayload = {
  199. endpoint: {
  200. name: endpoint.name,
  201. mapped_regions: endpoint.mapped_regions,
  202. description: endpoint.description,
  203. type: endpoint.type,
  204. connection_info: connectionInfo,
  205. },
  206. }
  207. const postResponse = await Api.send({
  208. url: `${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/endpoints`,
  209. method: 'POST',
  210. data: newPayload,
  211. })
  212. const uuidIndex = connectionInfo.secret_ref.lastIndexOf('/')
  213. const uuid = connectionInfo.secret_ref.substr(uuidIndex + 1)
  214. newEndpoint = postResponse.data.endpoint
  215. try {
  216. const conInfoResponse = await this.getSecretPayload(uuid)
  217. newEndpoint.connection_info = {
  218. ...newEndpoint.connection_info,
  219. ...conInfoResponse.data,
  220. }
  221. return newEndpoint
  222. } catch (e) {
  223. if (e.secretCustomError) {
  224. notificationStore.alert(e.secretCustomError, 'error')
  225. }
  226. throw e
  227. }
  228. }
  229. const response = await Api.send({
  230. url: `${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/endpoints`,
  231. method: 'POST',
  232. data: {
  233. endpoint: {
  234. name: endpoint.name,
  235. mapped_regions: endpoint.mapped_regions,
  236. description: endpoint.description,
  237. type: endpoint.type,
  238. connection_info: parsedConnectionInfo,
  239. },
  240. },
  241. })
  242. return SchemaParser.parseConnectionResponse(response.data.endpoint)
  243. }
  244. async loadStorage(endpointId: string, data: any): Promise<Storage> {
  245. const env = DomUtils.encodeToBase64Url(data)
  246. const response = await Api.get(`${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/endpoints/${endpointId}/storage?env=${env}`)
  247. return response.data.storage
  248. }
  249. }
  250. export default new EndpointSource()