mirror of
https://github.com/outline/outline.git
synced 2026-06-13 03:14:59 +03:00
0139b91b5d
* chore: Replace lodash with es-toolkit Migrate all direct lodash imports to es-toolkit/compat for a smaller, faster, lodash-compatible utility library. Transitive lodash usage from other packages remains unchanged. * fix: Restore isPlainObject semantics in CanCan policy The lodash migration aliased `isObject` to `lodash/isPlainObject` and the codemod incorrectly mapped the local name to es-toolkit's `isObject`, which also returns true for arrays and functions. This caused condition objects in policy definitions to be skipped, breaking authorization checks across the codebase. * fix: Restore unicode-aware length counting in validators es-toolkit/compat's size() returns string.length, while lodash's _.size() counts unicode code points. Switch to [...value].length to preserve the previous behavior so multi-byte characters like emoji count as one.
65 lines
1.6 KiB
TypeScript
65 lines
1.6 KiB
TypeScript
import { isEqual } from "es-toolkit/compat";
|
|
import { computed, observable } from "mobx";
|
|
import Model from "./base/Model";
|
|
import Field from "./decorators/Field";
|
|
import { AfterChange } from "./decorators/Lifecycle";
|
|
|
|
class Policy extends Model {
|
|
static modelName = "Policy";
|
|
|
|
/**
|
|
* An object containing keys representing abilities and values that are either
|
|
* a boolean or an array of membership IDs that have provided access to the ability.
|
|
*/
|
|
@Field
|
|
@observable
|
|
abilities: Record<string, boolean | string[]>;
|
|
|
|
/**
|
|
* Abilities flattened to an object with boolean values.
|
|
*/
|
|
@computed
|
|
get flattenedAbilities() {
|
|
const abilities: Record<string, boolean> = {};
|
|
for (const [key, value] of Object.entries(this.abilities)) {
|
|
if (Array.isArray(value)) {
|
|
// Array should never be empty, but we check as a safety measure.
|
|
abilities[key] = value.length > 0;
|
|
} else {
|
|
abilities[key] = value as boolean;
|
|
}
|
|
}
|
|
return abilities;
|
|
}
|
|
|
|
@AfterChange
|
|
public static removeChildPolicies(
|
|
model: Policy,
|
|
previousAttributes: Partial<Policy>
|
|
) {
|
|
const { documents, collections, policies } = model.store.rootStore;
|
|
|
|
if (isEqual(model.abilities, previousAttributes.abilities)) {
|
|
return;
|
|
}
|
|
|
|
const collection = collections.get(model.id);
|
|
if (collection) {
|
|
documents.inCollection(collection.id).forEach((i) => {
|
|
policies.remove(i.id);
|
|
});
|
|
return;
|
|
}
|
|
|
|
const document = documents.get(model.id);
|
|
if (document) {
|
|
document.childDocuments.forEach((i) => {
|
|
policies.remove(i.id);
|
|
});
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
export default Policy;
|