useArticleSecurity.ts 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. // Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. import { computed, toValue } from 'vue'
  3. import {
  4. NotificationTypes,
  5. useNotifications,
  6. } from '#shared/components/CommonNotifications/index.ts'
  7. import type { TicketArticle } from '#shared/entities/ticket/types.ts'
  8. import { translateArticleSecurity } from '#shared/entities/ticket-article/composables/translateArticleSecurity.ts'
  9. import { useTicketArticleRetrySecurityProcessMutation } from '#shared/entities/ticket-article/graphql/mutations/ticketArticleRetrySecurityProcess.api.ts'
  10. import { i18n } from '#shared/i18n/index.ts'
  11. import { MutationHandler } from '#shared/server/apollo/handler/index.ts'
  12. import type { MaybeRefOrGetter } from '@vueuse/core'
  13. export const useArticleSecurity = (
  14. ticketArticle: MaybeRefOrGetter<TicketArticle>,
  15. ) => {
  16. const article = computed(() => toValue(ticketArticle))
  17. const { notify } = useNotifications()
  18. const signingSuccess = computed(
  19. () => article.value?.securityState?.signingSuccess,
  20. )
  21. const signingMessage = computed(
  22. () => article.value?.securityState?.signingMessage,
  23. )
  24. const encryptionSuccess = computed(
  25. () => article.value?.securityState?.encryptionSuccess,
  26. )
  27. const encryptionMessage = computed(
  28. () => article.value?.securityState?.encryptionMessage,
  29. )
  30. const isEncrypted = computed(
  31. () =>
  32. article.value.securityState?.encryptionSuccess ||
  33. (article.value.securityState?.encryptionSuccess === false &&
  34. article.value.securityState?.encryptionMessage),
  35. )
  36. const isSigned = computed(
  37. () =>
  38. article.value.securityState?.signingSuccess ||
  39. (article.value.securityState?.signingSuccess === false &&
  40. article.value.securityState?.signingMessage),
  41. )
  42. const hasSecurityAttribute = computed(
  43. () => article.value.securityState && (isEncrypted.value || isSigned.value),
  44. )
  45. const hasError = computed(() => {
  46. if (!article.value.securityState) return false
  47. if (
  48. article?.value.securityState?.signingSuccess === false &&
  49. article?.value.securityState?.signingMessage
  50. )
  51. return true
  52. return !!(
  53. article?.value.securityState?.encryptionSuccess === false &&
  54. article?.value.securityState?.encryptionMessage
  55. )
  56. })
  57. const retrySecurityProcess = async () => {
  58. const retryMutation = new MutationHandler(
  59. useTicketArticleRetrySecurityProcessMutation(() => ({
  60. variables: {
  61. articleId: article.value.id,
  62. },
  63. })),
  64. )
  65. const result = await retryMutation.send()
  66. const security = result?.ticketArticleRetrySecurityProcess?.retryResult
  67. if (!security) {
  68. return notify({
  69. id: 'retry-security-error',
  70. type: NotificationTypes.Error,
  71. message: __('The retried security process failed!'),
  72. timeout: 2000,
  73. })
  74. }
  75. if (security.type !== article.value?.securityState?.type) {
  76. // shouldn't be possible, we only support S/MIME
  77. return notify({
  78. id: 'security-mechanism-error',
  79. type: NotificationTypes.Error,
  80. message: __('Article uses different security mechanism.'),
  81. timeout: 2000,
  82. })
  83. }
  84. if (security.signingSuccess) {
  85. notify({
  86. id: 'signature-verified',
  87. type: NotificationTypes.Success,
  88. message: __('The signature was successfully verified.'),
  89. })
  90. } else if (security.signingMessage) {
  91. notify({
  92. id: 'signature-verification-failed',
  93. type: NotificationTypes.Error,
  94. message: __('Signature verification failed! %s'),
  95. messagePlaceholder: [i18n.t(security.signingMessage)],
  96. timeout: 2000,
  97. })
  98. }
  99. if (security.encryptionSuccess) {
  100. notify({
  101. id: 'decryption-success',
  102. type: NotificationTypes.Success,
  103. message: __('Decryption was successful.'),
  104. })
  105. } else if (security.encryptionMessage) {
  106. notify({
  107. id: 'decryption-failed',
  108. type: NotificationTypes.Error,
  109. message: __('Decryption failed! %s'),
  110. messagePlaceholder: [i18n.t(security.encryptionMessage)],
  111. timeout: 2000,
  112. })
  113. }
  114. }
  115. const typeLabel = computed(() => {
  116. if (!article.value?.securityState?.type) return
  117. return translateArticleSecurity(article.value?.securityState?.type)
  118. })
  119. return {
  120. hasError,
  121. signingSuccess,
  122. signingMessage,
  123. encryptionSuccess,
  124. encryptionMessage,
  125. isEncrypted,
  126. isSigned,
  127. hasSecurityAttribute,
  128. typeLabel,
  129. signingIcon: computed(() =>
  130. signingSuccess.value ? 'signing-success' : 'signing-fail',
  131. ),
  132. encryptionIcon: computed(() =>
  133. encryptionSuccess.value ? 'encryption-success' : 'encryption-fail',
  134. ),
  135. signedStatusMessage: computed(() =>
  136. signingSuccess.value ? __('Signed') : __('Sign error'),
  137. ),
  138. encryptedStatusMessage: computed(() =>
  139. encryptionSuccess.value ? __('Encrypted') : __('Encryption error'),
  140. ),
  141. retrySecurityProcess,
  142. }
  143. }