Package backend :: Package server :: Package handlers :: Package xmlrpc :: Module proxy
[hide private]
[frames] | no frames]

Source Code for Module backend.server.handlers.xmlrpc.proxy

  1  # 
  2  # Copyright (c) 2008--2015 Red Hat, Inc. 
  3  # 
  4  # This software is licensed to you under the GNU General Public License, 
  5  # version 2 (GPLv2). There is NO WARRANTY for this software, express or 
  6  # implied, including the implied warranties of MERCHANTABILITY or FITNESS 
  7  # FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 
  8  # along with this software; if not, see 
  9  # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. 
 10  # 
 11  # Red Hat trademarks are not licensed under GPLv2. No permission is 
 12  # granted to use or replicate Red Hat trademarks that are incorporated 
 13  # in this software or its documentation. 
 14  # 
 15   
 16  # system module import 
 17  import time 
 18   
 19  # common module imports 
 20  from rhn.UserDictCase import UserDictCase 
 21  from spacewalk.common import rhnFlags 
 22  from spacewalk.common.rhnLog import log_debug, log_error 
 23  from spacewalk.common.rhnConfig import CFG 
 24  from spacewalk.common.rhnException import rhnFault 
 25  from spacewalk.common.rhnTranslate import _ 
 26   
 27  # local module imports 
 28  from spacewalk.server.rhnLib import computeSignature 
 29  from spacewalk.server.rhnHandler import rhnHandler 
 30  from spacewalk.server import rhnServer, rhnSQL, apacheAuth, rhnPackage, rhnChannel 
 31   
 32  # a class that provides additional authentication support for the 
 33  # proxy functions 
 34   
 35   
