def fill_vaction(vaction): vaction.oid = uuid.uuid4() vaction.created = np.datetime64(time_ns(), 'ns') vaction.vtype = random.randint(1, 4) vaction.vstatus = random.randint(1, 4) vaction.vcode = generate_activation_code() vaction.verified_oid = uuid.uuid4() vaction.verified_data = { 'f1': os.urandom(32), 'f2': random.randint(1, 100), 'f3': list(range(10)), 'f4': generate_activation_code() }
def fill_token(token): token.oid = uuid.uuid4() token.atype = 1 token.status = 1 token.created = datetime.utcnow() token.completed = None token.code = util.generate_activation_code() token.email = '*****@*****.**' token.pubkey = binascii.b2a_hex(os.urandom(32)).decode()
def page_xbr_submit_onboard(): session['site_area'] = 'landing' session['site-page'] = None # ImmutableMultiDict([ # ('onboard_member_name', 'oberstet'), # ('onboard_member_email', '*****@*****.**'), # ('onboard_wallet_type', 'imported'), # ('onboard_wallet_address', '0x6231eECbA6e7983efe5ce6d16972E16cCcD97CE7'), # ('onboard_accept_eula', 'on') # ]) onboard_member_name = request.form.get('onboard_member_name', None) onboard_member_email = request.form.get('onboard_member_email', None) onboard_wallet_type = request.form.get('onboard_wallet_type', None) onboard_wallet_address = request.form.get('onboard_wallet_address', None) onboard_accept_eula = request.form.get('onboard_accept_eula', None) print('page_xbr_submit_onboard:') print(' onboard_member_name', onboard_member_name) print(' onboard_member_email', onboard_member_email) print(' onboard_wallet_type', onboard_wallet_type) print(' onboard_wallet_address', onboard_wallet_address) print(' onboard_accept_eula', onboard_accept_eula) if onboard_wallet_type not in Account.WALLET_TYPE_FROM_STRING: return render_template( 'xbr_onboard_submit_error.html', onboard_member_error='Invalid wallet type "{}"'.format( onboard_wallet_type)) else: onboard_wallet_type = Account.WALLET_TYPE_FROM_STRING[ onboard_wallet_type] if onboard_accept_eula != 'on': return render_template('xbr_onboard_submit_error.html', onboard_member_error='EULA must be accepted') # eg, onboard_wallet_address = 0x6231eECbA6e7983efe5ce6d16972E16cCcD97CE7 if len(onboard_wallet_address) != 42: return render_template( 'xbr_onboard_submit_error.html', onboard_member_error='Invalid wallet address "{}"'.format( onboard_wallet_address)) try: onboard_wallet_address = binascii.a2b_hex(onboard_wallet_address[2:]) except: return render_template( 'xbr_onboard_submit_error.html', onboard_member_error='Invalid wallet address "{}"'.format( onboard_wallet_address)) if not validate_email(onboard_member_email, check_mx=False, verify=False): return render_template( 'xbr_onboard_submit_error.html', onboard_member_error='Invalid email address "{}"'.format( onboard_member_email)) if not is_valid_username(onboard_member_name): return render_template( 'xbr_onboard_submit_error.html', onboard_member_error= 'Invalid username "{}" - must be a string matching the regular expression {}' .format(onboard_member_name, _USERNAME_PAT_STR)) db = app.config['DB'] schema = app.config['DBSCHEMA'] with db.begin() as txn: account_oid = schema.idx_accounts_by_username[txn, onboard_member_name] if account_oid: return render_template( 'xbr_onboard_submit_error.html', onboard_member_error='Username "{}" already exists'.format( onboard_member_name)) vaction_oid = uuid.uuid4() vaction_code = generate_activation_code() mailgw = app.config['MAILGUN'] try: mailgw.send_onboard_verification(onboard_member_email, vaction_oid, vaction_code) except Exception as e: return render_template( 'xbr_onboard_submit_error.html', onboard_member_error= 'Failed to submit email via mailgun (exception {})'.format(e)) on_success_url = '{}/member'.format(app.config['WEBSITE_URL']) on_error_url = None verified_data = { 'onboard_member_name': onboard_member_name, 'onboard_member_email': onboard_member_email, 'onboard_wallet_type': onboard_wallet_type, 'onboard_wallet_address': onboard_wallet_address, 'on_success_url': on_success_url, 'on_error_url': on_error_url, } with db.begin(write=True) as txn: # double check (again) for username collision, as the mailgun email submit happens async in above after # we initially checked for collision account_oid = schema.idx_accounts_by_username[txn, onboard_member_name] if account_oid: return render_template( 'xbr_onboard_submit_error.html', onboard_member_error='Username "{}" already exists'.format( onboard_member_name)) vaction = VerifiedAction() vaction.oid = vaction_oid vaction.created = np.datetime64(time_ns(), 'ns') vaction.vtype = VerifiedAction.VERIFICATION_TYPE_ONBOARD_MEMBER vaction.vstatus = VerifiedAction.VERIFICATION_STATUS_PENDING vaction.vcode = vaction_code # vaction.verified_oid = None vaction.verified_data = verified_data schema.verified_actions[txn, vaction.oid] = vaction return render_template('xbr_onboard_submit_success.html', onboard_member_email=onboard_member_email, vaction_oid=vaction_oid)
async def _auth_user(self, realm, authid, authrole, pubkey, activation_code=None, request_new_activation_code=False): """ Authenticate a Crossbar.io user. For a user that is not yet registered, or a user key not yet associated with a user, this will raise an ApplicationError signaling the state of the registration process. """ self.log.debug( 'authenticating user for realm={realm}, authid={authid}, authrole={authrole}, pubkey={pubkey}, activation_code={activation_code}, request_new_activation_code={request_new_activation_code}', realm=realm, authid=authid, authrole=authrole, pubkey=pubkey, activation_code=activation_code, request_new_activation_code=request_new_activation_code) # we must protect against this! if activation_code and not authid: raise ApplicationError( Authenticator.ERROR_AUTH_INVALID_PARAMETERS, Authenticator.ERROR_AUTH_INVALID_PARAMETERS_MSG) # get activation for authid/pubkey pair (this will succeed when the user is registered and # the pubkey is associated with the user - the 99% case) activation = None with self._db.begin() as txn: oid = self._schema.idx_act_tokens_by_authid_pubkey[txn, authid + pubkey] if oid: activation = self._schema.activation_tokens[txn, oid] # if the activation is still pending, allow to reset it and we will send # a new activation code if activation and activation.status == ActivationStatus.PENDING and request_new_activation_code: with self._db.begin(write=True) as txn: oid = self._schema.idx_act_tokens_by_authid_pubkey[txn, authid + pubkey] if oid: del self._schema.activation_tokens[txn, oid] else: raise Exception('no such activation') activation = None # get user (if any) user = None if authid: with self._db.begin() as txn: oid = self._schema.idx_users_by_email[txn, authid] if oid: user = self._schema.users[txn, oid] is_new_user = user is None self.log.debug( 'authenticating user={user}, is_new_user={is_new_user}, activation={activation}', user=user, is_new_user=is_new_user, activation=activation) if realm is None or realm == 'com.crossbario.fabric': realm = Authenticator.GLOBAL_USER_REALM authrole = Authenticator.GLOBAL_USER_REALM_USER_ROLE else: if authrole is None: authrole = 'owner' # superusers are treated special .. if pubkey in self._superusers: auth = { 'pubkey': pubkey, 'realm': realm, 'authid': 'superuser', 'role': authrole, 'extra': None, 'cache': False } self.log.info( hl('SUPERUSER authenticated (realm={}, authid={}, authrole={})' .format(auth['realm'], auth['authid'], auth['role']), color='green', bold=True)) return auth # if there is no activation yet, create/store a new one elif not activation: # check for user provided an activation code, though there is no activation currently if activation_code: raise ApplicationError( Authenticator.ERROR_AUTH_NO_PENDING_ACT, Authenticator.ERROR_AUTH_NO_PENDING_ACT_MSG) # ok, create a new activation in the database activation = ActivationToken() activation.oid = uuid.uuid4() activation.atype = ActivationType.REGISTRATION if is_new_user else ActivationType.LOGIN activation.created = datetime.utcnow() # activation.activated = None activation.code = util.generate_activation_code() activation.status = ActivationStatus.PENDING activation.email = authid activation.pubkey = pubkey with self._db.begin(write=True) as txn: self._schema.activation_tokens[txn, activation.oid] = activation if is_new_user: # send user message with activation code await self._messenger.send_user_registration_mail( authid, activation.code) self.log.info( 'User registration mail sent to {authid} with activation code {activation_code}', authid=hlid(authid), activation_code=hl(activation.code, color='red', bold=True)) # deny authentication by raising an error and providing feedback to client raise ApplicationError( Authenticator.ERROR_AUTH_NEW_USER, Authenticator.ERROR_AUTH_NEW_USER_MSG.format(email=authid), email=authid) else: # send user message with activation code await self._messenger.send_user_login_mail( authid, activation.code) self.log.info( 'User login mail sent to {authid} with activation code {activation_code}', authid=hlid(authid), activation_code=hl(activation.code, color='red', bold=True)) # deny authentication by raising an error and providing feedback to client raise ApplicationError( Authenticator.ERROR_AUTH_REGISTERED_USER, Authenticator.ERROR_AUTH_REGISTERED_USER_MSG.format( email=authid), email=authid) else: self.log.debug('Activation found in database:\n{activation}', activation=activation) if activation.status == ActivationStatus.ACTIVE: # ok, so the user's public key is known and active .. the 99% case # user provided an activation code, though there is no activation currently if activation_code: raise ApplicationError( Authenticator.ERROR_AUTH_NO_PENDING_ACT, Authenticator.ERROR_AUTH_NO_PENDING_ACT_MSG) # .. if the user wants to join the global users realm, allow that, # but ignore any authrole that might have been requested if realm is None or realm == Authenticator.GLOBAL_USER_REALM: auth = { 'pubkey': pubkey, 'realm': Authenticator.GLOBAL_USER_REALM, 'authid': authid, 'role': Authenticator.GLOBAL_USER_REALM_USER_ROLE, 'extra': None, 'cache': False } self.log.info( 'Found user {authid} with active pubkey, authenticating for global user realm', authid=authid) return auth # .. if the user wants to join a specific (management) realm, we need to check more .. else: user_roles = None with self._db.begin() as txn: user_oid = self._schema.idx_users_by_email[txn, authid] if user_oid: mrealm_oid = self._schema.idx_mrealms_by_name[ txn, realm] if mrealm_oid: user_roles = self._schema.users_mrealm_roles[ txn, (user_oid, mrealm_oid)] self.log.info('user roles {user_roles}', user_roles=user_roles) else: self.log.info('no mrealm with name "{realm}"', realm=realm) else: self.log.info('no user for authid "{authid}"', authid=authid) if not user_roles: raise Exception( 'no realm "{}" or user not permitted'.format( realm)) else: self.log.info( 'user has {roles} roles on realm {realm}', roles=user_roles.roles, realm=realm) if authrole is None: # the user did not request a specific role, so take the first one? # or take the role with the highest privileges? or lowest? FIXME authrole = min( user_roles.roles ) # the minimum is the _highest_ permission (OWNER=1) else: MAP = { 'owner': UserRole.OWNER, 'admin': UserRole.ADMIN, 'user': UserRole.USER, 'guest': UserRole.GUEST, } authrole = MAP.get(authrole, None) # the user requested a specific role: check that the role is in the # list of permitted roles the user may take on the management realm if authrole not in user_roles.roles: raise Exception( 'not authorized for role {}'.format(authrole)) # map the integer authrole to a string MAP = { UserRole.OWNER: 'owner', UserRole.ADMIN: 'admin', UserRole.USER: '******', UserRole.GUEST: 'guest', } authrole = MAP.get(authrole, None) auth = { 'pubkey': pubkey, 'realm': realm, 'authid': authid, 'role': authrole, 'extra': None, 'cache': False } self.log.info('auth=\n{auth}', auth=auth) self.log.info( 'Authenticated CF user with pubkey {pubkey}.. as authid "{authid}" on realm "{realm}"', authid=authid, realm=realm, pubkey=pubkey[:16]) return auth elif activation.status == ActivationStatus.PENDING: now = datetime.utcnow() passed_secs = (now - activation.created).total_seconds() passed_secs_str = humanize.naturaldelta(passed_secs) if not activation_code: raise ApplicationError( Authenticator.ERROR_AUTH_PENDING_ACT, Authenticator.ERROR_AUTH_PENDING_ACT_MSG.format( passed_secs_str)) if activation_code != activation.code: msg = 'code does not match pending one' raise ApplicationError( Authenticator.ERROR_AUTH_INVALID_ACT_CODE, Authenticator.ERROR_AUTH_INVALID_ACT_CODE_MSG.format( msg)) # check if the activation is expired (15min). if so, delete it, and bail out if passed_secs > (60 * 15): with self._db.begin(write=True) as txn: oid = self._schema.idx_act_tokens_by_authid_pubkey[ txn, authid + pubkey] if oid: del self._schema.activation_tokens[txn, oid] else: raise Exception('no such activation') msg = 'code created {} ago has expired'.format( passed_secs_str) raise ApplicationError( Authenticator.ERROR_AUTH_INVALID_ACT_CODE, Authenticator.ERROR_AUTH_INVALID_ACT_CODE_MSG.format( msg)) # sanitize the stored activation info against what the client provided if activation.atype not in [ ActivationType.LOGIN, ActivationType.REGISTRATION ]: msg = 'activation type "{}" is not for user login/registration.'.format( activation.atype) raise ApplicationError( Authenticator.ERROR_AUTH_INVALID_ACT_CODE, Authenticator.ERROR_AUTH_INVALID_ACT_CODE_MSG.format( msg)) if activation.email != authid: msg = 'email associated with activation code does not match authid provided by client.' raise ApplicationError( Authenticator.ERROR_AUTH_INVALID_ACT_CODE, Authenticator.ERROR_AUTH_INVALID_ACT_CODE_MSG.format( msg)) if activation.pubkey != pubkey: msg = 'pubkey associated with activation code does not match pubkey provided by client.' raise ApplicationError( Authenticator.ERROR_AUTH_INVALID_ACT_CODE, Authenticator.ERROR_AUTH_INVALID_ACT_CODE_MSG.format( msg)) activation.status = ActivationStatus.ACTIVE activation.activated = now # create user if is_new_user: user = User() user.oid = uuid.uuid4() user.email = authid user.registered = datetime.utcnow() with self._db.begin(write=True) as txn: self._schema.users[txn, user.oid] = user self.log.info('New user stored in database:\n{user}', user=user) else: self.log.info('User already stored in database:\n{user}', user=user) # update user-pubkey activation with self._db.begin(write=True) as txn: oid = self._schema.idx_act_tokens_by_authid_pubkey[txn, authid + pubkey] if oid: self._schema.activation_tokens[txn, oid] = activation else: raise Exception('no such activation') # immediately auth user on global users realm auth = { 'pubkey': pubkey, 'realm': Authenticator.GLOBAL_USER_REALM, 'authid': authid, 'role': Authenticator.GLOBAL_USER_REALM_USER_ROLE, 'extra': None, 'cache': False } self.log.info( 'found principal for public key {pubkey} of {authid}', pubkey=pubkey, authid=auth['authid']) return auth else: raise Exception( 'internal error: unprocessed activation status {}'.format( activation.status))
def test_parse_valid_activation_codes(self): for i in range(20): code = generate_activation_code() parsed_code = parse_activation_code(code) self.assertTupleEqual(tuple(code.split('-')), parsed_code.groups())
async def onJoin(self, details): self.log.debug("Connected: {details}", details=details) self._cnt_received = 0 N = 100 def on_event(evt, counter, value3=None, details=None): self._cnt_received += 1 if self._cnt_received % N == 0: self.log.info('Received {cnt} events so far ..', cnt=self._cnt_received) sub = await self.subscribe(on_event, 'com.example.geoservice.', options=SubscribeOptions( match='prefix', details_arg='details')) self.log.debug( 'Subscribed to "com.example.geoservice." with prefix match: {sub}', sub=sub) self._cnt_sent = 0 N = 100 publish_options = PublishOptions( acknowledge=False, exclude_me=False, exclude=[1, 2, 3], exclude_authid=['badguy1', 'badguy2'], exclude_authrole=['hacker', 'fool'], # eligible=[1, 2, 3], # eligible_authid=['anonymous'], eligible_authrole=['anonymous']) while True: for j in range(10): category = random.choice( ['alert', 'warning', 'info', 'ad', 'other']) x = random.randint(0, 100) y = random.randint(0, 100) value1 = os.urandom(16) value2 = random.random() value3 = util.generate_activation_code() evt = { 'category': category, 'x': x, 'y': y, 'value1': value1, 'value2': value2, 'value3': value3, 'i': self._cnt_sent, 'j': j, } topic = 'com.example.geoservice.{}.{}.{}'.format( category, x, y) # self.publish(topic, evt, self._cnt_sent, value3=value3) # await self.publish(topic, evt, self._cnt_sent, value3=value3, options=publish_options) self.publish(topic, evt, self._cnt_sent, value3=value3, options=publish_options) self._cnt_sent += 1 if self._cnt_sent % N == 0: self.log.info('published {cnt} events ..', cnt=self._cnt_sent) await sleep(.1)