# -*- coding: utf-8 -*- # Copyright (C) 2010-2016 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 # (only), as published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # # This program is dual-licensed. If you wish to learn more about the # App Enlight Enterprise Edition, including its added features, Support # services, and proprietary license terms, please see # https://rhodecode.com/licenses/ import wtforms import formencode import re import pyramid.threadlocal import datetime import appenlight.lib.helpers as h from appenlight.models.user import User from appenlight.models.group import Group from appenlight.models import DBSession from appenlight.models.alert_channel import AlertChannel from appenlight.models.integrations import IntegrationException from appenlight.models.integrations.campfire import CampfireIntegration from appenlight.models.integrations.bitbucket import BitbucketIntegration from appenlight.models.integrations.github import GithubIntegration from appenlight.models.integrations.flowdock import FlowdockIntegration from appenlight.models.integrations.hipchat import HipchatIntegration from appenlight.models.integrations.jira import JiraClient from appenlight.models.integrations.slack import SlackIntegration from appenlight.lib.ext_json import json from wtforms.ext.csrf.form import SecureForm from wtforms.compat import iteritems from collections import defaultdict _ = str strip_filter = lambda x: x.strip() if x else None uppercase_filter = lambda x: x.upper() if x else None FALSE_VALUES = ('false', '', False, None) class CSRFException(Exception): pass class ReactorForm(SecureForm): def __init__(self, formdata=None, obj=None, prefix='', csrf_context=None, **kwargs): super(ReactorForm, self).__init__(formdata=formdata, obj=obj, prefix=prefix, csrf_context=csrf_context, **kwargs) self._csrf_context = csrf_context def generate_csrf_token(self, csrf_context): return csrf_context.session.get_csrf_token() def validate_csrf_token(self, field): request = self._csrf_context or pyramid.threadlocal.get_current_request() is_from_auth_token = 'auth:auth_token' in request.effective_principals if is_from_auth_token: return True if field.data != field.current_token: # try to save the day by using token from angular if request.headers.get('X-XSRF-TOKEN') != field.current_token: raise CSRFException('Invalid CSRF token') @property def errors_dict(self): r_dict = defaultdict(list) for k, errors in self.errors.items(): r_dict[k].extend([str(e) for e in errors]) return r_dict @property def errors_json(self): return json.dumps(self.errors_dict) def populate_obj(self, obj, ignore_none=False): """ Populates the attributes of the passed `obj` with data from the form's fields. :note: This is a destructive operation; Any attribute with the same name as a field will be overridden. Use with caution. """ if ignore_none: for name, field in iteritems(self._fields): if field.data is not None: field.populate_obj(obj, name) else: for name, field in iteritems(self._fields): field.populate_obj(obj, name) css_classes = {} ignore_labels = {} class SignInForm(ReactorForm): came_from = wtforms.HiddenField() sign_in_user_name = wtforms.StringField(_('User Name')) sign_in_user_password = wtforms.PasswordField(_('Password')) ignore_labels = ['submit'] css_classes = {'submit': 'btn btn-primary'} html_attrs = {'sign_in_user_name': {'placeholder': 'Your login'}, 'sign_in_user_password': { 'placeholder': 'Your password'}} from wtforms.widgets import html_params, HTMLString def select_multi_checkbox(field, ul_class='set', **kwargs): """Render a multi-checkbox widget""" kwargs.setdefault('type', 'checkbox') field_id = kwargs.pop('id', field.id) html = ['') return HTMLString(''.join(html)) def button_widget(field, button_cls='ButtonField btn btn-default', **kwargs): """Render a button widget""" kwargs.setdefault('type', 'button') field_id = kwargs.pop('id', field.id) kwargs.setdefault('value', field.label.text) html = ['' % (html_params(id=field_id, class_=button_cls), kwargs['value'],)] return HTMLString(''.join(html)) def clean_whitespace(value): if value: return value.strip() return value def found_username_validator(form, field): user = User.by_user_name(field.data) # sets user to recover in email validator form.field_user = user if not user: raise wtforms.ValidationError('This username does not exist') def found_username_email_validator(form, field): user = User.by_email(field.data) if not user: raise wtforms.ValidationError('Email is incorrect') def unique_username_validator(form, field): user = User.by_user_name(field.data) if user: raise wtforms.ValidationError('This username already exists in system') def unique_groupname_validator(form, field): group = Group.by_group_name(field.data) mod_group = getattr(form, '_modified_group', None) if group and (not mod_group or mod_group.id != group.id): raise wtforms.ValidationError( 'This group name already exists in system') def unique_email_validator(form, field): user = User.by_email(field.data) if user: raise wtforms.ValidationError('This email already exists in system') def email_validator(form, field): validator = formencode.validators.Email() try: validator.to_python(field.data) except formencode.Invalid as e: raise wtforms.ValidationError(e) def unique_alert_email_validator(form, field): q = DBSession.query(AlertChannel) q = q.filter(AlertChannel.channel_name == 'email') q = q.filter(AlertChannel.channel_value == field.data) email = q.first() if email: raise wtforms.ValidationError( 'This email already exists in alert system') def blocked_email_validator(form, field): blocked_emails = [ 'goood-mail.org', 'shoeonlineblog.com', 'louboutinemart.com', 'guccibagshere.com', 'nikeshoesoutletforsale.com' ] data = field.data or '' domain = data.split('@')[-1] if domain in blocked_emails: raise wtforms.ValidationError('Don\'t spam') def old_password_validator(form, field): if not field.user.check_password(field.data or ''): raise wtforms.ValidationError('You need to enter correct password') class UserRegisterForm(ReactorForm): user_name = wtforms.StringField( _('User Name'), filters=[strip_filter], validators=[ wtforms.validators.Length(min=2, max=30), wtforms.validators.Regexp( re.compile(r'^[\.\w-]+$', re.UNICODE), message="Invalid characters used"), unique_username_validator, wtforms.validators.DataRequired() ]) user_password = wtforms.PasswordField(_('User Password'), filters=[strip_filter], validators=[ wtforms.validators.Length(min=4), wtforms.validators.DataRequired() ]) email = wtforms.StringField(_('Email Address'), filters=[strip_filter], validators=[email_validator, unique_email_validator, blocked_email_validator, wtforms.validators.DataRequired()], description=_("We promise we will not share " "your email with anyone")) first_name = wtforms.HiddenField(_('First Name')) last_name = wtforms.HiddenField(_('Last Name')) ignore_labels = ['submit'] css_classes = {'submit': 'btn btn-primary'} html_attrs = {'user_name': {'placeholder': 'Your login'}, 'user_password': {'placeholder': 'Your password'}, 'email': {'placeholder': 'Your email'}} class UserCreateForm(UserRegisterForm): status = wtforms.BooleanField('User status', false_values=FALSE_VALUES) class UserUpdateForm(UserCreateForm): user_name = None user_password = wtforms.PasswordField(_('User Password'), filters=[strip_filter], validators=[ wtforms.validators.Length(min=4), wtforms.validators.Optional() ]) email = wtforms.StringField(_('Email Address'), filters=[strip_filter], validators=[email_validator, wtforms.validators.DataRequired()]) class LostPasswordForm(ReactorForm): email = wtforms.StringField(_('Email Address'), filters=[strip_filter], validators=[email_validator, found_username_email_validator, wtforms.validators.DataRequired()]) submit = wtforms.SubmitField(_('Reset password')) ignore_labels = ['submit'] css_classes = {'submit': 'btn btn-primary'} class ChangePasswordForm(ReactorForm): old_password = wtforms.PasswordField( 'Old Password', filters=[strip_filter], validators=[old_password_validator, wtforms.validators.DataRequired()]) new_password = wtforms.PasswordField( 'New Password', filters=[strip_filter], validators=[wtforms.validators.Length(min=4), wtforms.validators.DataRequired()]) new_password_confirm = wtforms.PasswordField( 'Confirm Password', filters=[strip_filter], validators=[wtforms.validators.EqualTo('new_password'), wtforms.validators.DataRequired()]) submit = wtforms.SubmitField('Change Password') ignore_labels = ['submit'] css_classes = {'submit': 'btn btn-primary'} class CheckPasswordForm(ReactorForm): password = wtforms.PasswordField( 'Password', filters=[strip_filter], validators=[old_password_validator, wtforms.validators.DataRequired()]) class NewPasswordForm(ReactorForm): new_password = wtforms.PasswordField( 'New Password', filters=[strip_filter], validators=[wtforms.validators.Length(min=4), wtforms.validators.DataRequired()]) new_password_confirm = wtforms.PasswordField( 'Confirm Password', filters=[strip_filter], validators=[wtforms.validators.EqualTo('new_password'), wtforms.validators.DataRequired()]) submit = wtforms.SubmitField('Set Password') ignore_labels = ['submit'] css_classes = {'submit': 'btn btn-primary'} class CORSTextAreaField(wtforms.StringField): """ This field represents an HTML ``