Skip to content

Instantly share code, notes, and snippets.

@oleksmarkh
Created January 11, 2017 20:16
Show Gist options
  • Save oleksmarkh/4f2420b634f1872274fbbf415642ae14 to your computer and use it in GitHub Desktop.
Save oleksmarkh/4f2420b634f1872274fbbf415642ae14 to your computer and use it in GitHub Desktop.
generic access control approach (user + resource [+ action])
import { has } from 'lodash';
// "user" assumed to always represent currently authenticated user
// once this assumption isn't sufficient, the politic has to be extended
// with something like:
// * isCurrentUser(user, currentUser)
// * belongsToCurrentUser(user, currentUser, resource)
// a "resource" should at least have the "type" attribute for matching purposes
// "user" is one possible detalization of a "resource" (e.g. 'const user = {type: "admin", profileId: 1, email: "kobe@example.com", ...}'),
// "profile" is another one (e.g. 'const profile = {type: "profile", id: 1, firstName: "Kobe", ...}')
export const ADMIN_TYPE = 'admin';
// export const NORMAL_USER_TYPE = 'normal user';
// export const GUEST_USER_TYPE = 'guest user';
export const PROFILE_TYPE = 'profile';
// @todo: add more resource types
const CREATE_ACTION = 'create';
const READ_ACTION = 'read';
const EDIT_ACTION = 'edit';
const REMOVE_ACTION = 'remove';
// type agnostic condition - could be nearly everything
export function matchType(resource, type) {
return (resource && resource.type === type);
}
export function isAdmin(user) {
return matchType(user, ADMIN_TYPE);
}
// export function isNormalUser(user) {
// return matchType(user, NORMAL_USER_TYPE);
// }
// export function isGuestUser(user) {
// return matchType(user, GUEST_USER_TYPE);
// }
function isProfile(resource) {
return matchType(resource, PROFILE_TYPE);
}
// @todo: add more possible "is<resource type>(resource)" validators
function ownsProfile(user, resource) {
if (!isProfile(resource)) {
return false;
}
return (user.profileId === resource.id);
}
// @todo: add more possible "owns<resource type>(user, resource)" validators
// concerned with different resource types
export function owns(user, resource) {
if (ownsProfile(user, resource)) {
return true;
}
// @todo: extend with possible additional "owns<resource type>(user, resource)" checks
return false;
}
// concerned with different relation types, e.g. (in)direct ownership
export function belongsTo(user, resource) {
if (owns(user, resource)) {
return true;
}
// @todo: extend the aspect with business logic
return false;
}
// concerned with different action types, e.g. (non)destructive
export function hasAccessTo(user, resource, action) {
if (action === READ_ACTION) {
return true;
}
// @todo: extend the relation aspect with dangerous actions
// by injecting a state sensitive aspect check if needed,
// e.g. "isFrozen(resource)"
return false;
}
// authorization: pessimistic (untrusted) politic
export function isAllowed(user, resource, action = READ_ACTION) {
// authentication (token) isn't concerned here
if (!user) {
return false;
}
// god mode
if (isAdmin(user)) {
return true;
}
if (!resource) {
return false;
}
if (belongsTo(user, resource)) {
return true;
}
return hasAccessTo(user, resource, action);
}
export function canCreate(user, resource) {
return isAllowed(user, resource, CREATE_ACTION);
}
export function canRead(user, resource) {
return isAllowed(user, resource, READ_ACTION);
}
export function canEdit(user, resource) {
return isAllowed(user, resource, EDIT_ACTION);
}
export function canRemove(user, resource) {
return isAllowed(user, resource, REMOVE_ACTION);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment