class ProjectGroupLimitFactors(object): # project_group billing-type에 따라 count에 제한을 둠. da_cnt_limit = db.IntField(default=100) project_cnt_limit = db.IntField(default=100) member_cnt_limit = db.IntField(default=100) # project 생성통제 ban_create_project = db.BooleanField(default=False) # ip기반의 위치통제를 하기 위함 use_firewall = db.BooleanField(default=False) firewall_rules = db.ListField(db.EmbeddedDocumentField(FirewallRule)) def check_allowed_ip(self, ipaddr_str): ipaddr = ipaddress.ip_address(text(ipaddr_str)) rules = [rule for rule in self.firewall_rules if rule.allow] if len(rules) == 0: return True return any((rule.match(ipaddr) for rule in rules)) def is_firewall_working(self): return self.use_firewall and len(self.firewall_rules) > 0
class FirewallRule(db.EmbeddedDocument): seq = db.IntField(default=0) allow = db.BooleanField(default=True) source = db.StringField(max_length=50, required=True) cidr = db.IntField(max_value=32, required=True) def to_ipcidr(self): return u"{0}/{1}".format(self.source, self.cidr) def match(self, ipaddr): # if isinstance(ipaddr, str) or isinstance(ipaddr, unicode): if isinstance(ipaddr, six.text_type): ipaddr = ipaddress.ip_address(text(ipaddr)) return ipaddr in ipaddress.ip_network(self.to_ipcidr())
class ProjectGroupNotice(db.EmbeddedDocument): # project_group = db.ReferenceField('ProjectGroup') seq = db.IntField(default=0) created_at = db.DateTimeField(default=datetime.now, required=True) created_by = db.ReferenceField('User', required=True) publish_begin_at = db.DateTimeField(default=datetime.now, required=True) publish_end_at = db.DateTimeField(default=datetime.now, required=True) content = db.StringField(required=True)
class ProjectRecommendationMixin(object): """프로젝트 추천하기.""" picked = db.BooleanField(default=False) display_priority = db.IntField(default=0) @classmethod def _random(cls, limit, project_group, picked=False): u"""추천알고리즘없이 임의로 프로젝트 골라오기.""" kw = dict(private=False, visible=True, demo=False, project_group=project_group) if picked: kw['picked'] = True # picked는 false일때는 true/false모두 챙긴다. projects = Project.objects(**kw).no_dereference().only('id') if len(projects): random.shuffle(list(projects)) projects = [ projects[i].reload() for i in range(min(limit, len(projects))) ] projects.sort(key=lambda x: -x.display_priority) return projects else: return [] @classmethod def random(cls, limit=4, project_group=None): """추천알고리즘없이 임의로 프로젝트 골라오기. PROJECT: 이 함수들은 성능이 좋지 않아서 PRODUCTION에서는 사용할 수 없습니다. batch로 project list를 준비하던가 해야겠음 """ if project_group is None: project_group = ProjectGroup.default() # picked priority selected = cls._random(limit=limit, picked=True, project_group=project_group) if len(selected) < limit: # 만약 limit만큼 채워지지 않았으면 조금 더 얻어오기 chunks = cls._random(limit=limit - len(selected), project_group=project_group) selected.extend(chunks) # shuffle! # random.shuffle(selected) return selected
class ProjectWaitingUserOutbound(ProjectUserBase): """가입되지 않은 사용자를 초대받을때""" user = db.EmailField(required=True) outbound_email_sent_at = db.DateTimeField() outbound_email_sent_count = db.IntField(default=0) invited_by = db.ReferenceField('User', reverse_delete_rule=mongoengine.NULLIFY) invited_at = db.DateTimeField(default=datetime.now) # dropped = db.BooleanField(default=False) render_template_path = 'project/_members_invited_tbl_row.html' @property def is_expired(self): '''초대요청이 오래지나면 만료된다. 만료기간은 14일.''' return self.outbound_email_sent_at < datetime.now() - \ timedelta(days=14) def check_integrity(self): '''외부사용자객체의 무결성을 검사합니다. 외부사용자로 되어있는데 실제 user에도 존재하는 경우는 강제보정 만료된 객체의 경우 삭제처리합니다.''' if self.is_expired: logger.info( 'Deleting expired old-waiting-user-project-relation' '[%s].', self) self.delete() else: user = User.objects(email=self.user).first() if user: '''이미 가입된 사용자로 존재하는군요. 프로젝트와의 관계까지검사합니다.''' relation = ProjectUserBase.objects(project=self.project, user=user).first() if relation: logger.info( 'Deleting useless old-waiting-user-project-relation' '[%s].', self) self.delete() # useless. else: '''미처리된 사용자이므로 가입처리''' new_project_user = ProjectUser(project=self.project, user=user).save() logger.info('Creating new-user-project-relation' '[%s].', new_project_user) logger.info( 'Deleting old-waiting-user-project-relation' '[%s].', self) self.delete() else: '''정상적인 waiting이므로 done nothing.''' pass def sendmail(self): if self.outbound_email_sent_count > 4: logger.warning('outbound-email[%s]-surge-protection', self.user) return token = InviteToken(email=self.user, project=self.project).save() token.sendmail() self.outbound_email_sent_at = datetime.now() self.outbound_email_sent_count += 1 self.save() def clean(self): if self.project.project_group.is_not_default: raise ValidationError( 'outbound-user cannot be created at non-default-project-group') # 초대그룹의 email이 encrypt되어있지 않아서 다른쪽과 interface를 맞추기 위해 암호화 self.user_email = self.user def to_json(self, *args, **kwargs): """일부 계산된 properties를 json으로 내보내기 위한 patch""" ret = super(ProjectWaitingUserOutbound, self).to_json(*args, **kwargs) # patch for email-encryption (slow...) from json import dumps, loads d = loads(ret) d['is_expired'] = self.is_expired return dumps(d)