Compare commits

..

10 Commits

Author SHA1 Message Date
standardci 0aa2584e82 chore(release): publish new version
- @standardnotes/auth-server@1.164.0
 - @standardnotes/home-server@1.18.14
2023-11-01 08:55:15 +00:00
Karol Sójko eb8c704d84 feat(auth): add sending email to old email address when the address is changed (#894) 2023-11-01 09:28:00 +01:00
standardci e93fa14703 chore(release): publish new version
- @standardnotes/api-gateway@1.81.7
 - @standardnotes/files-server@1.32.3
 - @standardnotes/home-server@1.18.13
 - @standardnotes/syncing-server@1.119.4
2023-10-31 14:16:00 +00:00
Karol Sójko 16a6815b69 fix: add fallback methods for 404 requests (#893)
* fix: add fallback methods for 404 requests

* fix: remove fallback controllers exports

* fix: have only one fallback controller expored
2023-10-31 14:44:33 +01:00
standardci b08e9731b8 chore(release): publish new version
- @standardnotes/auth-server@1.163.2
 - @standardnotes/home-server@1.18.12
2023-10-30 12:23:43 +00:00
Karol Sójko 9bd4fb2d79 fix(auth): checking permissions to update setting only when directly performed by user (#892) 2023-10-30 12:51:31 +01:00
standardci 647aeda1de chore(release): publish new version
- @standardnotes/auth-server@1.163.1
 - @standardnotes/home-server@1.18.11
2023-10-30 11:20:27 +00:00
Karol Sójko 78ff748d91 fix(auth): add more information on the listed creation error 2023-10-30 11:52:26 +01:00
standardci 31f8cf1169 chore(release): publish new version
- @standardnotes/api-gateway@1.81.6
 - @standardnotes/home-server@1.18.10
2023-10-27 10:03:28 +00:00
Karol Sójko 14bcf7b6c9 fix(api-gateway): logs for errors reaching service 2023-10-27 11:36:52 +02:00
27 changed files with 205 additions and 25 deletions
+12
View File
@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.81.7](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.81.6...@standardnotes/api-gateway@1.81.7) (2023-10-31)
### Bug Fixes
* add fallback methods for 404 requests ([#893](https://github.com/standardnotes/api-gateway/issues/893)) ([16a6815](https://github.com/standardnotes/api-gateway/commit/16a6815b69e344573ae07682f3bac1d44d715d79))
## [1.81.6](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.81.5...@standardnotes/api-gateway@1.81.6) (2023-10-27)
### Bug Fixes
* **api-gateway:** logs for errors reaching service ([14bcf7b](https://github.com/standardnotes/api-gateway/commit/14bcf7b6c9403c3413e7579f58ea17168d14dce7))
## [1.81.5](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.81.4...@standardnotes/api-gateway@1.81.5) (2023-10-26)
**Note:** Version bump only for package @standardnotes/api-gateway
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/api-gateway",
"version": "1.81.5",
"version": "1.81.7",
"engines": {
"node": ">=18.0.0 <21.0.0"
},
@@ -0,0 +1,9 @@
import { BaseHttpController, all, controller, results } from 'inversify-express-utils'
@controller('')
export class FallbackController extends BaseHttpController {
@all('*')
public async fallback(): Promise<results.NotFoundResult> {
return this.notFound()
}
}
@@ -1,4 +1,5 @@
export * from './AuthMiddleware'
export * from './FallbackController'
export * from './HealthCheckController'
export * from './SubscriptionTokenAuthMiddleware'
export * from './TokenAuthenticationMethod'
@@ -261,14 +261,15 @@ export class HttpServiceProxy implements ServiceProxyInterface {
)
}
const errorMessage = (error as AxiosError).isAxiosError
? JSON.stringify((error as AxiosError).response?.data)
: (error as Error).message
let detailedErrorMessage = (error as Error).message
if (error instanceof AxiosError) {
detailedErrorMessage = `Status: ${error.status}, code: ${error.code}, message: ${error.message}`
}
this.logger.error(
tooManyRetryAttempts
? `Request to ${serverUrl}/${endpointOrMethodIdentifier} timed out after ${retryAttempt} retries`
: `Could not pass the request to ${serverUrl}/${endpointOrMethodIdentifier} on underlying service: ${errorMessage}`,
: `Could not pass the request to ${serverUrl}/${endpointOrMethodIdentifier} on underlying service: ${detailedErrorMessage}`,
)
this.logger.debug(`Response error: ${JSON.stringify(error)}`)
@@ -282,7 +283,14 @@ export class HttpServiceProxy implements ServiceProxyInterface {
? +((error as AxiosError).code as string)
: 500
response.status(errorCode).send(errorMessage)
const responseErrorMessage = (error as AxiosError).response?.data
response
.status(errorCode)
.send(
responseErrorMessage ??
"Unfortunately, we couldn't handle your request. Please try again or contact our support if the error persists.",
)
}
return
+18
View File
@@ -3,6 +3,24 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.164.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.163.2...@standardnotes/auth-server@1.164.0) (2023-11-01)
### Features
* **auth:** add sending email to old email address when the address is changed ([#894](https://github.com/standardnotes/server/issues/894)) ([eb8c704](https://github.com/standardnotes/server/commit/eb8c704d84277130dc0dc51c1fe475a7220612cd))
## [1.163.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.163.1...@standardnotes/auth-server@1.163.2) (2023-10-30)
### Bug Fixes
* **auth:** checking permissions to update setting only when directly performed by user ([#892](https://github.com/standardnotes/server/issues/892)) ([9bd4fb2](https://github.com/standardnotes/server/commit/9bd4fb2d794dae032286c68f23d3896b68735bdd))
## [1.163.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.163.0...@standardnotes/auth-server@1.163.1) (2023-10-30)
### Bug Fixes
* **auth:** add more information on the listed creation error ([78ff748](https://github.com/standardnotes/server/commit/78ff748d911a5a4063903847ef761822bbb8f4e2))
# [1.163.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.162.0...@standardnotes/auth-server@1.163.0) (2023-10-26)
### Features
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/auth-server",
"version": "1.163.0",
"version": "1.164.0",
"engines": {
"node": ">=18.0.0 <21.0.0"
},
+17 -2
View File
@@ -274,6 +274,7 @@ import { TypeORMSetting } from '../Infra/TypeORM/TypeORMSetting'
import { SettingPersistenceMapper } from '../Mapping/Persistence/SettingPersistenceMapper'
import { SubscriptionSettingPersistenceMapper } from '../Mapping/Persistence/SubscriptionSettingPersistenceMapper'
import { ApplyDefaultSettings } from '../Domain/UseCase/ApplyDefaultSettings/ApplyDefaultSettings'
import { AuthResponseFactoryResolverInterface } from '../Domain/Auth/AuthResponseFactoryResolverInterface'
export class ContainerConfigLoader {
constructor(private mode: 'server' | 'worker' = 'server') {}
@@ -723,7 +724,9 @@ export class ContainerConfigLoader {
container.bind<AuthResponseFactory20161215>(TYPES.Auth_AuthResponseFactory20161215).to(AuthResponseFactory20161215)
container.bind<AuthResponseFactory20190520>(TYPES.Auth_AuthResponseFactory20190520).to(AuthResponseFactory20190520)
container.bind<AuthResponseFactory20200115>(TYPES.Auth_AuthResponseFactory20200115).to(AuthResponseFactory20200115)
container.bind<AuthResponseFactoryResolver>(TYPES.Auth_AuthResponseFactoryResolver).to(AuthResponseFactoryResolver)
container
.bind<AuthResponseFactoryResolverInterface>(TYPES.Auth_AuthResponseFactoryResolver)
.to(AuthResponseFactoryResolver)
container.bind<KeyParamsFactory>(TYPES.Auth_KeyParamsFactory).to(KeyParamsFactory)
container
.bind<TokenDecoderInterface<SessionTokenData>>(TYPES.Auth_SessionTokenDecoder)
@@ -1020,7 +1023,19 @@ export class ContainerConfigLoader {
container.bind<GetActiveSessionsForUser>(TYPES.Auth_GetActiveSessionsForUser).to(GetActiveSessionsForUser)
container.bind<DeleteOtherSessionsForUser>(TYPES.Auth_DeleteOtherSessionsForUser).to(DeleteOtherSessionsForUser)
container.bind<DeleteSessionForUser>(TYPES.Auth_DeleteSessionForUser).to(DeleteSessionForUser)
container.bind<ChangeCredentials>(TYPES.Auth_ChangeCredentials).to(ChangeCredentials)
container
.bind<ChangeCredentials>(TYPES.Auth_ChangeCredentials)
.toConstantValue(
new ChangeCredentials(
container.get<UserRepositoryInterface>(TYPES.Auth_UserRepository),
container.get<AuthResponseFactoryResolverInterface>(TYPES.Auth_AuthResponseFactoryResolver),
container.get<DomainEventPublisherInterface>(TYPES.Auth_DomainEventPublisher),
container.get<DomainEventFactoryInterface>(TYPES.Auth_DomainEventFactory),
container.get<TimerInterface>(TYPES.Auth_Timer),
container.get<DeleteOtherSessionsForUser>(TYPES.Auth_DeleteOtherSessionsForUser),
container.get<winston.Logger>(TYPES.Auth_Logger),
),
)
container
.bind<GetSettings>(TYPES.Auth_GetSettings)
.toConstantValue(
@@ -0,0 +1,9 @@
import { html } from './user-email-changed.html'
export function getSubject(): string {
return 'Confirmation: Your Email Address Has Been Successfully Updated'
}
export function getBody(newEmail: string): string {
return html(newEmail)
}
@@ -0,0 +1,14 @@
export const html = (newEmail: string) => `
<p>Dear User,</p>
<p>We trust you're doing well. We are writing to inform you that your request to update your email address in our application has been successfully processed. Your email address associated with your Standard Notes account has now been changed to the following:</p>
<p>New Email Address: ${newEmail}</p>
<p>We understand that email address changes may occur for various reasons, and we appreciate your diligence in ensuring your account information is up to date.</p>
<p>From now on, you can log in to your account using your new email address. Your password and all other account details remain the same. If you did not initiate this change or have any concerns about the update, please contact our support team by visiting our <a href="https://standardnotes.com/help">help page</a>
or by replying directly to this email.</p>
<p>We thank you for choosing Standard Notes, and we are committed to providing you with the best experience possible. Should you have any questions or require further assistance, please do not hesitate to reach out to our support team.</p>
<p>Your satisfaction and security are our top priorities, and we appreciate your continued trust in our services.</p>
<p>Best regards,</p>
<p>
Standard Notes
</p>
`
@@ -53,7 +53,7 @@ export class ListedAccountCreatedEventHandler implements DomainEventHandlerInter
})
if (result.isFailed()) {
this.logger.error(`Could not update listed author secrets for user with uuid ${user.uuid}`)
this.logger.error(`Could not update listed author secrets for user with uuid ${user.uuid}: ${result.getError()}`)
}
}
}
@@ -15,6 +15,7 @@ import { Result, Username } from '@standardnotes/domain-core'
import { DeleteOtherSessionsForUser } from '../DeleteOtherSessionsForUser'
import { ApiVersion } from '../../Api/ApiVersion'
import { Session } from '../../Session/Session'
import { Logger } from 'winston'
describe('ChangeCredentials', () => {
let userRepository: UserRepositoryInterface
@@ -25,6 +26,7 @@ describe('ChangeCredentials', () => {
let timer: TimerInterface
let user: User
let deleteOtherSessionsForUser: DeleteOtherSessionsForUser
let logger: Logger
const createUseCase = () =>
new ChangeCredentials(
@@ -34,9 +36,13 @@ describe('ChangeCredentials', () => {
domainEventFactory,
timer,
deleteOtherSessionsForUser,
logger,
)
beforeEach(() => {
logger = {} as jest.Mocked<Logger>
logger.error = jest.fn()
authResponseFactory = {} as jest.Mocked<AuthResponseFactoryInterface>
authResponseFactory.createResponse = jest
.fn()
@@ -51,7 +57,7 @@ describe('ChangeCredentials', () => {
user.email = 'test@test.te'
userRepository = {} as jest.Mocked<UserRepositoryInterface>
userRepository.save = jest.fn()
userRepository.save = jest.fn().mockImplementation((user: User) => Promise.resolve(user))
userRepository.findOneByUsernameOrEmail = jest.fn().mockReturnValue(user)
domainEventPublisher = {} as jest.Mocked<DomainEventPublisherInterface>
@@ -1,10 +1,8 @@
import * as bcrypt from 'bcryptjs'
import { inject, injectable } from 'inversify'
import { DomainEventPublisherInterface, UserEmailChangedEvent } from '@standardnotes/domain-events'
import { TimerInterface } from '@standardnotes/time'
import { Result, UseCaseInterface, Username } from '@standardnotes/domain-core'
import { EmailLevel, Result, UseCaseInterface, Username } from '@standardnotes/domain-core'
import TYPES from '../../../Bootstrap/Types'
import { AuthResponseFactoryResolverInterface } from '../../Auth/AuthResponseFactoryResolverInterface'
import { User } from '../../User/User'
import { UserRepositoryInterface } from '../../User/UserRepositoryInterface'
@@ -14,18 +12,18 @@ import { DeleteOtherSessionsForUser } from '../DeleteOtherSessionsForUser'
import { AuthResponse20161215 } from '../../Auth/AuthResponse20161215'
import { AuthResponse20200115 } from '../../Auth/AuthResponse20200115'
import { Session } from '../../Session/Session'
import { getBody, getSubject } from '../../Email/UserEmailChanged'
import { Logger } from 'winston'
@injectable()
export class ChangeCredentials implements UseCaseInterface<AuthResponse20161215 | AuthResponse20200115> {
constructor(
@inject(TYPES.Auth_UserRepository) private userRepository: UserRepositoryInterface,
@inject(TYPES.Auth_AuthResponseFactoryResolver)
private userRepository: UserRepositoryInterface,
private authResponseFactoryResolver: AuthResponseFactoryResolverInterface,
@inject(TYPES.Auth_DomainEventPublisher) private domainEventPublisher: DomainEventPublisherInterface,
@inject(TYPES.Auth_DomainEventFactory) private domainEventFactory: DomainEventFactoryInterface,
@inject(TYPES.Auth_Timer) private timer: TimerInterface,
@inject(TYPES.Auth_DeleteOtherSessionsForUser)
private domainEventPublisher: DomainEventPublisherInterface,
private domainEventFactory: DomainEventFactoryInterface,
private timer: TimerInterface,
private deleteOtherSessionsForUserUseCase: DeleteOtherSessionsForUser,
private logger: Logger,
) {}
async execute(dto: ChangeCredentialsDTO): Promise<Result<AuthResponse20161215 | AuthResponse20200115>> {
@@ -41,6 +39,7 @@ export class ChangeCredentials implements UseCaseInterface<AuthResponse20161215
user.encryptedPassword = await bcrypt.hash(dto.newPassword, User.PASSWORD_HASH_COST)
let userEmailChangedEvent: UserEmailChangedEvent | undefined = undefined
const existingEmailAddress = user.email
if (dto.newEmail !== undefined) {
const newUsernameOrError = Username.create(dto.newEmail)
if (newUsernameOrError.isFailed()) {
@@ -78,6 +77,8 @@ export class ChangeCredentials implements UseCaseInterface<AuthResponse20161215
if (userEmailChangedEvent !== undefined) {
await this.domainEventPublisher.publish(userEmailChangedEvent)
await this.sendEmailChangedNotification(existingEmailAddress, updatedUser.email)
}
const authResponseFactory = this.authResponseFactoryResolver.resolveAuthResponseFactoryVersion(dto.apiVersion)
@@ -113,4 +114,21 @@ export class ChangeCredentials implements UseCaseInterface<AuthResponse20161215
})
}
}
private async sendEmailChangedNotification(oldEmail: string, newEmail: string): Promise<void> {
try {
await this.domainEventPublisher.publish(
this.domainEventFactory.createEmailRequestedEvent({
userEmail: oldEmail,
level: EmailLevel.LEVELS.System,
body: getBody(newEmail),
messageIdentifier: 'EMAIL_CHANGED',
subject: getSubject(),
}),
)
} catch (error) {
/* istanbul ignore next */
this.logger.error(`Could not publish email changed request for email: ${(error as Error).message}`)
}
}
}
@@ -92,6 +92,7 @@ describe('SetSettingValue', () => {
userUuid: '00000000-0000-0000-0000-000000000000',
settingName: SettingName.NAMES.ListedAuthorSecrets,
value: 'value',
checkUserPermissions: true,
})
expect(result.isFailed()).toBe(true)
@@ -108,6 +109,7 @@ describe('SetSettingValue', () => {
userUuid: '00000000-0000-0000-0000-000000000000',
settingName: SettingName.NAMES.MfaSecret,
value: 'value',
checkUserPermissions: true,
})
expect(result.isFailed()).toBe(true)
@@ -140,6 +142,20 @@ describe('SetSettingValue', () => {
expect(settingRepository.update).toHaveBeenCalled()
})
it('should create a setting with checking user permissions', async () => {
const useCase = createUseCase()
const result = await useCase.execute({
userUuid: '00000000-0000-0000-0000-000000000000',
settingName: SettingName.NAMES.MfaSecret,
value: 'value',
checkUserPermissions: true,
})
expect(result.isFailed()).toBe(false)
expect(settingRepository.insert).toHaveBeenCalled()
})
it('should insert a new setting if one does not exist', async () => {
getSetting.execute = jest.fn().mockReturnValue(Result.fail('not found'))
@@ -37,7 +37,7 @@ export class SetSettingValue implements UseCaseInterface<Setting> {
return Result.fail(`Setting ${settingName.value} is a subscription setting!`)
}
if (!(await this.userHasPermissionToUpdateSetting(userUuid, settingName))) {
if (dto.checkUserPermissions && !(await this.userHasPermissionToUpdateSetting(userUuid, settingName))) {
return Result.fail(`User ${userUuid.value} does not have permission to update setting ${settingName.value}.`)
}
@@ -2,4 +2,5 @@ export interface SetSettingValueDTO {
settingName: string
userUuid: string
value: string | null
checkUserPermissions?: boolean
}
@@ -160,6 +160,7 @@ export class BaseSettingsController extends BaseHttpController {
settingName: name,
value,
userUuid: response.locals.user.uuid,
checkUserPermissions: true,
})
if (result.isFailed()) {
+6
View File
@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.32.3](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.32.2...@standardnotes/files-server@1.32.3) (2023-10-31)
### Bug Fixes
* add fallback methods for 404 requests ([#893](https://github.com/standardnotes/files/issues/893)) ([16a6815](https://github.com/standardnotes/files/commit/16a6815b69e344573ae07682f3bac1d44d715d79))
## [1.32.2](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.32.1...@standardnotes/files-server@1.32.2) (2023-10-26)
**Note:** Version bump only for package @standardnotes/files-server
+1
View File
@@ -8,6 +8,7 @@ sdk.start()
import * as busboy from 'connect-busboy'
import '../src/Infra/InversifyExpress/AnnotatedFallbackController'
import '../src/Infra/InversifyExpress/AnnotatedHealthCheckController'
import '../src/Infra/InversifyExpress/AnnotatedFilesController'
import '../src/Infra/InversifyExpress/AnnotatedSharedVaultFilesController'
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/files-server",
"version": "1.32.2",
"version": "1.32.3",
"engines": {
"node": ">=18.0.0 <21.0.0"
},
@@ -0,0 +1,9 @@
import { BaseHttpController, all, controller, results } from 'inversify-express-utils'
@controller('')
export class AnnotatedFallbackController extends BaseHttpController {
@all('*')
public async fallback(): Promise<results.NotFoundResult> {
return this.notFound()
}
}
+20
View File
@@ -3,6 +3,26 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.18.14](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.18.13...@standardnotes/home-server@1.18.14) (2023-11-01)
**Note:** Version bump only for package @standardnotes/home-server
## [1.18.13](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.18.12...@standardnotes/home-server@1.18.13) (2023-10-31)
**Note:** Version bump only for package @standardnotes/home-server
## [1.18.12](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.18.11...@standardnotes/home-server@1.18.12) (2023-10-30)
**Note:** Version bump only for package @standardnotes/home-server
## [1.18.11](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.18.10...@standardnotes/home-server@1.18.11) (2023-10-30)
**Note:** Version bump only for package @standardnotes/home-server
## [1.18.10](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.18.9...@standardnotes/home-server@1.18.10) (2023-10-27)
**Note:** Version bump only for package @standardnotes/home-server
## [1.18.9](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.18.8...@standardnotes/home-server@1.18.9) (2023-10-26)
**Note:** Version bump only for package @standardnotes/home-server
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/home-server",
"version": "1.18.9",
"version": "1.18.14",
"engines": {
"node": ">=18.0.0 <21.0.0"
},
+6
View File
@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.119.4](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.119.3...@standardnotes/syncing-server@1.119.4) (2023-10-31)
### Bug Fixes
* add fallback methods for 404 requests ([#893](https://github.com/standardnotes/syncing-server-js/issues/893)) ([16a6815](https://github.com/standardnotes/syncing-server-js/commit/16a6815b69e344573ae07682f3bac1d44d715d79))
## [1.119.3](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.119.2...@standardnotes/syncing-server@1.119.3) (2023-10-26)
**Note:** Version bump only for package @standardnotes/syncing-server
+1
View File
@@ -6,6 +6,7 @@ import { ServiceIdentifier } from '@standardnotes/domain-core'
const sdk = new OpenTelemetrySDK({ serviceName: ServiceIdentifier.NAMES.SyncingServer })
sdk.start()
import '../src/Infra/InversifyExpressUtils/AnnotatedFallbackController'
import '../src/Infra/InversifyExpressUtils/AnnotatedHealthCheckController'
import '../src/Infra/InversifyExpressUtils/AnnotatedItemsController'
import '../src/Infra/InversifyExpressUtils/AnnotatedMessagesController'
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/syncing-server",
"version": "1.119.3",
"version": "1.119.4",
"engines": {
"node": ">=18.0.0 <21.0.0"
},
@@ -0,0 +1,9 @@
import { BaseHttpController, all, controller, results } from 'inversify-express-utils'
@controller('')
export class AnnotatedFallbackController extends BaseHttpController {
@all('*')
public async fallback(): Promise<results.NotFoundResult> {
return this.notFound()
}
}