class ConferenceSchema(BaseSchema): id = fields.Integer(dump_only=True) tenant_uuid = fields.String(dump_only=True) name = fields.String(allow_none=True, validate=Length(max=128)) preprocess_subroutine = fields.String(allow_none=True, validate=Length(max=39)) max_users = fields.Integer(validate=Range(min=0)) record = fields.Boolean() pin = fields.String(allow_none=True, validate=(Length(max=80), Predicate('isdigit'))) admin_pin = fields.String(allow_none=True, validate=(Length(max=80), Predicate('isdigit'))) quiet_join_leave = fields.Boolean() announce_join_leave = fields.Boolean() announce_user_count = fields.Boolean() announce_only_user = fields.Boolean() music_on_hold = fields.String(allow_none=True, validate=Length(max=128)) links = ListLink(Link('conferences')) extensions = Nested( 'ExtensionSchema', only=['id', 'exten', 'context', 'links'], many=True, dump_only=True, ) incalls = Nested('IncallSchema', only=['id', 'extensions', 'links'], many=True, dump_only=True)
class ParkingLotSchema(BaseSchema): id = fields.Integer(dump_only=True) tenant_uuid = fields.String(dump_only=True) name = fields.String(allow_none=True, validate=Length(max=128)) slots_start = fields.String(validate=(Length(max=40), Predicate('isdigit')), required=True) slots_end = fields.String(validate=(Length(max=40), Predicate('isdigit')), required=True) timeout = fields.Integer(validate=Range(min=0), allow_none=True, missing=45) music_on_hold = fields.String(validate=Length(max=128), allow_none=True, missing='default') links = ListLink(Link('parkinglots')) extensions = Nested( 'ExtensionSchema', only=['id', 'exten', 'context', 'links'], many=True, dump_only=True, ) @validates_schema def validate_slots_range(self, data, **kwargs): # validates_schema is executed before fields validator, so the required # fields is not yet checked if not data.get('slots_start') or not data.get('slots_end'): return if int(data['slots_start']) > int(data['slots_end']): raise ValidationError('It is not a valid range')
class LineSchema(BaseSchema): id = fields.Integer(dump_only=True) tenant_uuid = fields.String(dump_only=True) name = fields.String(dump_only=True) protocol = fields.String(dump_only=True) device_id = fields.String(dump_only=True) device_slot = fields.Integer(dump_only=True) provisioning_extension = fields.String(dump_only=True) context = fields.String(required=True) provisioning_code = fields.String(validate=(Predicate('isdigit'), Length(equal=6))) position = fields.Integer(validate=Range(min=1)) caller_id_name = fields.String( allow_none=True) # Validate length callerid_name + num = max(160) caller_id_num = fields.String(validate=Predicate('isdigit'), allow_none=True) registrar = fields.String(validate=Length(max=128)) links = ListLink(Link('lines')) application = Nested('ApplicationSchema', only=['uuid', 'name', 'links'], dump_only=True) endpoint_sip = Nested( 'EndpointSIPSchema', # TODO(pc-m): Is it really useful to have the username/password on the relation? only=[ 'uuid', 'label', 'name', 'auth_section_options.username', 'links', ], dump_only=True, ) endpoint_sccp = Nested('SccpSchema', only=['id', 'links'], dump_only=True) endpoint_custom = Nested('CustomSchema', only=['id', 'interface', 'links'], dump_only=True) extensions = Nested( 'ExtensionSchema', only=['id', 'exten', 'context', 'links'], many=True, dump_only=True, ) users = Nested( 'UserSchema', only=['uuid', 'firstname', 'lastname', 'links'], many=True, dump_only=True, )
class WizardContextIncallSchema(WizardContextInternalSchema): display_name = fields.String(validate=Length(min=3, max=128), missing='Incalls') did_length = fields.Integer(validate=Range(min=0, max=20)) number_start = fields.String(validate=(Predicate('isdigit'), Length(max=16))) number_end = fields.String(validate=(Predicate('isdigit'), Length(max=16))) @validates_schema def validate_numbers(self, data): super(WizardContextIncallSchema, self).validate_numbers(data) if data.get('number_start') and data.get('number_end'): if not data.get('did_length'): raise ValidationError('Missing data for required field.', 'did_length')
class OutcallDestinationSchema(BaseDestinationSchema): outcall_id = fields.Integer(attribute='actionarg1', required=True) exten = fields.String( validate=(Predicate('isdigit'), Length(max=255)), attribute='actionarg2', required=True, )
class DISADestinationSchema(ApplicationDestinationSchema): pin = fields.String( validate=(Predicate('isdigit'), Length(max=40)), allow_none=True, attribute='actionarg1', ) context = fields.String(validate=Regexp(CONTEXT_REGEX), attribute='actionarg2', required=True)
class RangeSchema(BaseSchema): start = fields.String( validate=(Predicate('isdigit'), Length(max=16)), required=True ) end = fields.String(validate=(Predicate('isdigit'), Length(max=16))) def validate_range(self, start, end): if start > end: raise ValidationError( 'Start of range must be lower than or equal to the end' ) @validates_schema def validate_schema(self, data, **kwargs): if data.get('start') and data.get('end'): start = data.get('start') end = data.get('end') if len(start) != len(end): raise ValidationError('Start and end must be of the same length') self.validate_range(int(start), int(end))
class LineSipSchema(BaseSchema): id = fields.Integer(dump_only=True) username = fields.String(validate=Regexp(USERNAME_REGEX)) secret = fields.String(validate=Regexp(SECRET_REGEX)) callerid = fields.String(validate=Regexp(CALLERID_REGEX), allow_none=True) device_slot = fields.Integer(validate=Range(min=0)) context = fields.String(required=True) provisioning_extension = fields.String( validate=(Length(equal=6), Predicate('isdigit')) ) links = ListLink(Link('lines'), Link('lines_sip'))
class WizardContextInternalSchema(BaseSchema): display_name = fields.String(validate=Length(min=3, max=128), missing='Default') number_start = fields.String(validate=(Predicate('isdigit'), Length(max=16)), required=True) number_end = fields.String(validate=(Predicate('isdigit'), Length(max=16)), required=True) @validates_schema def validate_numbers(self, data): if not data.get('number_start') and not data.get('number_end'): return if not data.get('number_start') or not data.get('number_end'): raise ValidationError( 'Both numbers, number_start and number_end, must be set') if len(data['number_start']) != len(data['number_end']): raise ValidationError('Numbers do not have de same length') if int(data['number_start']) > int(data['number_end']): raise ValidationError('It is not a valid interval')
class PagingSchema(BaseSchema): id = fields.Integer(dump_only=True) tenant_uuid = fields.String(dump_only=True) number = fields.String(validate=(Length(max=32), Predicate('isdigit')), required=True) name = fields.String(validate=Length(max=128), allow_none=True) announce_caller = fields.Boolean() announce_sound = fields.String(validate=Length(max=64), allow_none=True) caller_notification = fields.Boolean() duplex = fields.Boolean(attribute='duplex_bool') ignore_forward = fields.Boolean() record = fields.Boolean(attribute='record_bool') enabled = fields.Boolean() links = ListLink(Link('pagings')) users_caller = Nested( 'UserSchema', only=['uuid', 'firstname', 'lastname', 'links'], many=True, dump_only=True, ) users_member = Nested( 'UserSchema', only=['uuid', 'firstname', 'lastname', 'links'], many=True, dump_only=True, ) @post_dump def wrap_users(self, data, **kwargs): users_member = data.pop('users_member', []) users_caller = data.pop('users_caller', []) if not self.only or 'members' in self.only: data['members'] = {'users': users_member} if not self.only or 'callers' in self.only: data['callers'] = {'users': users_caller} return data
class ParamSchema(Schema): class Meta: unknown = EXCLUDE admin_pw = Str(required=True) city = Str(required=True) country_code = Str( validate=[ Length(2, 2), Predicate( "isupper", error="Non-uppercased characters aren't allowed", ), ], required=True, ) email = Email(required=True) hostname = Str(required=True) org_name = Str(required=True) state = Str(required=True) optional_scopes = List( Str(), validate=ContainsOnly(OPTIONAL_SCOPES), missing=[], ) ldap_pw = Str(missing="", default="") sql_pw = Str(missing="", default="") couchbase_pw = Str(missing="", default="") couchbase_superuser_pw = Str(missing="", default="") auth_sig_keys = Str(missing="") auth_enc_keys = Str(missing="") @validates("hostname") def validate_fqdn(self, value): fqdn = FQDN(value) if not fqdn.is_valid: raise ValidationError("Invalid FQDN format.") @validates("admin_pw") def validate_password(self, value, **kwargs): if not PASSWD_RGX.search(value): raise ValidationError( "Must be at least 6 characters and include " "one uppercase letter, one lowercase letter, one digit, " "and one special character." ) @validates_schema def validate_ldap_pw(self, data, **kwargs): if "ldap" in data["optional_scopes"]: try: self.validate_password(data["ldap_pw"]) except ValidationError as exc: raise ValidationError({"ldap_pw": exc.messages}) @validates_schema def validate_ext_persistence_pw(self, data, **kwargs): err = {} scope_attr_map = [ ("sql", "sql_pw"), ("couchbase", "couchbase_pw"), ] for scope, attr in scope_attr_map: # note we don't enforce custom password validation as cloud-based # databases may use password that not conform to our policy # hence we simply check for empty password only if scope in data["optional_scopes"] and data[attr] == "": err[attr] = ["Empty password isn't allowed"] if err: raise ValidationError(err)