@@ -92,17 +92,37 @@ def response(data=None, code=200, headers=None, raw=False):
92
92
return flask .current_app .make_response ((data , code , h ))
93
93
94
94
95
- def check_session ():
96
- session = flask .session
97
- if not session :
98
- logger .debug ('check_session: Session is empty' )
95
+ def validate_parent_access (parent_id ):
96
+ cfg = config .load ()
97
+ if cfg .standalone is not False :
98
+ return True
99
+ auth = _parse_auth_header ()
100
+ if not auth :
99
101
return False
100
- if 'from' in session and get_remote_ip () != session ['from' ]:
101
- logger .debug ('check_session: Wrong source ip address' )
102
- session .clear ()
102
+ full_repos_name = auth .get ('repository' , '' ).split ('/' )
103
+ if len (full_repos_name ) != 2 :
104
+ logger .debug ('validate_token: Invalid repository field' )
105
+ return False
106
+ index_endpoint = cfg .index_endpoint
107
+ if index_endpoint is None :
108
+ index_endpoint = 'https://index.docker.io'
109
+ index_endpoint = index_endpoint .strip ('/' )
110
+ url = '{0}/v1/images/{1}/{2}/layer/{3}/access' .format (
111
+ index_endpoint , full_repos_name [0 ], full_repos_name [1 ], parent_id
112
+ )
113
+ headers = {'Authorization' : flask .request .headers .get ('authorization' )}
114
+ resp = requests .get (url , verify = True , headers = headers )
115
+ if resp .status_code != 200 :
116
+ logger .debug ('validate_parent_access: index returns status {0}' .format (
117
+ resp .status_code
118
+ ))
119
+ return False
120
+ try :
121
+ return json .loads (resp .text ).get ('authorized' , False )
122
+
123
+ except json .JSONDecodeError :
124
+ logger .debug ('validate_parent_access: Wrong response format' )
103
125
return False
104
- # Session is valid
105
- return session .get ('auth' ) is True
106
126
107
127
108
128
def validate_token (auth ):
@@ -150,18 +170,23 @@ def is_ssl():
150
170
return False
151
171
152
172
153
- def check_token (args ):
154
- cfg = config .load ()
155
- if cfg .disable_token_auth is True or cfg .standalone is not False :
156
- return True
173
+ def _parse_auth_header ():
157
174
auth = flask .request .headers .get ('authorization' , '' )
158
175
if auth .split (' ' )[0 ].lower () != 'token' :
159
176
logger .debug ('check_token: Invalid token format' )
160
- return False
161
- logger .debug ('args = {0}' .format (args ))
177
+ return None
162
178
logger .debug ('Auth Token = {0}' .format (auth ))
163
179
auth = dict (_re_authorization .findall (auth ))
164
180
logger .debug ('auth = {0}' .format (auth ))
181
+ return auth
182
+
183
+
184
+ def check_token (args ):
185
+ cfg = config .load ()
186
+ if cfg .disable_token_auth is True or cfg .standalone is not False :
187
+ return True
188
+ logger .debug ('args = {0}' .format (args ))
189
+ auth = _parse_auth_header ()
165
190
if not auth :
166
191
return False
167
192
if 'namespace' in args and 'repository' in args :
@@ -190,7 +215,6 @@ def check_token(args):
190
215
# Token is valid, we create a session
191
216
session = flask .session
192
217
session ['repository' ] = auth .get ('repository' )
193
- session ['auth' ] = True
194
218
if is_ssl () is False :
195
219
# We enforce the IP check only when not using SSL
196
220
session ['from' ] = get_remote_ip ()
@@ -232,9 +256,11 @@ def parse_content_signature(s):
232
256
def requires_auth (f ):
233
257
@functools .wraps (f )
234
258
def wrapper (* args , ** kwargs ):
235
- if check_signature () is True or check_session () is True \
236
- or check_token (kwargs ) is True :
237
- return f (* args , ** kwargs )
259
+ session = flask .session
260
+ if check_signature () is True or check_token (kwargs ) is True :
261
+ if 'from' not in session or session ['from' ] == get_remote_ip ():
262
+ return f (* args , ** kwargs )
263
+ session .clear ()
238
264
headers = {'WWW-Authenticate' : 'Token' }
239
265
return api_error ('Requires authorization' , 401 , headers )
240
266
return wrapper
0 commit comments