36 -class rhnProxyHandler(rhnHandler):
37
38 - def __init__(self):
39 rhnHandler.__init__(self)
40
41 - def auth_system(self, system_id):
42 """ System authentication. We override the standard function because 43 we need to check additionally if this system_id is entitled for 44 proxy functionality. 45 """ 46 log_debug(3) 47 server = rhnHandler.auth_system(self, system_id) 48 # if it did not blow up, we have a valid server. Check proxy 49 # entitlement. 50 # XXX: this needs to be moved out of the rhnServer module, 51 # possibly in here 52 h = rhnSQL.prepare(""" 53 select 1 54 from rhnProxyInfo pi 55 where pi.server_id = :server_id 56 """) 57 h.execute(server_id=self.server_id) 58 row = h.fetchone_dict() 59 if not row: 60 # we require entitlement for this functionality 61 log_error("Server not entitled for Proxy", self.server_id) 62 raise rhnFault(1002, _( 63 'Spacewalk Proxy service not enabled for server profile: "%s"') 64 % server.server["name"]) 65 # we're fine... 66 return server
67
68 - def auth_client(self, token):
69 """ Authenticate a system based on the same authentication tokens 70 the client is sending for GET requests 71 """ 72 log_debug(3) 73 # Build a UserDictCase out of the token 74 dict = UserDictCase(token) 75 # Set rhnFlags so that we can piggyback on apacheAuth's auth_client 76 rhnFlags.set('AUTH_SESSION_TOKEN', dict) 77 78 # XXX To clean up apacheAuth.auth_client's logging, this is not about 79 # GET requests 80 result = apacheAuth.auth_client() 81 82 if not result: 83 raise rhnFault(33, _("Invalid session key")) 84 85 log_debug(4, "Client auth OK") 86 # We checked it already, so we're sure it's there 87 client_id = dict['X-RHN-Server-Id'] 88 89 server = rhnServer.search(client_id) 90 if not server: 91 raise rhnFault(8, _("This server ID no longer exists")) 92 # XXX: should we check if the username still has access to it? 93 # probably not, because there is no known good way we can 94 # update the server system_id on the client side when 95 # permissions change... Damn it. --gafton 96 self.server = server 97 self.server_id = client_id 98 self.user = dict['X-RHN-Auth-User-Id'] 99 return server
100 101
102 -class Proxy(rhnProxyHandler):
103 104 """ this is the XML-RPC receiver for proxy calls """ 105
106 - def __init__(self):
107 log_debug(3) 108 rhnProxyHandler.__init__(self) 109 self.functions.append('package_source_in_channel') 110 self.functions.append('login') 111 self.functions.append('listAllPackagesKickstart') 112 self.functions.append('getKickstartChannel') 113 self.functions.append('getKickstartOrgChannel') 114 self.functions.append('getKickstartSessionChannel') 115 self.functions.append('getKickstartChildChannel') 116 self.functions.append('getTinyUrlChannel') 117 self.functions.append('checkTokenValidity')
118 119 # Method to force a check of the client's auth token. 120 # Proxy may call this if it does not recognize the token, which may 121 # happen if the proxy is load-balanced.
122 - def checkTokenValidity(self, token, systemid):
123 log_debug(5, token, systemid) 124 # authenticate that this request is initiated from a proxy 125 try: 126 self.auth_system(systemid) 127 server = self.auth_client(token) # sets self.server_id 128 except rhnFault: 129 # A Fault means that something did not auth. Either the caller 130 # is not a proxy or the token is not valid, return false. 131 return False 132 # Proxy has to calculate new proxy-clock-skew, and needs channel info 133 ret = {} 134 ret['X-RHN-Auth-Server-Time'] = str(time.time()) 135 channels = rhnChannel.getSubscribedChannels(self.server_id) 136 ret['X-RHN-Auth-Channels'] = channels 137 return ret
138
139 - def package_source_in_channel(self, package, channel, auth_token):
140 """ Validates the client request for a source package download """ 141 log_debug(3, package, channel) 142 server = self.auth_client(auth_token) 143 return rhnPackage.package_source_in_channel(self.server_id, 144 package, channel)
145
146 - def login(self, system_id):
147 """ Login routine for the proxy 148 149 Return a formatted string of session token information as regards 150 an Spacewalk Proxy. Also sets this information in the headers. 151 152 NOTE: design description for the auth token format and how it is 153 is used is well documented in the proxy/broker/rhnProxyAuth.py 154 code. 155 """ 156 log_debug(5, system_id) 157 # Authenticate. We need the user record to be able to generate 158 # auth tokens 159 self.load_user = 1 160 self.auth_system(system_id) 161 # log the entry 162 log_debug(1, self.server_id) 163 rhnServerTime = str(time.time()) 164 expireOffset = str(CFG.PROXY_AUTH_TIMEOUT) 165 signature = computeSignature(CFG.SECRET_KEY, self.server_id, self.user, 166 rhnServerTime, expireOffset) 167 168 token = '%s:%s:%s:%s:%s' % (self.server_id, self.user, rhnServerTime, 169 expireOffset, signature) 170 171 # NOTE: for RHN Proxies of version 3.1+ tokens are passed up in a 172 # multi-valued header with HOSTNAME tagged onto the end of the 173 # token, so, it looks something like this: 174 # x-rhn-proxy-auth: 'TOKEN1:HOSTNAME1,TOKEN2:HOSTNAME2' 175 # This note is only that -- a "heads up" -- in case anyone gets 176 # confused. 177 178 # Push this value into the headers so that the proxy can 179 # intercept and cache it without parsing the xmlrpc. 180 transport = rhnFlags.get('outputTransportOptions') 181 transport['X-RHN-Action'] = 'login' 182 transport['X-RHN-Proxy-Auth'] = token 183 return token
184
185 - def listAllPackagesKickstart(self, channel, system_id):
186 """ Creates and/or serves up a cached copy of all the packages for 187 this channel, including checksum information. 188 """ 189 log_debug(5, channel) 190 # authenticate that this request is initiated from a proxy 191 self.auth_system(system_id) 192 193 packages = rhnChannel.list_all_packages_checksum(channel) 194 195 # transport options... 196 rhnFlags.set("compress_response", 1) 197 return packages
198
199 - def getKickstartChannel(self, kickstart, system_id):
200 """ Gets channel information for this kickstart tree""" 201 log_debug(5, kickstart) 202 # authenticate that this request is initiated from a proxy 203 self.auth_system(system_id) 204 return self.__getKickstartChannel(kickstart)
205
206 - def getKickstartOrgChannel(self, kickstart, org_id, system_id):
207 """ Gets channel information for this kickstart tree""" 208 log_debug(5, kickstart, org_id) 209 # authenticate that this request is initiated from a proxy 210 self.auth_system(system_id) 211 ret = rhnChannel.getChannelInfoForKickstartOrg(kickstart, org_id) 212 return self.__getKickstart(kickstart, ret)
213
214 - def getKickstartSessionChannel(self, kickstart, session, system_id):
215 """ Gets channel information for this kickstart tree""" 216 log_debug(5, kickstart, session) 217 # authenticate that this request is initiated from a proxy 218 self.auth_system(system_id) 219 return self.__getKickstartSessionChannel(kickstart, session)
220
221 - def getKickstartChildChannel(self, kickstart, child, system_id):
222 """ Gets channel information for this kickstart tree""" 223 log_debug(5, kickstart, child) 224 # authenticate that this request is initiated from a proxy 225 self.auth_system(system_id) 226 if (hasattr(CFG, 'KS_RESTRICT_CHILD_CHANNELS') and 227 CFG.KS_RESTRICT_CHILD_CHANNELS): 228 return getKickstartChannel(kickstart) 229 230 ret = rhnChannel.getChildChannelInfoForKickstart(kickstart, child) 231 return self.__getKickstart(kickstart, ret)
232
233 - def getTinyUrlChannel(self, tinyurl, system_id):
234 """ Gets channel information for this tinyurl""" 235 log_debug(5, tinyurl) 236 # authenticate that this request is initiated from a proxy 237 self.auth_system(system_id) 238 ret = rhnChannel.getChannelInfoForTinyUrl(tinyurl) 239 if not ret or not 'url' in ret or len(ret['url'].split('/')) != 6: 240 raise rhnFault(40, 241 "could not find any data on tiny url '%s'" % tinyurl) 242 243 # tiny urls are always for kickstart sessions 244 args = ret['url'].split('/') 245 return self.__getKickstartSessionChannel(args[-1], args[-2])
246 247 248 #----------------------------------------------------------------------------- 249
250 - def __getKickstartChannel(self, kickstart):
253
254 - def __getKickstartSessionChannel(self, kickstart, session):
255 ret = rhnChannel.getChannelInfoForKickstartSession(session) 256 257 if not ret: 258 return self.__getKickstartChannel(kickstart) 259 return self.__getKickstart(kickstart, ret)
260
261 - def __getKickstart(self, kickstart, ret):
262 if not ret: 263 raise rhnFault(40, 264 "could not find any data on kickstart '%s'" % kickstart) 265 return ret
266