@@ -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 ) ( 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+
2667export async function interpretResource (
2768 adminUser : AdminUser ,
2869 resource : AdminForthResource ,
@@ -194,6 +235,18 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
194235 } ,
195236 } )
196237
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+
197250 server . endpoint ( {
198251 noAuth : true ,
199252 method : 'GET' ,
@@ -210,8 +263,6 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
210263 const resource = this . adminforth . config . resources . find ( ( res ) => res . resourceId === this . adminforth . config . auth . usersResourceId ) ;
211264 const usernameColumn = resource . columns . find ( ( col ) => col . name === usernameField ) ;
212265
213- const loginPromptHTML = await getLoginPromptHTML ( this . adminforth . config . auth . loginPromptHTML ) ;
214-
215266 return {
216267 brandName : this . adminforth . config . customization . brandName ,
217268 usernameFieldName : usernameColumn . label ,
@@ -220,7 +271,6 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
220271 removeBackgroundBlendMode : this . adminforth . config . auth . removeBackgroundBlendMode ,
221272 title : this . adminforth . config . customization ?. title ,
222273 demoCredentials : this . adminforth . config . auth . demoCredentials ,
223- loginPromptHTML : await tr ( loginPromptHTML , 'system.loginPromptHTML' ) ,
224274 loginPageInjections : this . adminforth . config . customization . loginPageInjections ,
225275 globalInjections : {
226276 everyPageBottom : this . adminforth . config . customization . globalInjections . everyPageBottom ,
@@ -295,7 +345,6 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
295345
296346 const announcementBadge : AnnouncementBadgeResponse = this . adminforth . config . customization . announcementBadge ?.( adminUser ) ;
297347
298- const loginPromptHTML = await getLoginPromptHTML ( this . adminforth . config . auth . loginPromptHTML ) ;
299348
300349
301350 const publicPart = {
@@ -306,7 +355,6 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
306355 removeBackgroundBlendMode : this . adminforth . config . auth . removeBackgroundBlendMode ,
307356 title : this . adminforth . config . customization ?. title ,
308357 demoCredentials : this . adminforth . config . auth . demoCredentials ,
309- loginPromptHTML : await tr ( loginPromptHTML , 'system.loginPromptHTML' ) ,
310358 loginPageInjections : this . adminforth . config . customization . loginPageInjections ,
311359 rememberMeDays : this . adminforth . config . auth . rememberMeDays ,
312360 singleTheme : this . adminforth . config . customization . singleTheme ,
@@ -356,12 +404,20 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
356404
357405 // strip all backendOnly fields or not described in adminForth fields from dbUser
358406 // (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 ) {
362418 delete adminUser . dbUser [ key ] ;
363419 }
364- } )
420+ }
365421
366422 return {
367423 user : userData ,
@@ -794,15 +850,32 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
794850 } )
795851 ) ;
796852
853+ const pkField = resource . columns . find ( ( col ) => col . primaryKey ) ?. name ;
797854 // 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+ }
802875 }
803- } )
804- item . _label = resource . recordLabel ( item ) ;
805- } ) ;
876+ item . _label = resource . recordLabel ( item ) ;
877+ }
878+ }
806879 if ( source === 'list' && resource . options . listTableClickUrl ) {
807880 await Promise . all (
808881 data . data . map ( async ( item ) => {
@@ -1068,11 +1141,30 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
10681141 }
10691142 }
10701143
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+
10711161 for ( const column of resource . columns ) {
10721162 const fieldName = column . name ;
10731163 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 } ;
10761168 }
10771169 }
10781170 }
@@ -1164,15 +1256,24 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
11641256 return { error : allowedError } ;
11651257 }
11661258
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+
11671267 for ( const column of resource . columns ) {
11681268 const fieldName = column . name ;
11691269 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 } ;
11721274 }
11731275 }
11741276 }
1175-
11761277 // for polymorphic foreign resources, we need to find out the value for polymorphicOn column
11771278 for ( const column of resource . columns ) {
11781279 if ( column . foreignResource ?. polymorphicOn && record [ column . name ] === null ) {
0 commit comments