def _check_GET_permission(self): GET = self.setting.get("GET") if not GET: raise UliwebError("'%s' not accessible by apijson"%(self.name)) roles = GET.get("roles") params_role = self.params.get("@role") if not params_role: if hasattr(request,"user"): params_role = "LOGIN" else: params_role = "UNKNOWN" elif params_role != "UNKNOWN": if not hasattr(request,"user"): raise UliwebError("no login user for role '%s'"%(params_role)) if params_role not in roles: raise UliwebError("'%s' not accessible by role '%s'"%(self.name,params_role)) if params_role == "UNKNOWN": self.permission_check_ok = True elif functions.has_role(request.user,params_role): self.permission_check_ok = True else: raise UliwebError("user doesn't have role '%s'"%(params_role)) if not self.permission_check_ok: raise UliwebError("no permission") self.params_role = params_role
def _get_array_params(self): query_count = self.query_params.get("@count") if query_count: try: query_count = int(query_count) except ValueError as e: log.error("bad param in '%s': '%s'" % (query_count, self.query_params)) raise UliwebError("@count should be an int, but get '%s'" % (query_count)) self.query_count = query_count query_page = self.query_params.get("@page") if query_page: #@page begin from 0 try: query_page = int(query_page) except ValueError as e: log.error("bad param in '%s': '%s'" % (query_page, self.query_params)) raise UliwebError("@page should be an int, but get '%s'" % (query_page)) if query_page < 0: raise UliwebError("page should >0, but get '%s'" % (query_page)) self.query_page = query_page #https://github.com/TommyLemon/APIJSON/blob/master/Document.md#32-%E5%8A%9F%E8%83%BD%E7%AC%A6 query_type = self.query_params.get("@query", 0) if query_type not in [0, 1, 2]: raise UliwebError("bad param 'query': %s" % (query_type)) self.query_type = query_type #order not in query params but in model params self.order = self.params.get("@order")
def _check_GET_permission(self): GET = self.setting.get("GET") if not GET: raise UliwebError("'%s' not accessible by apijson" % (self.name)) roles = GET.get("roles") params_role = self.params.get("@role") user = getattr(request, "user", None) if roles: if not params_role: if user: params_role = "LOGIN" else: params_role = "UNKNOWN" elif params_role != "UNKNOWN": if not user: raise UliwebError("no login user for role '%s'" % (params_role)) if params_role not in roles: raise UliwebError("'%s' not accessible by role '%s'" % (self.name, params_role)) if params_role == "UNKNOWN": self.permission_check_ok = True elif functions.has_role(user, params_role): self.permission_check_ok = True else: raise UliwebError("user doesn't have role '%s'" % (params_role)) if not self.permission_check_ok: perms = GET.get("permissions") if perms: if params_role: role, msg = functions.has_permission_as_role( user, params_role, *perms) if role: self.permission_check_ok = True else: role = functions.has_permission(user, *perms) if role: role_name = getattr(role, "name") if role_name: self.permission_check_ok = True params_role = role_name if not self.permission_check_ok: raise UliwebError("no permission") self.params_role = params_role
def install_exposes(self): #EXPOSES format #endpoint = topic #bind_name will be the same with function #expose_name = topic, func #expose_name = topic, func, {args} d = settings.get('EXPOSES', {}) for name, args in d.items(): if not args: continue is_wrong = False if isinstance(args, (tuple, list)): if len(args) == 2: expose(args[0], name=name)(args[1]) elif len(args) == 3: if not isinstance(args[2], dict): is_wrong = True else: expose(args[0], name=name, **args[2])(args[1]) else: is_wrong = True elif isinstance(args, (str, unicode)): expose(args)(name) else: is_wrong = True if is_wrong: log.error('EXPOSES definition should be "endpoint=url" or "name=url, endpoint" or "name=url, endpoint, {"args":value1,...}"') raise UliwebError('EXPOSES definition [%s=%r] is not right' % (name, args))
def __init__(self, name, params, parent, key): self.name = name self.params = params self.parent = parent self.key = key self.query_params = self.parent.request_data[key] try: self.model = getattr(models, name) except ModelNotFound as e: log.error("try to find model '%s' but not found: '%s'" % (name, e)) raise UliwebError("model '%s' not found" % (name)) self.setting = settings.APIJSON_MODELS.get(name, {}) self.secret_fields = self.setting.get("secret_fields") self.column = params.get("@column") if self.column: self.column_set = set(self.column.split(",")) if self.secret_fields: self.column_set -= set(self.secret_fields) self.column_set &= set(self.model.columns.keys()) else: self.column_set = None self.permission_check_ok = False
def _conver(): nonlocal v if v and col.type.python_type == datetime: _v = v v = to_datetime(v, tzinfo=getattr(request, "tzinfo", None)) if v == None: raise UliwebError("'%s' cannot convert to datetime" % (_v))
def install_binds(self): #process DISPATCH hooks #BINDS format #func = topic #bind_name will be the same with function #bind_name = topic, func #bind_name = topic, func, {args} d = settings.get('BINDS', {}) for bind_name, args in d.items(): if not args: continue is_wrong = False if isinstance(args, (tuple, list)): if len(args) == 2: dispatch.bind(args[0])(args[1]) elif len(args) == 3: if not isinstance(args[2], dict): is_wrong = True else: dispatch.bind(args[0], **args[2])(args[1]) else: is_wrong = True elif isinstance(args, (str, unicode)): dispatch.bind(args)(bind_name) else: is_wrong = True if is_wrong: log.error('BINDS definition should be "function=topic" or "bind_name=topic, function" or "bind_name=topic, function, {"args":value1,...}"') raise UliwebError('BINDS definition [%s=%r] is not right' % (bind_name, args))
def sort_middlewares(self, middlewares): #middleware process #middleware can be defined as #middleware_name = middleware_class_path[, order] #middleware_name = <empty> will be skip m = [] for v in middlewares: if not v: continue order = None if isinstance(v, (list, tuple)): if len(v) > 2: raise UliwebError('Middleware %r difinition is not right' % v) middleware_path = v[0] if len(v) == 2: order = v[1] else: middleware_path = v cls = import_attr(middleware_path) if order is None: order = getattr(cls, 'ORDER', 500) m.append((order, cls)) m.sort(cmp=lambda x, y: cmp(x[0], y[0])) return [x[1] for x in m]
def __getattr__(self, name): if name in self.__objects: return self.__objects[name] if name not in settings[self.__section]: raise UliwebError("Object %s is not existed!" % name) obj = import_attr(settings[self.__section].get(name)) self.__objects[name] = obj return obj
def function(fname, *args, **kwargs): func = settings.get_var('FUNCTIONS/'+fname) if func: if args or kwargs: return import_attr(func)(*args, **kwargs) else: return import_attr(func) else: raise UliwebError("Can't find the function [%s] in settings" % fname)
def get_configrable_object(key, section, cls=None): """ if obj is a class, then check if the class is subclass of cls or it should be object path, and it'll be imported by import_attr """ from uliweb import UliwebError, settings import inspect if inspect.isclass(key) and cls and issubclass(key, cls): return key elif isinstance(key, string_types): path = settings[section].get(key) if path: _cls = import_attr(path) return _cls else: raise UliwebError("Can't find section name %s in settings" % section) else: raise UliwebError("Key %r should be subclass of %r object or string path format!" % (key, cls))
def get_form(formcls): """ get form class according form class path or form class object """ from uliweb.form import Form import inspect if inspect.isclass(formcls) and issubclass(formcls, Form): return formcls elif isinstance(formcls, (str, unicode)): path = settings.FORMS.get(formcls) if path: _cls = import_attr(path) return _cls else: raise UliwebError("Can't find formcls name %s in settings.FORMS" % formcls) else: raise UliwebError( "formcls should be Form class object or string path format, but %r found!" % formcls)
def _filter_owner(self,q): owner_filtered = False if hasattr(self.model,"owner_condition"): q = q.filter(self.model.owner_condition(request.user.id)) owner_filtered = True if not owner_filtered: user_id_field = self.setting.get("user_id_field") if user_id_field: q = q.filter(getattr(self.model.c,user_id_field)==request.user.id) owner_filtered = True if not owner_filtered: raise UliwebError("'%s' cannot filter with owner"%(self.name)) return q
def _ref_get(self, path, context=None): if context == None: context = {} if path[0] == "/": #relative path c = context for i in path.split("/"): if i: if isinstance(c, dict): c = c.get(i) elif isinstance(c, list): try: c = c[int(i)] except Exception as e: raise UliwebError( "bad path item '%s' in path '%s', error: %s" % (i, path, e)) else: raise UliwebError("cannot get '%s' from '%s'" % (i, c)) return c else: #absolute path c = self.rdict for i in path.split("/"): if i: if isinstance(c, dict): c = c.get(i) elif isinstance(c, list): try: c = c[int(i)] except Exception as e: raise UliwebError( "bad path item '%s' in path '%s', error: %s" % (i, path, e)) else: raise UliwebError("bad path item '%s' in path '%s'" % (i, path)) return c
def query_array(self): self._check_GET_permission() self._get_array_params() params = self.params.copy() #update reference ref_fields = [] refs = {} for n in params: if n[-1] == "@": ref_fields.append(n) col_name = n[:-1] path = params[n] refs[col_name] = self.parent._ref_get(path) if ref_fields: for i in ref_fields: del params[i] params.update(refs) q = self._get_array_q(params) if self.query_type in [1, 2]: self.parent.vars["/%s/total" % (self.key)] = q.count() if self.query_type in [0, 2]: if self.query_count: if self.query_page: q = q.offset(self.query_page * self.query_count) q = q.limit(self.query_count) if self.order: for k in self.order.split(","): if k[-1] == "+": sort_key = k[:-1] sort_order = "asc" elif k[-1] == "-": sort_key = k[:-1] sort_order = "desc" else: sort_key = k sort_order = "asc" try: column = getattr(self.model.c, sort_key) except AttributeError as e: raise UliwebError("'%s' doesn't have column '%s'" % (self.name, sort_key)) q = q.order_by(getattr(column, sort_order)()) l = [self._get_info(i, True) for i in q] self.parent.rdict[self.key] = l
def _expr(self, model, model_param, model_expr): if not isinstance(model_expr, list): raise UliwebError("only accept array in @expr, but get '%s'" % (model_expr)) num = len(model_expr) if (num < 2 or num > 3): raise UliwebError( "only accept 2 or 3 items in @expr, but get '%s'" % (model_expr)) op = model_expr[-2] if op == '&': if num != 3: raise UliwebError( "'&'(and) expression need 3 items, but get '%s'" % (model_expr)) c1 = self._get_filter_condition(model, model_param, model_expr[0], expr=True) c2 = self._get_filter_condition(model, model_param, model_expr[2], expr=True) return and_(c1, c2) elif op == '|': if num != 3: raise UliwebError( "'|'(or) expression need 3 items, but get '%s'" % (model_expr)) c1 = self._get_filter_condition(model, model_param, model_expr[0], expr=True) c2 = self._get_filter_condition(model, model_param, model_expr[2], expr=True) return or_(c1, c2) elif op == '!': if num != 2: raise UliwebError( "'!'(not) expression need 2 items, but get '%s'" % (model_expr)) return not_( self._get_filter_condition(model, model_param, model_expr[1], expr=True)) else: raise UliwebError("unknown operator: '%s'" % (op))
def _get_filter_condition_from_str(self, col, cond_str): cond_str = cond_str.strip() c1, c2 = cond_str[0], cond_str[1] v = None def _conver(): nonlocal v if v and col.type.python_type == datetime: _v = v v = to_datetime(v, tzinfo=getattr(request, "tzinfo", None)) if v == None: raise UliwebError("'%s' cannot convert to datetime" % (_v)) if c1 == '>': if c2 == "=": v = cond_str[2:] _conver() return col >= v else: v = cond_str[1:] _conver() return col > cond_str[1:] elif c1 == '<': if c2 == "=": v = cond_str[2:] _conver() return col <= v else: v = cond_str[1:] _conver() return col < v elif c1 == "=": v = cond_str[1:] _conver() return col == v elif c1 == "!" and c2 == "=": v = cond_str[2:] _conver() return col != v raise UliwebError("not support '%s'" % (cond_str))
def set_log(self): import logging s = self.settings def _get_level(level): return getattr(logging, level.upper()) #get basic configuration config = {} for k, v in s.LOG.items(): if k in ['format', 'datefmt', 'filename', 'filemode']: config[k] = v if s.get_var('LOG/level'): config['level'] = _get_level(s.get_var('LOG/level')) logging.basicConfig(**config) if config.get('filename'): Handler = 'logging.FileHandler' if config.get('filemode'): _args =(config.get('filename'), config.get('filemode')) else: _args = (config.get('filename'),) else: Handler = 'logging.StreamHandler' _args = () #process formatters formatters = {} for f, v in s.get_var('LOG.Formatters', {}).items(): formatters[f] = logging.Formatter(v) #process handlers handlers = {} for h, v in s.get_var('LOG.Handlers', {}).items(): handler_cls = v.get('class', Handler) handler_args = v.get('args', _args) handler = import_attr(handler_cls)(*handler_args) if v.get('level'): handler.setLevel(_get_level(v.get('level'))) format = v.get('format') if format in formatters: handler.setFormatter(formatters[format]) elif format: fmt = logging.Formatter(format) handler.setFormatter(fmt) handlers[h] = handler #process loggers for logger_name, v in s.get_var('LOG.Loggers', {}).items(): if logger_name == 'ROOT': log = logging.getLogger('') else: log = logging.getLogger(logger_name) if v.get('level'): log.setLevel(_get_level(v.get('level'))) if 'propagate' in v: log.propagate = v.get('propagate') if 'handlers' in v: for h in v['handlers']: if h in handlers: log.addHandler(handlers[h]) else: raise UliwebError("Log Handler %s is not defined yet!") sys.exit(1) elif 'format' in v: if v['format'] not in formatters: fmt = logging.Formatter(v['format']) else: fmt = formatters[v['format']] _handler = import_attr(Handler)(*_args) _handler.setFormatter(fmt) log.addHandler(_handler)
def _get_filter_condition(self, model, model_param, item, expr=False): #item can be param key, or expr which expected to be a list if isinstance(item, list): if expr: return self._expr(model, model_param, model_expr=item) else: #current implementation won't run here, but keep for safe raise UliwebError("item can be list only in @expr: '%s'" % (item)) if not isinstance(item, string_types): #current implementation won't run here, but keep for safe raise UliwebError("item should be array or string: '%s'" % (item)) n = item if n[0] == "@": #current implementation won't run here, but keep for safe raise UliwebError("param key should not begin with @: '%s'" % (n)) if n[-1] == "$": name = n[:-1] if hasattr(model, name): return getattr(model.c, name).like(model_param[n]) else: raise UliwebError("model does not have column: '%s'" % (name)) elif n[-1] == "}" and n[-2] == "{": if n[-3] in ["&", "|", "!"]: operator = n[-3] name = n[:-3] else: operator = None name = n[:-2] if not hasattr(model, name): raise UliwebError("model does not have column: '%s'" % (name)) # https://github.com/APIJSON/APIJSON/blob/master/Document.md#32-%E5%8A%9F%E8%83%BD%E7%AC%A6 # https://vincentcheng.github.io/apijson-doc/zh/grammar.html#%E9%80%BB%E8%BE%91%E8%BF%90%E7%AE%97-%E7%AD%9B%E9%80%89 col = getattr(model.c, name) cond = model_param[n] if isinstance(cond, list): fcond = col.in_(cond) if operator == "!": fcond = not_(fcond) return fcond elif isinstance(cond, str): cond_list = cond.strip().split(",") if len(cond_list) == 1: fcond = self._get_filter_condition_from_str( col, cond_list[0]) if operator == "!": fcond = not_(fcond) return fcond elif len(cond_list) > 1: fcond = self._get_filter_condition_from_str( col, cond_list[0]) for c in cond_list[1:]: fc = self._get_filter_condition_from_str(col, c) if operator == "&": fcond = and_(fcond, fc) elif operator == "|" or operator == None: fcond = or_(fcond, fc) else: raise UliwebError( "'%s' not supported in condition list" % (operator)) return fcond raise UliwebError("not support '%s':'%s'" % (n, cond)) elif hasattr(model, n): return getattr(model.c, n) == model_param[n] else: raise UliwebError("non-existent column or not support item: '%s'" % (item))