useArticleSecurity.ts 4.7 KB

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