1+ # Copyright 2026 Genesis Corporation.
2+ #
3+ # All Rights Reserved.
4+ #
5+ # Licensed under the Apache License, Version 2.0 (the "License"); you may
6+ # not use this file except in compliance with the License. You may obtain
7+ # a copy of the License at
8+ #
9+ # http://www.apache.org/licenses/LICENSE-2.0
10+ #
11+ # Unless required by applicable law or agreed to in writing, software
12+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14+ # License for the specific language governing permissions and limitations
15+ # under the License.
16+
17+ from oslo_config import cfg
18+ from webob import dec
19+
20+ from restalchemy .api import middlewares
21+
22+ ALLOWED_ORIGINS_OPT = cfg .ListOpt (
23+ "allowed_origins" ,
24+ default = ["*" ],
25+ help = "List of allowed CORS origins" ,
26+ )
27+
28+ CORS_OPT_GROUP = cfg .OptGroup ("cors" )
29+ CORS_OPTS = [ALLOWED_ORIGINS_OPT ]
30+ BASE_RESPONSE_HEADERS = {
31+ "Access-Control-Allow-Credentials" : "true" ,
32+ "Access-Control-Allow-Methods" : "GET, POST, PUT, PATCH, DELETE, OPTIONS" ,
33+ "Access-Control-Allow-Headers" : (
34+ "Authorization, Content-Type, X-OTP-Token, X-Requested-With, "
35+ "Accept, Origin"
36+ ),
37+ "Access-Control-Max-Age" : "3600" ,
38+ }
39+
40+
41+ def register_cors_opts (conf ):
42+ conf .register_group (CORS_OPT_GROUP )
43+ conf .register_opts (CORS_OPTS , group = CORS_OPT_GROUP )
44+
45+
46+ class CORSMiddleware (middlewares .Middleware ):
47+
48+ def __init__ (self , application , allowed_origins = None ):
49+ super ().__init__ (application )
50+ self .allowed_origins = allowed_origins or []
51+
52+ @dec .wsgify
53+ def __call__ (self , req ):
54+ origin = req .headers .get ("Origin" , "" )
55+
56+ if req .method == "OPTIONS" and self ._is_origin_allowed (origin ):
57+ return req .ResponseClass (
58+ status = 200 ,
59+ headers = self ._cors_headers (origin ),
60+ )
61+
62+ response = req .get_response (self .application )
63+
64+ if self ._is_origin_allowed (origin ):
65+ for key , value in self ._cors_headers (origin ).items ():
66+ response .headers .add (key , value )
67+
68+ return response
69+
70+ def _is_origin_allowed (self , origin ):
71+ if not origin :
72+ return False
73+ return origin in self .allowed_origins or "*" in self .allowed_origins
74+
75+ @staticmethod
76+ def _cors_headers (origin ):
77+ headers = BASE_RESPONSE_HEADERS .copy ()
78+ headers ["Access-Control-Allow-Origin" ] = origin
79+ return headers
0 commit comments