@@ -11,6 +11,7 @@ import {
11
11
IAdminForthSort ,
12
12
HttpExtra ,
13
13
IAdminForthAndOrFilter ,
14
+ BackendOnlyInput ,
14
15
} from "../types/Back.js" ;
15
16
16
17
import { ADMINFORTH_VERSION , listify , md5hash , getLoginPromptHTML } from './utils.js' ;
@@ -23,6 +24,46 @@ import { ActionCheckSource, AdminForthConfigMenuItem, AdminForthDataTypes, Admin
23
24
ShowInResolved } from "../types/Common.js" ;
24
25
import { filtersTools } from "../modules/filtersTools.js" ;
25
26
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
+
26
67
export async function interpretResource (
27
68
adminUser : AdminUser ,
28
69
resource : AdminForthResource ,
@@ -363,12 +404,22 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
363
404
364
405
// strip all backendOnly fields or not described in adminForth fields from dbUser
365
406
// (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
+ }
370
421
}
371
- } )
422
+ }
372
423
373
424
return {
374
425
user : userData ,
@@ -803,14 +854,30 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
803
854
804
855
const pkField = resource . columns . find ( ( col ) => col . primaryKey ) ?. name ;
805
856
// 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
+ }
810
877
}
811
- } )
812
- item . _label = resource . recordLabel ( item ) ;
813
- } ) ;
878
+ item . _label = resource . recordLabel ( item ) ;
879
+ }
880
+ }
814
881
if ( source === 'list' && resource . options . listTableClickUrl ) {
815
882
await Promise . all (
816
883
data . data . map ( async ( item ) => {
@@ -1076,11 +1143,30 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
1076
1143
}
1077
1144
}
1078
1145
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
+
1079
1163
for ( const column of resource . columns ) {
1080
1164
const fieldName = column . name ;
1081
1165
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 } ;
1084
1170
}
1085
1171
}
1086
1172
}
@@ -1172,15 +1258,24 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
1172
1258
return { error : allowedError } ;
1173
1259
}
1174
1260
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
+
1175
1269
for ( const column of resource . columns ) {
1176
1270
const fieldName = column . name ;
1177
1271
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 } ;
1180
1276
}
1181
1277
}
1182
1278
}
1183
-
1184
1279
// for polymorphic foreign resources, we need to find out the value for polymorphicOn column
1185
1280
for ( const column of resource . columns ) {
1186
1281
if ( column . foreignResource ?. polymorphicOn && record [ column . name ] === null ) {
0 commit comments