@@ -8,6 +8,11 @@ import { ValueRecorder } from "@opentelemetry/api-metrics"
88import { Span } from "@opentelemetry/api" ;
99import { Logger } from "./Logger" ;
1010import { Auth } from "./Auth" ;
11+ import { flush } from "pm2" ;
12+ const { JSONPath } = require ( 'jsonpath-plus' ) ;
13+
14+
15+
1116// tslint:disable-next-line: typedef
1217const safeObjectID = ( s : string | number | ObjectID ) => ObjectID . isValid ( s ) ? new ObjectID ( s ) : null ;
1318const isoDatePattern = new RegExp ( / \d { 4 } - [ 0 1 ] \d - [ 0 - 3 ] \d T [ 0 - 2 ] \d : [ 0 - 5 ] \d : [ 0 - 5 ] \d \. \d + ( [ + - ] [ 0 - 2 ] \d : [ 0 - 5 ] \d | Z ) / ) ;
@@ -822,8 +827,10 @@ export class DatabaseConnection {
822827 span . addEvent ( "ensureResource" ) ;
823828 span . addEvent ( "verityToken" ) ;
824829 const user : TokenUser = Crypt . verityToken ( jwt ) ;
825-
826830 item = this . ensureResource ( item ) ;
831+ if ( ! await this . CheckEntityRestriction ( user , collectionname , item , span ) ) {
832+ throw Error ( "Create " + item . _type + " access denied" ) ;
833+ }
827834 span . addEvent ( "traversejsonencode" ) ;
828835 DatabaseConnection . traversejsonencode ( item ) ;
829836 let name = item . name ;
@@ -840,7 +847,7 @@ export class DatabaseConnection {
840847 Base . addRight ( item , user . _id , user . name , [ Rights . full_control ] ) ;
841848 }
842849 if ( collectionname != "audit" ) { Logger . instanse . silly ( "[" + user . username + "][" + collectionname + "] Adding " + item . _type + " " + name + " to database" ) ; }
843- if ( ! this . hasAuthorization ( user , item , Rights . create ) ) { throw new Error ( "Access denied, no authorization to InsertOne " + item . _type + " " + name + " to database" ) ; }
850+ if ( ! DatabaseConnection . hasAuthorization ( user , item , Rights . create ) ) { throw new Error ( "Access denied, no authorization to InsertOne " + item . _type + " " + name + " to database" ) ; }
844851
845852 span . addEvent ( "encryptentity" ) ;
846853 item = this . encryptentity ( item ) as T ;
@@ -959,6 +966,9 @@ export class DatabaseConnection {
959966 setTimeout ( ( ) => OAuthProvider . LoadClients ( ) , 1000 ) ;
960967 }
961968 }
969+ if ( collectionname === "config" && item . _type === "restriction" ) {
970+ this . EntityRestrictions = null ;
971+ }
962972 span . addEvent ( "traversejsondecode" ) ;
963973 DatabaseConnection . traversejsondecode ( item ) ;
964974 if ( Config . log_inserts ) Logger . instanse . debug ( "[" + user . username + "][" + collectionname + "] inserted " + item . name ) ;
@@ -992,6 +1002,11 @@ export class DatabaseConnection {
9921002 for ( let i = 0 ; i < items . length ; i ++ ) {
9931003 let item = this . ensureResource ( items [ i ] ) ;
9941004 DatabaseConnection . traversejsonencode ( item ) ;
1005+
1006+ if ( ! await this . CheckEntityRestriction ( user , collectionname , item , span ) ) {
1007+ continue ;
1008+ }
1009+
9951010 let name = item . name ;
9961011 if ( NoderedUtil . IsNullEmpty ( name ) ) name = item . _name ;
9971012 if ( NoderedUtil . IsNullEmpty ( name ) ) name = "Unknown" ;
@@ -1006,7 +1021,7 @@ export class DatabaseConnection {
10061021 Base . addRight ( item , user . _id , user . name , [ Rights . full_control ] ) ;
10071022 }
10081023 if ( collectionname != "audit" ) { Logger . instanse . silly ( "[" + user . username + "][" + collectionname + "] Adding " + item . _type + " " + name + " to database" ) ; }
1009- if ( ! this . hasAuthorization ( user , item , Rights . create ) ) { throw new Error ( "Access denied, no authorization to InsertOne " + item . _type + " " + name + " to database" ) ; }
1024+ if ( ! DatabaseConnection . hasAuthorization ( user , item , Rights . create ) ) { throw new Error ( "Access denied, no authorization to InsertOne " + item . _type + " " + name + " to database" ) ; }
10101025
10111026 item = this . encryptentity ( item ) as T ;
10121027
@@ -1191,8 +1206,7 @@ export class DatabaseConnection {
11911206 if ( q . item === null || q . item === undefined ) { throw Error ( "Cannot update null item" ) ; }
11921207 await this . connect ( span ) ;
11931208 const user : TokenUser = Crypt . verityToken ( q . jwt ) ;
1194- if ( ! this . hasAuthorization ( user , ( q . item as Base ) , Rights . update ) ) {
1195- const again = this . hasAuthorization ( user , ( q . item as Base ) , Rights . update ) ;
1209+ if ( ! DatabaseConnection . hasAuthorization ( user , ( q . item as Base ) , Rights . update ) ) {
11961210 throw new Error ( "Access denied, no authorization to UpdateOne" ) ;
11971211 }
11981212 if ( q . collectionname === "files" ) { q . collectionname = "fs.files" ; }
@@ -1209,8 +1223,7 @@ export class DatabaseConnection {
12091223 if ( NoderedUtil . IsNullEmpty ( name ) ) name = "Unknown" ;
12101224 original = await this . getbyid < T > ( q . item . _id , q . collectionname , q . jwt , span ) ;
12111225 if ( ! original ) { throw Error ( "item not found!" ) ; }
1212- if ( ! this . hasAuthorization ( user , original , Rights . update ) ) {
1213- const again = this . hasAuthorization ( user , original , Rights . update ) ;
1226+ if ( ! DatabaseConnection . hasAuthorization ( user , original , Rights . update ) ) {
12141227 throw new Error ( "Access denied, no authorization to UpdateOne " + q . item . _type + " " + name + " to database" ) ;
12151228 }
12161229 if ( q . collectionname === "users" && ! NoderedUtil . IsNullEmpty ( q . item . _type ) && ! NoderedUtil . IsNullEmpty ( q . item . name ) ) {
@@ -1230,6 +1243,11 @@ export class DatabaseConnection {
12301243 const keys : string [ ] = Object . keys ( original ) ;
12311244 for ( let i : number = 0 ; i < keys . length ; i ++ ) {
12321245 let key : string = keys [ i ] ;
1246+ if ( key == "username" && q . collectionname == "users" && q . item . _type == "user" ) {
1247+ if ( ! user . HasRoleName ( "admins" ) ) {
1248+ q . item [ key ] = original [ key ] ;
1249+ }
1250+ }
12331251 if ( key === "_created" ) {
12341252 q . item [ key ] = new Date ( original [ key ] ) ;
12351253 } else if ( key === "_createdby" || key === "_createdbyid" ) {
@@ -1251,6 +1269,9 @@ export class DatabaseConnection {
12511269 q . item . _version = original . _version ;
12521270 }
12531271 q . item = this . ensureResource ( q . item ) ;
1272+ if ( original . _type != q . item . _type && ! await this . CheckEntityRestriction ( user , q . collectionname , q . item , span ) ) {
1273+ throw Error ( "Create " + q . item . _type + " access denied" ) ;
1274+ }
12541275 DatabaseConnection . traversejsonencode ( q . item ) ;
12551276 q . item = this . encryptentity ( q . item ) ;
12561277 const hasUser : Ace = q . item . _acl . find ( e => e . _id === user . _id ) ;
@@ -1271,6 +1292,8 @@ export class DatabaseConnection {
12711292 let key : string = keys [ i ] ;
12721293 if ( key === "_created" ) {
12731294 ( q . item as any ) . metadata [ key ] = new Date ( ( original as any ) . metadata [ key ] ) ;
1295+ } else if ( key === "_type" ) {
1296+ ( q . item as any ) . metadata [ key ] = ( original as any ) . metadata [ key ] ;
12741297 } else if ( key === "_createdby" || key === "_createdbyid" ) {
12751298 ( q . item as any ) . metadata [ key ] = ( original as any ) . metadata [ key ] ;
12761299 } else if ( key === "_modifiedby" || key === "_modifiedbyid" || key === "_modified" ) {
@@ -1416,6 +1439,9 @@ export class DatabaseConnection {
14161439 } else {
14171440 ( q . item as any ) . metadata = this . decryptentity < T > ( ( q . item as any ) . metadata ) ;
14181441 }
1442+ if ( q . collectionname === "config" && q . item . _type === "restriction" ) {
1443+ this . EntityRestrictions = null ;
1444+ }
14191445 DatabaseConnection . traversejsondecode ( q . item ) ;
14201446 q . result = q . item ;
14211447 } catch ( error ) {
@@ -1447,7 +1473,7 @@ export class DatabaseConnection {
14471473 if ( q . item === null || q . item === undefined ) { throw Error ( "Cannot update null item" ) ; }
14481474 await this . connect ( ) ;
14491475 const user : TokenUser = Crypt . verityToken ( q . jwt ) ;
1450- if ( ! this . hasAuthorization ( user , q . item , Rights . update ) ) { throw new Error ( "Access denied, no authorization to UpdateMany" ) ; }
1476+ if ( ! DatabaseConnection . hasAuthorization ( user , q . item , Rights . update ) ) { throw new Error ( "Access denied, no authorization to UpdateMany" ) ; }
14511477
14521478 if ( q . collectionname === "users" && q . item . _type === "user" && q . item . hasOwnProperty ( "newpassword" ) ) {
14531479 ( q . item as any ) . passwordhash = await Crypt . hash ( ( q . item as any ) . newpassword ) ;
@@ -1496,6 +1522,7 @@ export class DatabaseConnection {
14961522 }
14971523
14981524 if ( ( q . item [ "$set" ] ) === undefined ) { ( q . item [ "$set" ] ) = { } } ;
1525+ if ( q . item [ "$set" ] . _type ) delete q . item [ "$set" ] . _type ;
14991526 ( q . item [ "$set" ] ) . _modifiedby = user . name ;
15001527 ( q . item [ "$set" ] ) . _modifiedbyid = user . _id ;
15011528 ( q . item [ "$set" ] ) . _modified = new Date ( new Date ( ) . toISOString ( ) ) ;
@@ -1567,7 +1594,7 @@ export class DatabaseConnection {
15671594 else if ( exists . length > 1 ) {
15681595 throw JSON . stringify ( query ) + " is not uniqe, more than 1 item in collection matches this" ;
15691596 }
1570- if ( ! this . hasAuthorization ( user , q . item , Rights . update ) ) {
1597+ if ( ! DatabaseConnection . hasAuthorization ( user , q . item , Rights . update ) ) {
15711598 Base . addRight ( q . item , user . _id , user . name , [ Rights . full_control ] , false ) ;
15721599 this . ensureResource ( q . item ) ;
15731600 }
@@ -1976,14 +2003,47 @@ export class DatabaseConnection {
19762003 }
19772004 return item ;
19782005 }
2006+ public EntityRestrictions : EntityRestriction [ ] = null ;
2007+ async CheckEntityRestriction ( user : TokenUser , collection : string , item : Base , parent : Span ) : Promise < boolean > {
2008+ if ( this . EntityRestrictions == null ) {
2009+ const rootjwt = Crypt . rootToken ( )
2010+ this . EntityRestrictions = await this . query < EntityRestriction > ( { "_type" : "restriction" } , null , 1 , 0 , null , "config" , rootjwt , null , null , parent ) ;
2011+ let allowadmins = new EntityRestriction ( ) ;
2012+ allowadmins . copyperm = false ; allowadmins . collection = "" ; allowadmins . paths = [ "$." ] ;
2013+ Base . addRight ( allowadmins , WellknownIds . admins , "admins" , [ Rights . create ] ) ;
2014+ this . EntityRestrictions . push ( allowadmins ) ;
2015+ for ( let i = 0 ; i < this . EntityRestrictions . length ; i ++ ) {
2016+ this . EntityRestrictions [ i ] = EntityRestriction . assign ( this . EntityRestrictions [ i ] ) ;
2017+ }
2018+ }
2019+ const defaultAllow : boolean = false ;
2020+ let result : boolean = false ;
2021+ const authorized = this . EntityRestrictions . filter ( x => x . IsAuthorized ( user ) && ( x . collection == collection || x . collection == "" ) ) ;
2022+ const matches = authorized . filter ( x => x . IsMatch ( item ) && ( x . collection == collection || x . collection == "" ) ) ;
2023+ const copyperm = authorized . filter ( x => x . copyperm && ( x . collection == collection || x . collection == "" ) ) ;
2024+ if ( ! defaultAllow && matches . length == 0 ) return false ; // no hits, if not allowed return false
2025+ if ( matches . length > 0 ) result = true ;
2026+
2027+ for ( let cr of copyperm ) {
2028+ for ( let a of cr . _acl ) {
2029+ let bits = [ ] ;
2030+ for ( let i = 0 ; i < 10 ; i ++ ) {
2031+ if ( Ace . isBitSet ( a , i ) ) bits . push ( i ) ;
2032+
2033+ }
2034+ Base . addRight ( item , a . _id , a . name , bits , false ) ;
2035+ }
2036+ }
2037+ return result ;
2038+ }
19792039 /**
19802040 * Validated user has rights to perform the requested action ( create is missing! )
19812041 * @param {TokenUser } user User requesting permission
19822042 * @param {any } item Item permission is needed on
19832043 * @param {Rights } action Permission wanted (create, update, delete)
19842044 * @returns boolean Is allowed
19852045 */
1986- hasAuthorization ( user : TokenUser , item : Base , action : number ) : boolean {
2046+ static hasAuthorization ( user : TokenUser , item : Base , action : number ) : boolean {
19872047 if ( Config . api_bypass_perm_check ) { return true ; }
19882048 if ( user . _id === WellknownIds . root ) { return true ; }
19892049 if ( action === Rights . create || action === Rights . delete ) {
@@ -2473,3 +2533,33 @@ export class DatabaseConnection {
24732533 }
24742534}
24752535
2536+ export class EntityRestriction extends Base {
2537+ public collection : string ;
2538+ public copyperm : boolean ;
2539+ public paths : string [ ] ;
2540+ constructor (
2541+ ) {
2542+ super ( ) ;
2543+ this . _type = "restriction" ;
2544+ }
2545+ static assign < EntityRestriction > ( o : any ) : EntityRestriction {
2546+ if ( typeof o === 'string' || o instanceof String ) {
2547+ return Object . assign ( new EntityRestriction ( ) , JSON . parse ( o . toString ( ) ) ) ;
2548+ }
2549+ return Object . assign ( new EntityRestriction ( ) , o ) ;
2550+ }
2551+ public IsMatch ( object : object ) : boolean {
2552+ for ( let path of this . paths ) {
2553+ const result = JSONPath ( { path, json : { a : object } } ) ;
2554+ if ( result && result . length > 0 ) return true ;
2555+ }
2556+ return false ;
2557+ }
2558+ public IsAuthorized ( user : TokenUser | User ) : boolean {
2559+ return DatabaseConnection . hasAuthorization ( user as TokenUser , this , Rights . create ) ;
2560+ }
2561+ // public IsAllowed(user: TokenUser | User, object: object, NoMatchValue: boolean) {
2562+ // if (!this.IsMatch(object)) return NoMatchValue;
2563+ // return this.IsAuthorized(user);
2564+ // }
2565+ }
0 commit comments