Skip to content

Commit

Permalink
Merge pull request #188 from konecty/develop
Browse files Browse the repository at this point in the history
Merge develop into main
  • Loading branch information
7sete7 authored Dec 2, 2024
2 parents c7c0def + 5a9fd10 commit 84a6755
Show file tree
Hide file tree
Showing 35 changed files with 1,456 additions and 1,170 deletions.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ module.exports = {
'@typescript-eslint/no-unsafe-call': 'off',
'no-case-declarations': 'off',
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/ban-types': 'warn',
},
settings: {
'import/parsers': {
Expand Down
44 changes: 44 additions & 0 deletions src/imports/access/getAccessForDocument.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { getUserSafe } from '@imports/auth/getUser';
import { MetaAccess } from '@imports/model/MetaAccess';
import { MetaObject } from '@imports/model/MetaObject';
import { KonectyResult } from '@imports/types/result';
import { checkMetaOperation, getAccessFor } from '@imports/utils/accessUtils';
import { errorReturn, successReturn } from '@imports/utils/return';
import { Span } from '@opentelemetry/api';
import filter from 'lodash/filter';

type GetAccessForParams = {
document: string;
authTokenId: string;

tracingSpan?: Span;
};

export default async function getAccessForDocument({ document, authTokenId, tracingSpan }: GetAccessForParams): Promise<KonectyResult<MetaAccess[]>> {
tracingSpan?.setAttribute('document', document);
tracingSpan?.addEvent('Get User', { authTokenId });

const userResponse = await getUserSafe(authTokenId);
if (userResponse.success === false) {
return errorReturn(userResponse.errors);
}

const user = userResponse.data;
const access = getAccessFor(document, user);

if (access === false || access.isReadable !== true) {
return errorReturn(`[${document}] You don't have permission for this document`);
}

tracingSpan?.addEvent('Check Meta Operation');

const metaOperationAccess = checkMetaOperation({ user, operation: 'readAccess', document });
if (metaOperationAccess === false) {
return errorReturn(`[${document}] You don't have permission to read access`);
}

tracingSpan?.addEvent('Filter Accesses');
const documentAccesses = filter(MetaObject.Access, { document });

return successReturn(documentAccesses);
}
101 changes: 101 additions & 0 deletions src/imports/access/updateAccess.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { getUserSafe } from '@imports/auth/getUser';
import { Condition } from '@imports/model/Filter';
import { MetaAccess, MetaAccessSchema } from '@imports/model/MetaAccess';
import { MetaObject } from '@imports/model/MetaObject';
import { KonectyResult } from '@imports/types/result';
import { checkMetaOperation } from '@imports/utils/accessUtils';
import { errorReturn, successReturn } from '@imports/utils/return';
import { Span } from '@opentelemetry/api';
import find from 'lodash/find';
import { UpdateFilter } from 'mongodb';
import { z } from 'zod';

const AccessUpdateSchema = z.union([
z.object({
fields: z
.object({
fieldNames: z.array(z.string()),
allow: z.boolean(),
operation: z.literal('READ').or(z.literal('UPDATE')).or(z.literal('DELETE')).or(z.literal('CREATE')),
condition: Condition.optional(),
})
.array(),
}),
MetaAccessSchema.pick({ readFilter: true }).required(),
MetaAccessSchema.pick({ updateFilter: true }).required(),
]);
export type AccessUpdate = z.infer<typeof AccessUpdateSchema>;

type UpdateAccessParams = {
document: string;
accessName: string;

data: AccessUpdate;
authTokenId: string;

tracingSpan?: Span;
};

export default async function updateAccess({ document, accessName, data, authTokenId, tracingSpan }: UpdateAccessParams): Promise<KonectyResult<MetaAccess>> {
tracingSpan?.setAttribute('document', document);
tracingSpan?.addEvent('Get User', { authTokenId });

const userResponse = await getUserSafe(authTokenId);
if (userResponse.success === false) {
return errorReturn(userResponse.errors);
}

const user = userResponse.data;

tracingSpan?.addEvent('Check Meta Operation');

const metaOperationAccess = checkMetaOperation({ user, operation: 'updateAccess', document });
if (metaOperationAccess === false) {
tracingSpan?.setAttribute('error', "You don't have permission to update access");
return errorReturn(`[${document}] You don't have permission to update access`);
}

tracingSpan?.addEvent('Find Access', { accessName });
const access = find(MetaObject.Access, { document, name: accessName });
if (!access) {
tracingSpan?.setAttribute('error', 'Access not found');
return errorReturn(`[${document}] Access not found`);
}

tracingSpan?.addEvent('Parse update schema');
const parseResponse = AccessUpdateSchema.safeParse(data);
if (parseResponse.success === false) {
const errors = parseResponse.error.flatten();
const errorMessages = Object.values(errors.fieldErrors).concat(errors.formErrors).flat();
tracingSpan?.setAttribute('error', errorMessages.join(', '));

return errorReturn(errorMessages);
}

const updateObj: Required<Pick<UpdateFilter<MetaAccess>, '$set'>> = { $set: {} };

if ('fields' in data) {
for (const { fieldNames, allow, condition, operation } of data.fields) {
for (const fieldName of fieldNames) {
updateObj.$set[`fields.${fieldName}.${operation}`] = { allow, condition };
}
}
}

if ('readFilter' in data) {
updateObj.$set = { ...updateObj.$set, readFilter: data.readFilter };
}

if ('updateFilter' in data) {
updateObj.$set = { ...updateObj.$set, updateFilter: data.updateFilter };
}

if (Object.keys(updateObj.$set).length === 0) {
tracingSpan?.addEvent('Nothing changed');
return successReturn(access);
}

tracingSpan?.addEvent('Update Access');
const result = await MetaObject.MetaObject.findOneAndUpdate({ _id: access._id }, updateObj, { returnDocument: 'after', ignoreUndefined: true });
return successReturn(result);
}
13 changes: 8 additions & 5 deletions src/imports/auth/login/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ interface LoginParams {
resolution?: { width: number; height: number } | string;
userAgent?: string;
source?: string;
fingerprint?: string;
}

interface accessLog {
interface AccessLog {
_createdAt: Date;
_updatedAt: Date;
ip?: string | string[];
Expand All @@ -31,6 +32,7 @@ interface accessLog {
resolution?: { width: number; height: number };
reason?: string;
source?: string;
fingerprint?: string;
__from?: string;
_user?: [
{
Expand All @@ -41,10 +43,10 @@ interface accessLog {
];
}

export async function login({ ip, user, password, password_SHA256, geolocation, resolution, userAgent, source }: LoginParams) {
export async function login({ ip, user, password, password_SHA256, geolocation, resolution, userAgent, source, fingerprint }: LoginParams) {
const ua = new UAParser(userAgent ?? 'API Call').getResult();

const accessLog: accessLog = {
const accessLog: AccessLog = {
_createdAt: new Date(),
_updatedAt: new Date(),
ip,
Expand All @@ -54,6 +56,7 @@ export async function login({ ip, user, password, password_SHA256, geolocation,
os: ua.os.name,
platform: ua.device.type,
source,
fingerprint,
__from: 'login',
};

Expand Down Expand Up @@ -97,7 +100,7 @@ export async function login({ ip, user, password, password_SHA256, geolocation,
return {
success: false,
logged: false,
errors: [{ message: 'Wrong user or password' }],
errors: [{ message: 'Usuário ou senha inválidos' }],
};
}

Expand All @@ -121,7 +124,7 @@ export async function login({ ip, user, password, password_SHA256, geolocation,
return {
success: false,
logged: false,
errors: [{ message: 'Wrong user or password' }],
errors: [{ message: 'Usuário ou senha inválidos' }],
};
}

Expand Down
3 changes: 3 additions & 0 deletions src/imports/consts.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,6 @@ export const DEFAULT_JPEG_MAX_SIZE = 3840;
export const DEFAULT_THUMBNAIL_SIZE = 200;
export const DEFAULT_EXPIRATION = 31536000;
export const ALLOWED_CORS_FILE_TYPES = ['png', 'jpg', 'gif', 'jpeg', 'webp'];

export const WRITE_TIMEOUT = 3e4; // 30 seconds
export const TRANSACTION_OPTIONS = { readConcern: { level: 'majority' }, writeConcern: { w: 'majority', wtimeoutMS: WRITE_TIMEOUT } };
Loading

0 comments on commit 84a6755

Please sign in to comment.