1+ using System ;
2+ using System . Globalization ;
3+ using System . Linq ;
4+ using System . Net ;
5+ using System . Net . Http ;
6+ using System . Text ;
7+ using GeneXus . Application ;
8+ using GeneXus . Configuration ;
9+ using GeneXus . Data ;
10+ using GeneXus . Http ;
11+ using GeneXus . Metadata ;
12+ using GeneXus . Security ;
13+ using Microsoft . AspNetCore . Http ;
14+ using Microsoft . AspNetCore . Mvc ;
15+
16+ namespace GeneXus . Utils
17+ {
18+ public class GxRestService : ControllerBase
19+ {
20+ static readonly IGXLogger log = GXLoggerFactory . GetLogger < GxRestService > ( ) ;
21+
22+ internal const string WARNING_HEADER = "Warning" ;
23+ protected IGxContext context ;
24+ protected string permissionPrefix ;
25+ protected string permissionMethod ;
26+ bool runAsMain = true ;
27+
28+ protected GxRestService ( )
29+ {
30+ context = GxContext . CreateDefaultInstance ( ) ;
31+ if ( GXUtil . CompressResponse ( ) )
32+ GXUtil . SetGZip ( HttpContext ) ;
33+ }
34+ protected void Cleanup ( )
35+ {
36+ ServiceHeaders ( ) ;
37+ if ( runAsMain )
38+ context . CloseConnections ( ) ;
39+ }
40+ protected bool RunAsMain
41+ {
42+ get { return runAsMain ; }
43+ set { runAsMain = value ; }
44+ }
45+ //Convert GxUnknownObjectCollection of Object[] to GxUnknownObjectCollection of GxSimpleCollections
46+ protected GxUnknownObjectCollection TableHashList ( GxUnknownObjectCollection tableHashList )
47+ {
48+ GxUnknownObjectCollection result = new GxUnknownObjectCollection ( ) ;
49+ if ( tableHashList != null && tableHashList . Count > 0 )
50+ {
51+ foreach ( object [ ] list in tableHashList )
52+ {
53+ GxStringCollection tableHash = new GxStringCollection ( ) ;
54+ foreach ( string data in list )
55+ {
56+ tableHash . Add ( data ) ;
57+ }
58+ result . Add ( tableHash ) ;
59+ }
60+ }
61+ return result ;
62+ }
63+
64+ protected string EmptyParm ( string parmValue )
65+ {
66+ if ( string . IsNullOrEmpty ( parmValue ) || parmValue . Equals ( "gxempty" , StringComparison . OrdinalIgnoreCase ) )
67+ return string . Empty ;
68+ else
69+ return parmValue ;
70+ }
71+ protected string RestStringParameter ( string parameterName , string parameterValue )
72+ {
73+ try
74+ {
75+ if ( HttpContext . Request . Query . TryGetValue ( parameterName , out var value ) )
76+ return value . FirstOrDefault ( ) ;
77+ else
78+ return parameterValue ;
79+ }
80+ catch ( Exception )
81+ {
82+ return parameterValue ;
83+ }
84+ }
85+ protected bool RestParameter ( string parameterName , string parameterValue )
86+ {
87+ try
88+ {
89+ if ( HttpContext . Request . Query . TryGetValue ( parameterName , out var value ) )
90+ return value . FirstOrDefault ( ) . Equals ( parameterValue , StringComparison . OrdinalIgnoreCase ) ;
91+ return false ;
92+ }
93+ catch ( Exception )
94+ {
95+ return false ;
96+ }
97+ }
98+ /*public void UploadImpl(Stream stream)
99+ {
100+ GXObjectUploadServices gxobject = new GXObjectUploadServices(context);
101+ IncomingWebRequestContext request = WebOperationContext.Current.IncomingRequest;
102+ gxobject.WcfExecute(stream, request.ContentType, request.ContentLength, request.Headers[HttpHeader.XGXFILENAME]);
103+ }*/
104+ protected void ErrorCheck ( IGxSilentTrn trn )
105+ {
106+ if ( trn . Errors ( ) == 1 )
107+ {
108+ msglist msg = trn . GetMessages ( ) ;
109+ if ( msg . Count > 0 )
110+ {
111+ msglistItem msgItem = ( msglistItem ) msg [ 0 ] ;
112+ if ( msgItem . gxTpr_Id . Contains ( "NotFound" ) )
113+ HttpHelper . SetError ( HttpContext , HttpStatusCode . NotFound . ToString ( HttpHelper . INT_FORMAT ) , msgItem . gxTpr_Description ) ;
114+ else if ( msgItem . gxTpr_Id . Contains ( "WasChanged" ) )
115+ HttpHelper . SetError ( HttpContext , HttpStatusCode . Conflict . ToString ( HttpHelper . INT_FORMAT ) , msgItem . gxTpr_Description ) ;
116+ else
117+ HttpHelper . SetError ( HttpContext , HttpStatusCode . BadRequest . ToString ( HttpHelper . INT_FORMAT ) , msgItem . gxTpr_Description ) ;
118+ }
119+ }
120+
121+ }
122+ protected void SetMessages ( msglist messages )
123+ {
124+ StringBuilder header = new StringBuilder ( ) ;
125+ bool emptyHeader = true , encoded = false ;
126+ const string SystemMsg = "System" ;
127+ const string UserMsg = "User" ;
128+ string typeMsg ;
129+
130+ foreach ( msglistItem msg in messages )
131+ {
132+ if ( msg . gxTpr_Type == 0 )
133+ {
134+ string value = msg . gxTpr_Description ;
135+ encoded = false ;
136+ if ( GXUtil . ContainsNoAsciiCharacter ( value ) )
137+ {
138+ value = GXUtil . UrlEncode ( value ) ;
139+ encoded = true ;
140+ }
141+ typeMsg = msg . IsGxMessage ? SystemMsg : UserMsg ;
142+
143+ header . AppendFormat ( "{0}299 {1} \" {2}{3}:{4}\" " , emptyHeader ? string . Empty : "," , context . GetServerName ( ) , encoded ? GxRestPrefix . ENCODED_PREFIX : string . Empty , typeMsg , value ) ;
144+ if ( emptyHeader )
145+ {
146+ emptyHeader = false ;
147+ }
148+ }
149+ }
150+ if ( ! emptyHeader )
151+ {
152+ AddHeader ( WARNING_HEADER , StringUtil . Sanitize ( header . ToString ( ) , StringUtil . HttpHeaderWhiteList ) ) ;
153+ }
154+ }
155+ protected void SetError ( string code , string message )
156+ {
157+ HttpHelper . SetError ( HttpContext , code , message ) ;
158+ }
159+ protected void WebException ( Exception ex )
160+ {
161+ GXLogging . Error ( log , "Failed to complete execution of Rest Service:" , ex ) ;
162+
163+ if ( ex is FormatException )
164+ {
165+ HttpHelper . SetUnexpectedError ( HttpContext , HttpStatusCode . BadRequest , ex ) ;
166+ }
167+ else
168+ {
169+ HttpHelper . SetUnexpectedError ( HttpContext , HttpStatusCode . InternalServerError , ex ) ;
170+ }
171+ }
172+
173+ protected bool IsAuthenticated ( string synchronizer )
174+ {
175+ GXLogging . Debug ( log , "IsMainAuthenticated synchronizer:" + synchronizer ) ;
176+ bool validSynchronizer = false ;
177+ try
178+ {
179+ if ( ! string . IsNullOrEmpty ( synchronizer ) )
180+ {
181+ string nspace ;
182+ if ( ! Config . GetValueOf ( "AppMainNamespace" , out nspace ) )
183+ nspace = "GeneXus.Programs" ;
184+ string assemblyName = synchronizer . ToLower ( ) ;
185+ string restServiceName = nspace + "." + assemblyName + "_services" ;
186+ GxRestService synchronizerService = ( GxRestService ) ClassLoader . GetInstance ( assemblyName , restServiceName , null ) ;
187+ if ( synchronizerService != null && synchronizerService . IsSynchronizer )
188+ {
189+ validSynchronizer = true ;
190+ return IsAuthenticated ( synchronizerService . IntegratedSecurityLevel , synchronizerService . IntegratedSecurityEnabled , synchronizerService . ExecutePermissionPrefix ) ;
191+ }
192+ }
193+ return false ;
194+ }
195+ catch ( Exception ex )
196+ {
197+ GXLogging . Error ( log , ex , "IsMainAuthenticated error " ) ;
198+ return false ;
199+ }
200+ finally
201+ {
202+ if ( ! validSynchronizer )
203+ HttpHelper . SetError ( HttpContext , "0" , "Invalid Synchronizer " + synchronizer ) ;
204+ }
205+ }
206+ protected bool IsAuthenticated ( )
207+ {
208+ return IsAuthenticated ( IntegratedSecurityLevel , IntegratedSecurityEnabled , permissionPrefix ) ;
209+ }
210+ private bool IsAuthenticated ( GAMSecurityLevel objIntegratedSecurityLevel , bool objIntegratedSecurityEnabled , string objPermissionPrefix )
211+ {
212+ if ( ! objIntegratedSecurityEnabled )
213+ {
214+ return true ;
215+ }
216+ else {
217+ String token = GetHeader ( "Authorization" ) ;
218+ if ( token == null )
219+ {
220+ HttpHelper . SetError ( HttpContext , "0" , "This service needs an Authorization Header" ) ;
221+ return false ;
222+ }
223+ else
224+ {
225+ token = token . Replace ( "OAuth " , "" ) ;
226+ if ( objIntegratedSecurityLevel == GAMSecurityLevel . SecurityLow )
227+ {
228+ bool isOK ;
229+ GxResult result = GxSecurityProvider . Provider . checkaccesstoken ( context , token , out isOK ) ;
230+ if ( ! isOK )
231+ {
232+ HttpHelper . SetGamError ( HttpContext , result . Code , result . Description ) ;
233+ return false ;
234+ }
235+ }
236+ else if ( objIntegratedSecurityLevel == GAMSecurityLevel . SecurityHigh )
237+ {
238+ bool sessionOk , permissionOk ;
239+ GxResult result = GxSecurityProvider . Provider . checkaccesstokenprm ( context , token , objPermissionPrefix , out sessionOk , out permissionOk ) ;
240+ if ( permissionOk )
241+ {
242+ return true ;
243+ }
244+ else
245+ {
246+ HttpHelper . SetGamError ( HttpContext , result . Code , result . Description ) ;
247+ if ( sessionOk )
248+ {
249+ SetStatusCode ( HttpStatusCode . Forbidden ) ;
250+ }
251+ else
252+ {
253+ AddHeader ( HttpHeader . AUTHENTICATE_HEADER , StringUtil . Sanitize ( HttpHelper . OatuhUnauthorizedHeader ( context . GetServerName ( ) , result . Code , result . Description ) , StringUtil . HttpHeaderWhiteList ) ) ;
254+ SetStatusCode ( HttpStatusCode . Unauthorized ) ;
255+ }
256+ return false ;
257+ }
258+ }
259+ }
260+ return true ;
261+ }
262+ }
263+ protected void SetStatusCode ( HttpStatusCode code )
264+ {
265+ if ( HttpContext != null )
266+ {
267+ HttpContext . Response . StatusCode = ( int ) code ;
268+ }
269+ }
270+ IHeaderDictionary GetHeaders ( )
271+ {
272+ if ( HttpContext != null )
273+ {
274+ return HttpContext . Request . Headers ;
275+ }
276+ else return null ;
277+ }
278+ string GetHeader ( string header )
279+ {
280+ if ( HttpContext != null )
281+ {
282+ return HttpContext . Request . Headers [ header ] ;
283+ }
284+ else return null ;
285+ }
286+ bool IsPost ( )
287+ {
288+ if ( HttpContext != null )
289+ {
290+ return HttpMethod . Post . Method == HttpContext . Request . GetMethod ( ) ;
291+ }
292+ else return false ;
293+ }
294+ void AddHeader ( string header , string value )
295+ {
296+ if ( HttpContext != null )
297+ {
298+ HttpContext . Response . Headers [ header ] = value ;
299+ }
300+ }
301+ protected bool ProcessHeaders ( string queryId )
302+ {
303+
304+ IHeaderDictionary headers = GetHeaders ( ) ;
305+ String language = null , theme = null , etag = null ;
306+ if ( headers != null )
307+ {
308+ language = headers [ "GeneXus-Language" ] ;
309+ theme = headers [ "GeneXus-Theme" ] ;
310+ if ( ! IsPost ( ) )
311+ etag = headers [ "If-Modified-Since" ] ;
312+ }
313+
314+ if ( ! string . IsNullOrEmpty ( language ) )
315+ context . SetLanguage ( language ) ;
316+
317+ if ( ! string . IsNullOrEmpty ( theme ) )
318+ context . SetTheme ( theme ) ;
319+
320+ DateTime dt = HTMLDateToDatetime ( etag ) ;
321+ DateTime newDt ;
322+ DataUpdateStatus status ;
323+ if ( etag == null )
324+ {
325+ status = DataUpdateStatus . Invalid ;
326+ GxSmartCacheProvider . CheckDataStatus ( queryId , dt , out newDt ) ;
327+ }
328+ else
329+ {
330+ status = GxSmartCacheProvider . CheckDataStatus ( queryId , dt , out newDt ) ;
331+ }
332+ AddHeader ( "Last-Modified" , dateTimeToHTMLDate ( newDt ) ) ;
333+
334+ if ( status == DataUpdateStatus . UpToDate )
335+ {
336+ SetStatusCode ( HttpStatusCode . NotModified ) ;
337+ return false ;
338+ }
339+ return true ;
340+ }
341+
342+
343+
344+ private void SendCacheHeaders ( )
345+ {
346+ if ( string . IsNullOrEmpty ( context . GetHeader ( HttpHeader . CACHE_CONTROL ) ) )
347+ AddHeader ( "Cache-Control" , HttpHelper . CACHE_CONTROL_HEADER_NO_CACHE ) ;
348+ }
349+ private void ServiceHeaders ( )
350+ {
351+ SendCacheHeaders ( ) ;
352+ if ( HttpContext != null )
353+ {
354+ HttpHelper . CorsHeaders ( HttpContext ) ;
355+ }
356+
357+ }
358+ DateTime HTMLDateToDatetime ( string s )
359+ {
360+ // Date Format: RFC 1123
361+ DateTime dt ;
362+ if ( DateTime . TryParse ( s , DateTimeFormatInfo . InvariantInfo , System . Globalization . DateTimeStyles . AdjustToUniversal , out dt ) )
363+ return dt ;
364+ return DateTime . MinValue ;
365+ }
366+ string dateTimeToHTMLDate ( DateTime dt )
367+ {
368+ return dt . ToUniversalTime ( ) . ToString ( DateTimeFormatInfo . InvariantInfo . RFC1123Pattern , DateTimeFormatInfo . InvariantInfo ) ;
369+ }
370+ protected virtual bool IsSynchronizer { get { return false ; } }
371+ protected virtual bool IntegratedSecurityEnabled { get { return false ; } }
372+ protected virtual GAMSecurityLevel IntegratedSecurityLevel { get { return 0 ; } }
373+ protected virtual string ExecutePermissionPrefix { get { return string . Empty ; } }
374+ }
375+ }
0 commit comments