+++ /dev/null
-#!/usr/bin/python
-#-*- coding: utf-8 -*-
-#
-# S.Çağlar Onur <caglar@cs.princeton.edu>
-
-from PLC.Config import Config
-from PLC.Faults import PLCPermissionDenied
-
-from PLC.Nodes import Node, Nodes
-from PLC.Persons import Person, Persons
-from PLC.Sessions import Session, Sessions
-
-from datetime import datetime, timedelta
-
-from pyaspects.meta import MetaAspect
-
-import memcache
-
-import os
-import sys
-import socket
-
-class BaseRateLimit(object):
-
- def __init__(self):
- self.config = Config("/etc/planetlab/plc_config")
-
- # FIXME: change with Config values
- self.prefix = "ratelimit"
- self.minutes = 5 # The time period
- self.requests = 50 # Number of allowed requests in that time period
- self.expire_after = (self.minutes + 1) * 60
-
- self.whitelist = []
-
- def log(self, line):
- log = open("/var/log/plc_api_ratelimit.log", "a")
- date = datetime.now().strftime("%d/%m/%y %H:%M")
- log.write("%s - %s\n" % (date, line))
- log.flush()
-
- def mail(self, to):
- sendmail = os.popen("/usr/sbin/sendmail -N never -t -f%s" % self.config.PLC_MAIL_SUPPORT_ADDRESS, "w")
-
- subject = "[PLCAPI] Maximum allowed number of API calls exceeded"
-
- header = {'from': "%s Support <%s>" % (self.config.PLC_NAME, self.config.PLC_MAIL_SUPPORT_ADDRESS),
- 'to': "%s, %s" % (to, self.config.PLC_MAIL_SUPPORT_ADDRESS),
- 'version': sys.version.split(" ")[0],
- 'subject': subject}
-
- body = "Maximum allowed number of API calls exceeded for the user %s within the last %s minutes." % (to, self.minutes)
-
- # Write headers
- sendmail.write(
-"""
-Content-type: text/plain
-From: %(from)s
-Reply-To: %(from)s
-To: %(to)s
-X-Mailer: Python/%(version)s
-Subject: %(subject)s
-
-""".lstrip() % header)
-
- # Write body
- sendmail.write(body)
- # Done
- sendmail.close()
-
- def before(self, wobj, data, *args, **kwargs):
- # ratelimit_128.112.139.115_201011091532 = 1
- # ratelimit_128.112.139.115_201011091533 = 14
- # ratelimit_128.112.139.115_201011091534 = 11
- # Now, on every request we work out the keys for the past five minutes and use get_multi to retrieve them.
- # If the sum of those counters exceeds the maximum allowed for that time period, we block the request.
-
- api_method_name = wobj.name
- api_method_source = wobj.source
-
- try:
- api_method = args[0]["AuthMethod"]
- except:
- return
-
- # decode api_method_caller
- if api_method == "session":
- api_method_caller = Sessions(wobj.api, {'session_id': args[0]["session"]})
- if api_method_caller == []:
- return
- elif api_method_caller[0]["person_id"] != None:
- api_method_caller = Persons(wobj.api, api_method_caller[0]["person_id"])[0]["email"]
- elif api_method_caller[0]["node_id"] != None:
- api_method_caller = Nodes(wobj.api, api_method_caller[0]["node_id"])[0]["hostname"]
- else:
- api_method_caller = args[0]["session"]
- elif api_method == "password" or api_method == "capability":
- api_method_caller = args[0]["Username"]
- elif api_method == "gpg":
- api_method_caller = args[0]["name"]
- elif api_method == "hmac" or api_method == "hmac_dummybox":
- api_method_caller = args[0]["node_id"]
- elif api_method == "anonymous":
- api_method_caller = "anonymous"
- else:
- api_method_caller = "unknown"
-
- # excludes
- if api_method_source == None or api_method_source[0] == socket.gethostbyname(self.config.PLC_API_HOST) or api_method_source[0] in self.whitelist:
- return
-
- # sanity check
- if api_method_caller == None:
- self.log("%s called from %s with Username = None?" % (api_method_name, api_method_source[0]))
- return
-
- # normalize unicode string otherwise memcache throws an exception
- api_method_caller = str(api_method_caller)
-
- mc = memcache.Client(["%s:11211" % self.config.PLC_API_HOST])
- now = datetime.now()
-
- current_key = "%s_%s_%s_%s" % (self.prefix, api_method_caller, api_method_source[0], now.strftime("%Y%m%d%H%M"))
- keys_to_check = ["%s_%s_%s_%s" % (self.prefix, api_method_caller, api_method_source[0], (now - timedelta(minutes = minute)).strftime("%Y%m%d%H%M")) for minute in range(self.minutes + 1)]
-
- try:
- value = mc.incr(current_key)
- except ValueError:
- value = None
-
- if value == None:
- mc.set(current_key, 1, time=self.expire_after)
-
- results = mc.get_multi(keys_to_check)
- total_requests = 0
- for i in results:
- total_requests += results[i]
-
- if total_requests > self.requests:
- self.log("%s - %s" % (api_method_source[0], api_method_caller))
-
- caller_key = "%s_%s" % (self.prefix, api_method_caller)
- if mc.get(caller_key) == None:
- mc.set(caller_key, 1, time = self.expire_after)
- if (api_method == "session" and api_method_caller.__contains__("@")) or (api_method == "password" or api_method == "capability"):
- self.mail(api_method_caller)
-
- raise PLCPermissionDenied, "Maximum allowed number of API calls exceeded"
-
- def after(self, wobj, data, *args, **kwargs):
- return
-
-class RateLimitAspect_class(BaseRateLimit):
- __metaclass__ = MetaAspect
- name = "ratelimitaspect_class"
-
- def __init__(self):
- BaseRateLimit.__init__(self)
-
- def before(self, wobj, data, *args, **kwargs):
- BaseRateLimit.before(self, wobj, data, *args, **kwargs)
-
- def after(self, wobj, data, *args, **kwargs):
- BaseRateLimit.after(self, wobj, data, *args, **kwargs)
-
-RateLimitAspect = RateLimitAspect_class
#Requires: python-simplejson
# for the RebootNodeWithPCU method
Requires: pcucontrol >= 1.0-6
-# for OMF integration
-Requires: pyaspects >= 0.4
-# again, these are no longer available in f29
-# Requires: python-twisted-words
-# Requires: python-twisted-web
-# ldap
-Requires: python-ldap
# for memcache
Requires: memcached python-memcached
### avoid having yum complain about updates, as stuff is moving around
# python-pycurl and python-psycopg2 avail. from fedora 5
# we used to ship our own version of psycopg2 and pycurl, for fedora4
# starting with 4.3, support for these two modules is taken out
-#
-# Build __init__.py metafiles and PHP API.
+#
+# Build __init__.py metafiles and PHP API.
%{__make} %{?_smp_mflags}
%{__make} -C wsdl
* Fri May 09 2008 Thierry Parmentelat <thierry.parmentelat@sophia.inria.fr> - PLCAPI-4.2-7
- no more doc packaged outside of myplc-docs - doc/ cleaned up
-- enhancements in doc on filters
+- enhancements in doc on filters
- bootcd-aware GetBootMedium merged from onelab
* Thu May 08 2008 Thierry Parmentelat <thierry.parmentelat@sophia.inria.fr> - PLCAPI-4.2-6
- checkpoint while the new myplc-docs package is underway
- bugfix: GetSlivers & conf files
-- doc: removed target files
+- doc: removed target files
* Wed Apr 23 2008 Stephen Soltesz <soltesz@cs.princeton.edu> - PLCAPI-4.2-5
- Removed conditions on the persons, site, and nodes indexes. previsouly only
- the non-deleted fields were index, resulting in massivly slow queries.
--
+-
* Wed Mar 26 2008 Thierry Parmentelat <thierry.parmentelat@sophia.inria.fr> - PLCAPI-4.2-3 PLCAPI-4.2-4
- plcsh: better handling of options when running as a shell script
- tweaks for accepted args in GetPCUTypes and BootNotifyOwners
* Thu Feb 14 2008 Thierry Parmentelat <thierry.parmentelat@sophia.inria.fr> - PLCAPI-4.2-2 PLCAPI-4.2-3
-- GetBootMedium support for build.sh full options, incl. serial & console_spec
+- GetBootMedium support for build.sh full options, incl. serial & console_spec
- GetBootMedium simpler, cleaner and safer use of tmpdirs in (dated from bootcustom.sh)
* Fri Feb 01 2008 Thierry Parmentelat <thierry.parmentelat@sophia.inria.fr> - PLCAPI-4.2-1 PLCAPI-4.2-2
- plcsh adds its own path to sys.path
- fix so GetNodes can be called from a Node
-* Fri Oct 27 2006 Mark Huang <mlhuang@CS.Princeton.EDU> -
+* Fri Oct 27 2006 Mark Huang <mlhuang@CS.Princeton.EDU> -
- Initial build.
%define module_current_branch 4.3