@@ -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 ) ( 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 , 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 ,
@@ -194,6 +235,18 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
194
235
} ,
195
236
} )
196
237
238
+ server . endpoint ( {
239
+ noAuth : true ,
240
+ method : 'GET' ,
241
+ path : '/get_login_form_config' ,
242
+ handler : async ( { tr } ) => {
243
+ const loginPromptHTML = await getLoginPromptHTML ( this . adminforth . config . auth . loginPromptHTML ) ;
244
+ return {
245
+ loginPromptHTML : await tr ( loginPromptHTML , 'system.loginPromptHTML' ) ,
246
+ }
247
+ }
248
+ } )
249
+
197
250
server . endpoint ( {
198
251
noAuth : true ,
199
252
method : 'GET' ,
@@ -210,8 +263,6 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
210
263
const resource = this . adminforth . config . resources . find ( ( res ) => res . resourceId === this . adminforth . config . auth . usersResourceId ) ;
211
264
const usernameColumn = resource . columns . find ( ( col ) => col . name === usernameField ) ;
212
265
213
- const loginPromptHTML = await getLoginPromptHTML ( this . adminforth . config . auth . loginPromptHTML ) ;
214
-
215
266
return {
216
267
brandName : this . adminforth . config . customization . brandName ,
217
268
usernameFieldName : usernameColumn . label ,
@@ -220,7 +271,6 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
220
271
removeBackgroundBlendMode : this . adminforth . config . auth . removeBackgroundBlendMode ,
221
272
title : this . adminforth . config . customization ?. title ,
222
273
demoCredentials : this . adminforth . config . auth . demoCredentials ,
223
- loginPromptHTML : await tr ( loginPromptHTML , 'system.loginPromptHTML' ) ,
224
274
loginPageInjections : this . adminforth . config . customization . loginPageInjections ,
225
275
globalInjections : {
226
276
everyPageBottom : this . adminforth . config . customization . globalInjections . everyPageBottom ,
@@ -295,7 +345,6 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
295
345
296
346
const announcementBadge : AnnouncementBadgeResponse = this . adminforth . config . customization . announcementBadge ?.( adminUser ) ;
297
347
298
- const loginPromptHTML = await getLoginPromptHTML ( this . adminforth . config . auth . loginPromptHTML ) ;
299
348
300
349
301
350
const publicPart = {
@@ -306,7 +355,6 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
306
355
removeBackgroundBlendMode : this . adminforth . config . auth . removeBackgroundBlendMode ,
307
356
title : this . adminforth . config . customization ?. title ,
308
357
demoCredentials : this . adminforth . config . auth . demoCredentials ,
309
- loginPromptHTML : await tr ( loginPromptHTML , 'system.loginPromptHTML' ) ,
310
358
loginPageInjections : this . adminforth . config . customization . loginPageInjections ,
311
359
rememberMeDays : this . adminforth . config . auth . rememberMeDays ,
312
360
singleTheme : this . adminforth . config . customization . singleTheme ,
@@ -356,12 +404,20 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
356
404
357
405
// strip all backendOnly fields or not described in adminForth fields from dbUser
358
406
// (when user defines column and does not set backendOnly, we assume it is not backendOnly)
359
- Object . keys ( adminUser . dbUser ) . forEach ( ( key ) => {
360
- const col = userResource . columns . find ( ( col ) => col . name === key ) ;
361
- if ( ! col || col . backendOnly ) {
407
+ const ctx = {
408
+ adminUser,
409
+ resource : userResource ,
410
+ meta : { } ,
411
+ source : ActionCheckSource . ShowRequest ,
412
+ adminforth : this . adminforth ,
413
+ } ;
414
+ for ( const key of Object . keys ( adminUser . dbUser ) ) {
415
+ const col = userResource . columns . find ( ( c ) => c . name === key ) ;
416
+ const bo = col ? await isBackendOnly ( col , ctx ) : true ;
417
+ if ( ! col || bo ) {
362
418
delete adminUser . dbUser [ key ] ;
363
419
}
364
- } )
420
+ }
365
421
366
422
return {
367
423
user : userData ,
@@ -794,15 +850,32 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
794
850
} )
795
851
) ;
796
852
853
+ const pkField = resource . columns . find ( ( col ) => col . primaryKey ) ?. name ;
797
854
// remove all columns which are not defined in resources, or defined but backendOnly
798
- data . data . forEach ( ( item ) => {
799
- Object . keys ( item ) . forEach ( ( key ) => {
800
- if ( ! resource . columns . find ( ( col ) => col . name === key ) || resource . columns . find ( ( col ) => col . name === key && col . backendOnly ) ) {
801
- delete item [ key ] ;
855
+ {
856
+ const ctx = {
857
+ adminUser,
858
+ resource,
859
+ meta,
860
+ source : {
861
+ show : ActionCheckSource . ShowRequest ,
862
+ list : ActionCheckSource . ListRequest ,
863
+ edit : ActionCheckSource . EditLoadRequest ,
864
+ } [ source ] ,
865
+ adminforth : this . adminforth ,
866
+ } ;
867
+
868
+ for ( const item of data . data ) {
869
+ for ( const key of Object . keys ( item ) ) {
870
+ const col = resource . columns . find ( ( c ) => c . name === key ) ;
871
+ const bo = col ? await isBackendOnly ( col , ctx ) : true ;
872
+ if ( ! col || bo ) {
873
+ delete item [ key ] ;
874
+ }
802
875
}
803
- } )
804
- item . _label = resource . recordLabel ( item ) ;
805
- } ) ;
876
+ item . _label = resource . recordLabel ( item ) ;
877
+ }
878
+ }
806
879
if ( source === 'list' && resource . options . listTableClickUrl ) {
807
880
await Promise . all (
808
881
data . data . map ( async ( item ) => {
@@ -1068,11 +1141,30 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
1068
1141
}
1069
1142
}
1070
1143
1144
+ const ctxCreate = {
1145
+ adminUser,
1146
+ resource,
1147
+ meta : { requestBody : body } ,
1148
+ source : ActionCheckSource . CreateRequest ,
1149
+ adminforth : this . adminforth ,
1150
+ } ;
1151
+
1152
+ for ( const column of resource . columns ) {
1153
+ if ( ( column . required as { create ?: boolean } ) ?. create ) {
1154
+ const shown = await isShown ( column , 'create' , ctxCreate ) ;
1155
+ if ( shown && record [ column . name ] === undefined ) {
1156
+ return { error : `Column '${ column . name } ' is required` , ok : false } ;
1157
+ }
1158
+ }
1159
+ }
1160
+
1071
1161
for ( const column of resource . columns ) {
1072
1162
const fieldName = column . name ;
1073
1163
if ( fieldName in record ) {
1074
- if ( ! column . showIn ?. create || column . backendOnly ) {
1075
- return { error : `Field "${ fieldName } " cannot be modified as it is restricted from creation (showIn.create is false, please set it to true)` , ok : false } ;
1164
+ const shown = await isShown ( column , 'create' , ctxCreate ) ;
1165
+ const bo = await isBackendOnly ( column , ctxCreate ) ;
1166
+ if ( ! shown || bo ) {
1167
+ 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 } ;
1076
1168
}
1077
1169
}
1078
1170
}
@@ -1164,15 +1256,24 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
1164
1256
return { error : allowedError } ;
1165
1257
}
1166
1258
1259
+ const ctxEdit = {
1260
+ adminUser,
1261
+ resource,
1262
+ meta : { requestBody : body , newRecord : record , oldRecord, pk : recordId } ,
1263
+ source : ActionCheckSource . EditRequest ,
1264
+ adminforth : this . adminforth ,
1265
+ } ;
1266
+
1167
1267
for ( const column of resource . columns ) {
1168
1268
const fieldName = column . name ;
1169
1269
if ( fieldName in record ) {
1170
- if ( ! column . showIn ?. edit || column . editReadonly || column . backendOnly ) {
1171
- return { error : `Field "${ fieldName } " cannot be modified as it is restricted from editing (showIn.edit is false, please set it to true)` , ok : false } ;
1270
+ const shown = await isShown ( column , 'edit' , ctxEdit ) ;
1271
+ const bo = await isBackendOnly ( column , ctxEdit ) ;
1272
+ if ( ! shown || column . editReadonly || bo ) {
1273
+ 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 } ;
1172
1274
}
1173
1275
}
1174
1276
}
1175
-
1176
1277
// for polymorphic foreign resources, we need to find out the value for polymorphicOn column
1177
1278
for ( const column of resource . columns ) {
1178
1279
if ( column . foreignResource ?. polymorphicOn && record [ column . name ] === null ) {
0 commit comments