@@ -11,6 +11,7 @@ import {
1111 IAdminForthSort ,
1212 HttpExtra ,
1313 IAdminForthAndOrFilter ,
14+ BackendOnlyInput ,
1415} from "../types/Back.js" ;
1516
1617import { ADMINFORTH_VERSION , listify , md5hash , getLoginPromptHTML } from './utils.js' ;
@@ -23,6 +24,46 @@ import { ActionCheckSource, AdminForthConfigMenuItem, AdminForthDataTypes, Admin
2324 ShowInResolved } from "../types/Common.js" ;
2425import { filtersTools } from "../modules/filtersTools.js" ;
2526
27+ async function resolveBoolOrFn (
28+ val : BackendOnlyInput | undefined ,
29+ ctx : {
30+ adminUser : AdminUser ;
31+ resource : AdminForthResource ;
32+ meta : any ;
33+ source : ActionCheckSource ;
34+ adminforth : IAdminForth ;
35+ }
36+ ) : Promise < boolean > {
37+ if ( typeof val === 'function' ) {
38+ return ! ! ( await ( val as any ) ( ctx ) ) ;
39+ }
40+ return ! ! val ;
41+ }
42+
43+ async function isBackendOnly (
44+ col : AdminForthResource [ 'columns' ] [ number ] ,
45+ ctx : {
46+ adminUser : AdminUser ;
47+ resource : AdminForthResource ;
48+ meta : any ;
49+ source : ActionCheckSource ;
50+ adminforth : IAdminForth ;
51+ }
52+ ) : Promise < boolean > {
53+ return await resolveBoolOrFn ( col . backendOnly as BackendOnlyInput , ctx ) ;
54+ }
55+
56+ async function isShown (
57+ col : AdminForthResource [ 'columns' ] [ number ] ,
58+ page : 'list' | 'show' | 'edit' | 'create' | 'filter' ,
59+ ctx : Parameters < typeof isBackendOnly > [ 1 ]
60+ ) : Promise < boolean > {
61+ const s = ( col . showIn as any ) || { } ;
62+ if ( s [ page ] !== undefined ) return await resolveBoolOrFn ( s [ page ] , ctx ) ;
63+ if ( s . all !== undefined ) return await resolveBoolOrFn ( s . all , ctx ) ;
64+ return true ;
65+ }
66+
2667export async function interpretResource (
2768 adminUser : AdminUser ,
2869 resource : AdminForthResource ,
@@ -363,12 +404,22 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
363404
364405 // strip all backendOnly fields or not described in adminForth fields from dbUser
365406 // (when user defines column and does not set backendOnly, we assume it is not backendOnly)
366- Object . keys ( adminUser . dbUser ) . forEach ( ( key ) => {
367- const col = userResource . columns . find ( ( col ) => col . name === key ) ;
368- if ( ! col || col . backendOnly ) {
369- delete adminUser . dbUser [ key ] ;
407+ {
408+ const ctx = {
409+ adminUser,
410+ resource : userResource ,
411+ meta : { } ,
412+ source : ActionCheckSource . ShowRequest ,
413+ adminforth : this . adminforth ,
414+ } ;
415+ for ( const key of Object . keys ( adminUser . dbUser ) ) {
416+ const col = userResource . columns . find ( ( c ) => c . name === key ) ;
417+ const bo = col ? await isBackendOnly ( col , ctx ) : true ;
418+ if ( ! col || bo ) {
419+ delete adminUser . dbUser [ key ] ;
420+ }
370421 }
371- } )
422+ }
372423
373424 return {
374425 user : userData ,
@@ -803,14 +854,30 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
803854
804855 const pkField = resource . columns . find ( ( col ) => col . primaryKey ) ?. name ;
805856 // remove all columns which are not defined in resources, or defined but backendOnly
806- data . data . forEach ( ( item ) => {
807- Object . keys ( item ) . forEach ( ( key ) => {
808- if ( ! resource . columns . find ( ( col ) => col . name === key ) || resource . columns . find ( ( col ) => col . name === key && col . backendOnly ) ) {
809- delete item [ key ] ;
857+ {
858+ const ctx = {
859+ adminUser,
860+ resource,
861+ meta,
862+ source : {
863+ show : ActionCheckSource . ShowRequest ,
864+ list : ActionCheckSource . ListRequest ,
865+ edit : ActionCheckSource . EditLoadRequest ,
866+ } [ source ] ,
867+ adminforth : this . adminforth ,
868+ } ;
869+
870+ for ( const item of data . data ) {
871+ for ( const key of Object . keys ( item ) ) {
872+ const col = resource . columns . find ( ( c ) => c . name === key ) ;
873+ const bo = col ? await isBackendOnly ( col , ctx ) : true ;
874+ if ( ! col || bo ) {
875+ delete item [ key ] ;
876+ }
810877 }
811- } )
812- item . _label = resource . recordLabel ( item ) ;
813- } ) ;
878+ item . _label = resource . recordLabel ( item ) ;
879+ }
880+ }
814881 if ( source === 'list' && resource . options . listTableClickUrl ) {
815882 await Promise . all (
816883 data . data . map ( async ( item ) => {
@@ -1076,11 +1143,30 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
10761143 }
10771144 }
10781145
1146+ const ctxCreate = {
1147+ adminUser,
1148+ resource,
1149+ meta : { requestBody : body } ,
1150+ source : ActionCheckSource . CreateRequest ,
1151+ adminforth : this . adminforth ,
1152+ } ;
1153+
1154+ for ( const column of resource . columns ) {
1155+ if ( ( column . required as { create ?: boolean } ) ?. create ) {
1156+ const shown = await isShown ( column , 'create' , ctxCreate ) ;
1157+ if ( shown && record [ column . name ] === undefined ) {
1158+ return { error : `Column '${ column . name } ' is required` , ok : false } ;
1159+ }
1160+ }
1161+ }
1162+
10791163 for ( const column of resource . columns ) {
10801164 const fieldName = column . name ;
10811165 if ( fieldName in record ) {
1082- if ( ! column . showIn ?. create || column . backendOnly ) {
1083- return { error : `Field "${ fieldName } " cannot be modified as it is restricted from creation (showIn.create is false, please set it to true)` , ok : false } ;
1166+ const shown = await isShown ( column , 'create' , ctxCreate ) ;
1167+ const bo = await isBackendOnly ( column , ctxCreate ) ;
1168+ if ( ! shown || bo ) {
1169+ return { error : `Field "${ fieldName } " cannot be modified as it is restricted from creation (backendOnly or showIn.create is false, please set it to true)` , ok : false } ;
10841170 }
10851171 }
10861172 }
@@ -1172,15 +1258,24 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
11721258 return { error : allowedError } ;
11731259 }
11741260
1261+ const ctxEdit = {
1262+ adminUser,
1263+ resource,
1264+ meta : { requestBody : body , newRecord : record , oldRecord, pk : recordId } ,
1265+ source : ActionCheckSource . EditRequest ,
1266+ adminforth : this . adminforth ,
1267+ } ;
1268+
11751269 for ( const column of resource . columns ) {
11761270 const fieldName = column . name ;
11771271 if ( fieldName in record ) {
1178- if ( ! column . showIn ?. edit || column . editReadonly || column . backendOnly ) {
1179- return { error : `Field "${ fieldName } " cannot be modified as it is restricted from editing (showIn.edit is false, please set it to true)` , ok : false } ;
1272+ const shown = await isShown ( column , 'edit' , ctxEdit ) ;
1273+ const bo = await isBackendOnly ( column , ctxEdit ) ;
1274+ if ( ! shown || column . editReadonly || bo ) {
1275+ return { error : `Field "${ fieldName } " cannot be modified as it is restricted from editing (backendOnly or showIn.edit is false, please set it to true)` , ok : false } ;
11801276 }
11811277 }
11821278 }
1183-
11841279 // for polymorphic foreign resources, we need to find out the value for polymorphicOn column
11851280 for ( const column of resource . columns ) {
11861281 if ( column . foreignResource ?. polymorphicOn && record [ column . name ] === null ) {
0 commit comments