def is_user_viewer_of_record(user_info, recid): """Check if the user is allow to view the record based in the marc tags. Checks inside CFG_ACC_GRANT_VIEWER_RIGHTS_TO_EMAILS_IN_TAGS i.e. his email is inside the 506__m tag or he is inside an e-group listed in the 506__m tag :param user_info: the user_info dictionary that describe the user. :type user_info: user_info dictionary :param recid: the record identifier. :type recid: positive integer @return: True if the user is 'allow to view' the record; False otherwise @rtype: bool """ from invenio_access.local_config import \ CFG_ACC_GRANT_VIEWER_RIGHTS_TO_EMAILS_IN_TAGS, \ CFG_ACC_GRANT_VIEWER_RIGHTS_TO_USERIDS_IN_TAGS if not isinstance(recid, MutableMapping): record = get_record(int(recid)) else: record = recid uid_tags = cfg.get('CFG_ACC_GRANT_VIEWER_RIGHTS_TO_USERIDS_IN_TAGS', CFG_ACC_GRANT_VIEWER_RIGHTS_TO_USERIDS_IN_TAGS) email_tags = cfg.get('CFG_ACC_GRANT_VIEWER_RIGHTS_TO_EMAILS_IN_TAGS', CFG_ACC_GRANT_VIEWER_RIGHTS_TO_EMAILS_IN_TAGS) return is_user_in_tags(record, user_info, uid_tags, email_tags)
def submit_rt_ticket(obj, queue, subject, body, requestors, ticket_id_key): """Submit ticket to RT with the given parameters.""" from inspirehep.utils.tickets import get_instance # Trick to prepare ticket body body = "\n ".join([line.strip() for line in body.split("\n")]) rt_instance = get_instance() if cfg.get("PRODUCTION_MODE") else None rt_queue = cfg.get("CFG_BIBCATALOG_QUEUES") or queue recid = obj.extra_data.get("recid", "") if not recid: recid = obj.data.get("recid", "") if not rt_instance: obj.log.error("No RT instance available. Skipping!") obj.log.info("Ticket submission ignored.") else: ticket_id = rt_instance.create_ticket( Queue=rt_queue, Subject=subject, Text=body, Requestors=requestors, CF_RecordID=recid ) obj.extra_data[ticket_id_key] = ticket_id obj.log.info("Ticket {0} created:\n{1}".format( ticket_id, body.encode("utf-8", "ignore") )) return True
def send_account_activation_email(user): """Send an account activation email.""" expires_in = cfg.get('CFG_WEBSESSION_ADDRESS_ACTIVATION_EXPIRE_IN_DAYS') address_activation_key = EmailConfirmationSerializer( expires_in=timedelta(days=expires_in).total_seconds() ).create_token(user.id, {'email': user.email}) # Render context. ctx = { "ip_address": None, "user": user, "email": user.email, "activation_link": url_for( 'webaccount.access', mailcookie=address_activation_key, _external=True, _scheme='https', ), "days": expires_in, } # Send email send_email( cfg.get('CFG_SITE_SUPPORT_EMAIL'), user.email, _("Account registration at %(sitename)s", sitename=cfg["CFG_SITE_NAME_INTL"].get( getattr(g, 'ln', cfg['CFG_SITE_LANG']), cfg['CFG_SITE_NAME'])), render_template("accounts/emails/activation.tpl", **ctx) )
def index(p, so, page): """Index page with uploader and list of existing depositions.""" ctx = mycommunities_ctx() if not so: so = cfg.get('COMMUNITIES_DEFAULT_SORTING_OPTION') communities = Community.filter_communities(p, so) featured_community = FeaturedCommunity.get_current() form = SearchForm(p=p) per_page = cfg.get('COMMUNITIES_DISPLAYED_PER_PAGE', 10) page = max(page, 1) p = Pagination(page, per_page, communities.count()) ctx.update({ 'r_from': max(p.per_page * (p.page - 1), 0), 'r_to': min(p.per_page * p.page, p.total_count), 'r_total': p.total_count, 'pagination': p, 'form': form, 'title': _('Community Collections'), 'communities': communities.slice( per_page * (page - 1), per_page * page).all(), 'featured_community': featured_community, 'format_record': format_record, }) return render_template( "communities/index.html", **ctx )
def get_canonical_and_alternates_urls(url, drop_ln=True, washed_argd=None, quote_path=False): """ Given an Invenio URL returns a tuple with two elements. The first is the canonical URL, that is the original URL with CFG_SITE_URL prefix, and where the ln= argument stripped. The second element element is mapping, language code -> alternate URL @param quote_path: if True, the path section of the given C{url} is quoted according to RFC 2396 """ dummy_scheme, dummy_netloc, path, dummy_params, query, fragment = urlparse(url) canonical_scheme, canonical_netloc = urlparse(cfg.get("CFG_SITE_URL"))[0:2] parsed_query = washed_argd or parse_qsl(query) no_ln_parsed_query = [(key, value) for (key, value) in parsed_query if key != "ln"] if drop_ln: canonical_parsed_query = no_ln_parsed_query else: canonical_parsed_query = parsed_query if quote_path: path = urllib.quote(path) canonical_query = urlencode(canonical_parsed_query) canonical_url = urlunparse((canonical_scheme, canonical_netloc, path, dummy_params, canonical_query, fragment)) alternate_urls = {} for ln in cfg.get("CFG_SITE_LANGS"): alternate_query = urlencode(no_ln_parsed_query + [("ln", ln)]) alternate_url = urlunparse((canonical_scheme, canonical_netloc, path, dummy_params, alternate_query, fragment)) alternate_urls[ln] = alternate_url return canonical_url, alternate_urls
def send_account_activation_email(user): """Send an account activation email.""" expires_in = cfg.get('CFG_WEBSESSION_ADDRESS_ACTIVATION_EXPIRE_IN_DAYS') address_activation_key = EmailConfirmationSerializer(expires_in=timedelta( days=expires_in).total_seconds()).create_token(user.id, {'email': user.email}) # Render context. ctx = { "ip_address": None, "user": user, "email": user.email, "activation_link": url_for( 'webaccount.access', mailcookie=address_activation_key, _external=True, _scheme='https', ), "days": expires_in, } # Send email send_email( cfg.get('CFG_SITE_SUPPORT_EMAIL'), user.email, _("Account registration at %(sitename)s", sitename=cfg["CFG_SITE_NAME_INTL"].get( getattr(g, 'ln', cfg['CFG_SITE_LANG']), cfg['CFG_SITE_NAME'])), render_template("accounts/emails/activation.tpl", **ctx))
def is_user_owner_of_record(user_info, recid): """Check if the user is owner of the record. I.e. he is the submitter and/or belongs to a owner-like group authorized to 'see' the record. :param user_info: the user_info dictionary that describe the user. :type user_info: user_info dictionary :param recid: the record identifier. :type recid: positive integer :return: True if the user is 'owner' of the record; False otherwise """ from invenio_access.local_config import \ CFG_ACC_GRANT_AUTHOR_RIGHTS_TO_EMAILS_IN_TAGS, \ CFG_ACC_GRANT_AUTHOR_RIGHTS_TO_USERIDS_IN_TAGS if not isinstance(recid, MutableMapping): record = get_record(int(recid)) else: record = recid uid_tags = cfg.get('CFG_ACC_GRANT_AUTHOR_RIGHTS_TO_USERIDS_IN_TAGS', CFG_ACC_GRANT_AUTHOR_RIGHTS_TO_EMAILS_IN_TAGS) email_tags = cfg.get('CFG_ACC_GRANT_AUTHOR_RIGHTS_TO_USERIDS_IN_TAGS', CFG_ACC_GRANT_AUTHOR_RIGHTS_TO_USERIDS_IN_TAGS) return is_user_in_tags(record, user_info, uid_tags, email_tags)
def __init__(self): """Initialize provider.""" self.api = DataCiteMDSClient(username=cfg.get('CFG_DATACITE_USERNAME'), password=cfg.get('CFG_DATACITE_PASSWORD'), prefix=cfg.get('CFG_DATACITE_DOI_PREFIX'), test_mode=cfg.get('CFG_DATACITE_TESTMODE', False), url=cfg.get('CFG_DATACITE_URL'))
def get_storage_path(suffix=""): """Return a path ready to store files.""" from invenio_base.globals import cfg storage_path = os.path.join(cfg.get("CFG_PREFIX"), cfg.get("HARVESTER_STORAGE_PREFIX"), suffix) if not os.path.exists(storage_path): os.makedirs(storage_path) return storage_path
def make_user_agent_string(component=None): """ Return a nice and uniform user-agent string to be used when Invenio act as a client in HTTP requests. """ ret = 'Invenio-%s (+%s; "%s")' % (cfg.get("CFG_VERSION"), cfg.get("CFG_SITE_URL"), cfg.get("CFG_SITE_NAME")) if component: ret += " %s" % component return ret
def __init__(self): """Initialize provider.""" self.api = DataCiteMDSClient( username=cfg.get('CFG_DATACITE_USERNAME'), password=cfg.get('CFG_DATACITE_PASSWORD'), prefix=cfg.get('CFG_DATACITE_DOI_PREFIX'), test_mode=cfg.get('CFG_DATACITE_TESTMODE', False), url=cfg.get('CFG_DATACITE_URL') )
def get_tarball_for_model(eng, arxiv_id): """We download it.""" storage_path = os.path.join( cfg.get('OAIHARVESTER_STORAGEDIR', cfg.get('CFG_TMPSHAREDDIR')), str(eng.uuid) ) if not os.path.exists(storage_path): os.makedirs(storage_path) return get_tarball( arxiv_id, storage_path )
def get_instance(): """Make a RT instance and return it.""" url = cfg.get("CFG_BIBCATALOG_SYSTEM_RT_URL", "") login = cfg.get("CFG_BIBCATALOG_SYSTEM_RT_DEFAULT_USER", "") password = cfg.get("CFG_BIBCATALOG_SYSTEM_RT_DEFAULT_PWD", "") if url: tracker = rt.Rt( url=url, default_login=login, default_password=password, ) tracker.login() return tracker
def _previously_rejected(obj, eng): if cfg.get("PRODUCTION_MODE"): model = eng.workflow_definition.model(obj) record = get_record_from_model(model) if days_ago is None: _days_ago = cfg.get("INSPIRE_ACCEPTANCE_TIMEOUT", 5) else: _days_ago = days_ago if is_too_old(record, days_ago=_days_ago): obj.log.info("Record is likely rejected previously.") return True return False
def get_connection_for_dump_on_slave(): """Return a slave connection for performing dbdump operation on a slave.""" su_user = cfg.get("CFG_DATABASE_SLAVE_SU_USER", "") if "CFG_DATABASE_SLAVE_SU_PASS" not in cfg: cfg["CFG_DATABASE_SLAVE_SU_PASS"] = \ _get_password_from_database_password_file(su_user) connection = connect(host=cfg.get("CFG_DATABASE_SLAVE", ""), port=int(cfg.get("CFG_DATABASE_PORT"), 3306), db=cfg.get("CFG_DATABASE_NAME", ""), user=su_user, passwd=cfg.get("CFG_DATABASE_SLAVE_SU_PASS", ""), use_unicode=False, charset='utf8') connection.autocommit(True) return connection
def generator(self): """Load function from configuration ``CFG_BIBDOCFILE_FILEDIR``.""" func = cfg.get( 'RECORD_DOCUMENT_NAME_GENERATOR', default_name_generator) if isinstance(func, six.string_types): func = import_string(func) return func
def default_name_generator(document): """Return default name of record document with storage path. The path is generated from the uuid using two folder level, being the first two characters the name of the first folder and the second two the name of the second folder. It avoids creating the directories twice but if any of them is not a directory it will raise an OSError exception. :param document: The document to be stored. :returns: Path based on the `_id` of the document. """ uuid = document['_id'] directory = os.path.join(cfg.get('CFG_BIBDOCFILE_FILEDIR'), uuid[0:2], uuid[2:4]) try: os.makedirs(directory) except OSError as e: if e.errno == errno.EEXIST and os.path.isdir(directory): pass else: raise return os.path.join(directory, uuid[4:])
def arxiv_fft_get(obj, eng): """Get FFT from arXiv, if arXiv ID is provided.""" deposition = Deposition(obj) sip = deposition.get_latest_sip(sealed=False) metadata = sip.metadata if 'arxiv_id' in metadata and metadata['arxiv_id']: arxiv_pdf_url = cfg.get("ARXIV_PDF_URL", "http://arxiv.org/pdf/") + \ "{0}.{1}" from invenio.config import CFG_TMPSHAREDDIR arxiv_file, arxiv_file_path = mkstemp( prefix="%s_" % (metadata['arxiv_id'].replace("/", "_")), suffix='.pdf', dir=CFG_TMPSHAREDDIR, ) os.close(arxiv_file) download_url(url=arxiv_pdf_url.format(metadata['arxiv_id'], "pdf"), content_type="pdf", download_to_file=arxiv_file_path) # To get 1111.2222.pdf as filename. filename = "{0}.pdf".format(metadata['arxiv_id'].replace("/", "_")) try: try: save_deposition_file(deposition, filename, arxiv_file_path) except FilenameAlreadyExists: obj.log.error("PDF file not saved: filename already exists.") except Exception as e: obj.log.error("PDF file not saved: {}.".format(e.message))
def generator(self): """Load function from configuration ``CFG_BIBDOCFILE_FILEDIR``.""" func = cfg.get('RECORD_DOCUMENT_NAME_GENERATOR', default_name_generator) if isinstance(func, six.string_types): func = import_string(func) return func
def send_reset_password_email(email): """Reset password by sending a email with the unique link.""" expires_in = cfg.get('CFG_WEBSESSION_ADDRESS_ACTIVATION_EXPIRE_IN_DAYS') reset_key = EmailConfirmationSerializer( expires_in=timedelta(days=expires_in).total_seconds() ).create_token(email, {'email': email}) if not reset_key: raise AccountSecurityError( _('Something goes wrong when the cookie has been generated') ) email_text = render_template( 'accounts/email_reset_password.html', reset_key=reset_key, email=email ) return send_email( fromaddr=cfg['CFG_SITE_SUPPORT_EMAIL'], subject=_("Password reset request for %(website)s", website=cfg['CFG_SITE_URL']), toaddr=email, content=email_text )
def train(records, output, skip_categories=False, skip_astro=False): """Train a set of records from the command line. Usage: inveniomanage predicter train -r /path/to/json -o model.pickle """ if not records: print("Missing records!", file=sys.stderr) return if not os.path.isfile(records): print("{0} is not a file!".format(records), file=sys.stderr) return if os.path.basename(output) == output: # Only a relative name, prefix with config output = os.path.join( cfg.get("CLASSIFIER_MODEL_PATH", ""), output ) else: output = os.path.abspath(output) # Make sure directories are created if not os.path.exists(os.path.dirname(output)): os.makedirs(output) # Check that location is writable if not os.access(os.path.dirname(output), os.W_OK): print("{0} is not writable file!".format(output), file=sys.stderr) return job = celery_train.delay(records, output, skip_categories, skip_astro) print("Scheduled job {0}".format(job.id))
def setup_app(): """Setup OAuth2 provider.""" # Initialize OAuth2 provider oauth2.init_app(current_app) # Configures the OAuth2 provider to use the SQLALchemy models for getters # and setters for user, client and tokens. bind_sqlalchemy(oauth2, db.session, client=Client) # Flask-OAuthlib does not support CACHE_REDIS_URL if cfg['OAUTH2_CACHE_TYPE'] == 'redis' and \ cfg.get('CACHE_REDIS_URL'): from redis import from_url as redis_from_url cfg.setdefault( 'OAUTH2_CACHE_REDIS_HOST', redis_from_url(cfg['CACHE_REDIS_URL']) ) # Configures an OAuth2Provider instance to use configured caching system # to get and set the grant token. bind_cache_grant(current_app, oauth2, OAuthUserProxy.get_current_user) # Disables oauthlib's secure transport detection in in debug mode. if current_app.debug or current_app.testing: os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1'
def login(nickname=None, password=None, login_method=None, remember=False, referer=None): """Login.""" if cfg.get('CFG_ACCESS_CONTROL_LEVEL_SITE') > 0: return abort(401) # page is not authorized if 'action' in request.values: warnings.warn('Action argument "{}" is not used anymore.'.format( request.values['action']), DeprecationWarning) form = LoginForm(CombinedMultiDict( [ImmutableMultiDict({'referer': referer, 'login_method': 'Local'} if referer else {'login_method': 'Local'}), request.values]), csrf_enabled=False) if request.method == "POST": try: if login_method == 'Local' and form.validate_on_submit() and \ authenticate(nickname, password, login_method=login_method, remember=remember): flash( _("You are logged in as %(nick)s.", nick=nickname), "success" ) return login_redirect(referer) else: flash(_("Invalid credentials."), "error") except Exception as e: current_app.logger.error( 'Exception during login process: %s', str(e) ) flash(_("Problem with login."), "error") return render_template('accounts/login.html', form=form), 401
def load(module='', prefix=''): """ Load and returns a template class, given a module name (like 'websearch', 'webbasket',...). The module corresponding to the currently selected template model (see invenio.conf, variable CFG_WEBSTYLE_TEMPLATE_SKIN) is tried first. In case it does not exist, it returns the default template for that module. """ local = {} # load the right template based on the CFG_WEBSTYLE_TEMPLATE_SKIN and the specified module if CFG_WEBSTYLE_TEMPLATE_SKIN == "default": try: mymodule = __import__("invenio.%s_%stemplates" % (module, prefix), local, local, ["invenio.legacy.%s.templates" % (module)]) except ImportError: mymodule = __import__("invenio.legacy.%s.%stemplates" % (module, prefix), local, local, ["invenio.legacy.%s.templates" % (module)]) else: try: mymodule = __import__("invenio.legacy.%s.templates_%s" % (module, CFG_WEBSTYLE_TEMPLATE_SKIN), local, local, ["invenio.legacy.%s.templates" % (module, CFG_WEBSTYLE_TEMPLATE_SKIN)]) except ImportError: mymodule = __import__("invenio.legacy.%s.templates" % (module), local, local, ["invenio.legacy.%s.templates" % (module)]) if 'inspect-templates' in cfg.get('CFG_DEVEL_TOOLS', []): for method_name in dir(mymodule.Template): if method_name.startswith('tmpl_'): enhance_method(module, mymodule.Template, method_name, method_wrapper) return mymodule.Template()
def _report_via_email(obj, eng): recipients = obj.extra_data["config"].get("recipients") if not recipients: obj.log.warning("No recipients") return collections = obj.data.get('collections', dict()) files_uploaded = [] for update_type, filename in collections.items(): count = len(obj.data.get(update_type, list())) files_uploaded.append((basename(filename), count)) harvesting_date = datetime.now().strftime("%Y-%m-%d %H:%M:%S") context = { "object": obj, "files_uploaded": files_uploaded, "args": obj.extra_data.get("args", dict()), "harvesting_date": harvesting_date } body = render_template( template, **context ) subject = "{0} harvest results: {1}".format( context.get("args").get("workflow"), harvesting_date ) send_email(fromaddr=cfg.get("CFG_SITE_SUPPORT_EMAIL"), toaddr=recipients, subject=subject, content=body)
def get_harvesting_workflows(): """Return the workflows enabled in the harvester module.""" enabled_workflows = [] for name in cfg.get("HARVESTER_WORKFLOWS", list()): if workflows.get(name): enabled_workflows.append(name) return enabled_workflows
def setUp(self): """ Initialises test by adding dummy log entries """ from invenio_records.api import create_record from invenio_records.models import Record self.__transaction = db.session.begin_nested() for i in range(10): rec = Record(id=i + 1) db.session.add(rec) create_record({'recid': i + 1}) from inspire.modules.citations.tasks import update_citations_log data = u'[[1, 2, 1, "added", "2013-04-25 04:20:30"],[2, 3, 1, "added", "2013-04-25 04:20:30"],[3, 5, 1, "added", "2013-04-25 04:20:30"],[4, 4, 2, "added", "2013-04-25 04:20:30"],[5, 5, 2, "added", "2013-04-25 04:20:30"],[6, 6, 2, "added", "2013-04-25 04:20:30"],[7, 10, 4, "added", "2013-04-25 04:20:30"],[8, 5, 1, "removed", "2013-04-25 04:20:30"],[9, 5, 4, "added", "2013-04-25 04:20:30"],[10, 6, 4, "added", "2013-04-25 04:20:30"],[11, 3, 4, "added", "2013-04-25 04:20:31"],[12, 8, 5, "added", "2013-04-25 04:20:31"],[13, 10, 4, "removed", "2013-04-25 04:20:31"],[14, 7, 6, "added", "2013-04-25 04:20:31"],[15, 9, 6, "added", "2013-04-25 04:20:31"],[16, 10, 6, "added", "2013-04-25 04:20:31"],[17, 1, 7, "added", "2013-04-25 04:20:31"],[18, 8, 7, "added", "2013-04-25 04:20:31"],[19, 10, 7, "added", "2013-04-25 04:20:31"],[20, 3, 8, "added", "2013-04-25 04:20:31"],[21, 10, 9, "added", "2013-04-25 04:20:31"],[22, 3, 9, "added", "2013-04-25 04:20:31"],[23, 3, 8, "removed", "2013-04-25 04:20:31"],[24, 1, 10, "added", "2013-04-25 04:20:31"],[25, 2, 10, "added", "2013-04-25 04:20:31"],[26, 3, 10, "added", "2013-04-25 04:20:31"]]' # Mocks two responses. The first one contains the dummy data and the second an empty list # to force update_citations_log() to terminate. httpretty.register_uri( httpretty.GET, cfg.get("CITATIONS_FETCH_LEGACY_URL"), responses=[ httpretty.Response(body=data, status=200), httpretty.Response(body='[]', status=200), ] ) Citation.query.delete() Citation_Log.query.delete() update_citations_log()
def record_extraction_from_string(xml_string, oai_namespace="http://www.openarchives.org/OAI/2.0/"): """Given a OAI-PMH XML return a list of every record incl. headers. :param xml_string: OAI-PMH XML :type xml_string: str :param oai_namespace: optionally provide the OAI-PMH namespace :type oai_namespace: str :return: return a list of XML records as string :rtype: str """ if oai_namespace: nsmap = {None: oai_namespace} else: nsmap = cfg.get("OAIHARVESTER_DEFAULT_NAMESPACE_MAP") namespace_prefix = "{{{0}}}".format(oai_namespace) root = etree.fromstring(xml_string) headers = [] headers.extend(root.findall(".//{0}responseDate".format(namespace_prefix), nsmap)) headers.extend(root.findall(".//{0}request".format(namespace_prefix), nsmap)) records = root.findall(".//{0}record".format(namespace_prefix), nsmap) list_of_records = [] for record in records: wrapper = etree.Element("OAI-PMH", nsmap=nsmap) for header in headers: wrapper.append(header) wrapper.append(record) list_of_records.append(etree.tostring(wrapper)) return list_of_records
def duplicated_doi_validator(form, field): """Check if a record with the same doi already exists.""" doi = field.data # TODO: local check for duplicates if not doi: return if cfg.get('PRODUCTION_MODE'): inspirehep_duplicated_validator('doi:' + doi, 'DOI')
def decorated(recid, *args, **kwargs): from invenio_collections.models import Collection from .api import get_record from .access import check_user_can_view_record from .models import Record as Bibrec # ensure recid to be integer recid = int(recid) g.bibrec = Bibrec.query.get(recid) g.record = record = get_record(recid) if record is None: abort(404) g.collection = collection = Collection.query.filter( Collection.name.in_(record['_collections'])).first() (auth_code, auth_msg) = check_user_can_view_record( current_user, record) # only superadmins can use verbose parameter for obtaining debug # information if not current_user.is_super_admin and 'verbose' in kwargs: kwargs['verbose'] = 0 if auth_code: flash(auth_msg, 'error') abort(apache.HTTP_UNAUTHORIZED) # TODO check record status (exists, merged, deleted) title = record.get(cfg.get('RECORDS_BREADCRUMB_TITLE_KEY'), '') tabs = [] def _format_record(record, of='hd', user_info=current_user, *args, **kwargs): from invenio_formatter import format_record return format_record(record, of, user_info=user_info, *args, **kwargs) @register_template_context_processor def record_context(): # from invenio.modules.comments.api import get_mini_reviews return dict(recid=recid, record=record, tabs=tabs, title=title, get_mini_reviews=lambda *args, **kwargs: '', # FIXME get_mini_reviews, collection=collection, format_record=_format_record ) pre_template_render.send( "%s.%s" % (blueprint.name, f.__name__), recid=recid, ) return f(recid, *args, **kwargs)
def __init__(self, expires_in=None): """Initialize underlying TimedJSONWebSignatureSerializer.""" dt = expires_in or cfg.get('ACCOUNTS_CONFIRMLINK_EXPIRES_IN') super(EmailConfirmationSerializer, self).__init__( cfg['SECRET_KEY'], expires_in=dt, salt='accounts-email', )
def duplicated_arxiv_id_validator(form, field): """Check if a record with the same arXiv ID already exists.""" arxiv_id = field.data # TODO: local check for duplicates if not arxiv_id: return if cfg.get('PRODUCTION_MODE'): inspirehep_duplicated_validator( '035__a:oai:arXiv.org:' + arxiv_id, 'arXiv ID')
def was_already_harvested(record): """Return True if the record was already harvested. We use the following heuristic: if the record belongs to one of the CORE categories then it was probably ingested in some other way. """ categories = record.get('subject_terms.term', []) for category in categories: if category.lower() in cfg.get('INSPIRE_ACCEPTED_CATEGORIES', []): return True
def already_harvested(obj, eng): """Check if record is already harvested.""" if cfg.get("PRODUCTION_MODE"): model = eng.workflow_definition.model(obj) record = get_record_from_model(model) if was_already_harvested(record): obj.log.info("Record is already being harvested on INSPIRE.") return True return False
def wash_field(f): """Wash field passed by URL.""" if f: # get rid of unnecessary whitespace and make it lowercase # (e.g. Author -> author) to better suit iPhone etc input # mode: f = f.strip().lower() # wash legacy 'f' field names, e.g. replace 'wau' or `au' by # 'author', if applicable: return cfg.get('CFG_WEBSEARCH_FIELDS_CONVERT', {}).get(f, f)
def setup_app(app): """ Prepare application config from Invenio configuration. @see: https://flask-email.readthedocs.org/en/latest/#configuration """ cfg = app.config app.config.setdefault( 'EMAIL_BACKEND', cfg.get('CFG_EMAIL_BACKEND', 'flask_email.backends.smtp.Mail')) app.config.setdefault('DEFAULT_FROM_EMAIL', cfg['CFG_SITE_SUPPORT_EMAIL']) app.config.setdefault('SERVER_EMAIL', cfg['CFG_SITE_ADMIN_EMAIL']) app.config.setdefault('ADMINS', (('', cfg['CFG_SITE_ADMIN_EMAIL']), )) app.config.setdefault('MANAGERS', (cfg['CFG_SITE_SUPPORT_EMAIL'], )) CFG_MISCUTIL_SMTP_HOST = cfg.get('CFG_MISCUTIL_SMTP_HOST') CFG_MISCUTIL_SMTP_PORT = cfg.get('CFG_MISCUTIL_SMTP_PORT') CFG_MISCUTIL_SMTP_USER = cfg.get('CFG_MISCUTIL_SMTP_USER', '') CFG_MISCUTIL_SMTP_PASS = cfg.get('CFG_MISCUTIL_SMTP_PASS', '') CFG_MISCUTIL_SMTP_TLS = cfg.get('CFG_MISCUTIL_SMTP_TLS', False) app.config.setdefault('EMAIL_HOST', CFG_MISCUTIL_SMTP_HOST) app.config.setdefault('EMAIL_PORT', CFG_MISCUTIL_SMTP_PORT) app.config.setdefault('EMAIL_HOST_USER', CFG_MISCUTIL_SMTP_USER) app.config.setdefault('EMAIL_HOST_PASSWORD', CFG_MISCUTIL_SMTP_PASS) app.config.setdefault('EMAIL_USE_TLS', CFG_MISCUTIL_SMTP_TLS) # app.config['EMAIL_USE_SSL']: defaults to False app.config.setdefault('EMAIL_FILE_PATH', cfg['CFG_LOGDIR']) return app
def _get_password_from_database_password_file(user): """Parse CFG_DATABASE_PASSWORD_FILE and return password for user.""" pwfile = cfg.get("CFG_DATABASE_PASSWORD_FILE", None) if pwfile and os.path.exists(pwfile): for row in open(pwfile): if row.strip(): a_user, pwd = row.strip().split(" // ") if user == a_user: return pwd raise ValueError("user '%s' not found in database password file '%s'" % (user, pwfile)) raise IOError("No password defined for user '%s' but database password " "file is not available" % user)
def send_reset_password_email(email): """Reset password by sending a email with the unique link.""" expires_in = cfg.get('CFG_WEBSESSION_ADDRESS_ACTIVATION_EXPIRE_IN_DAYS') reset_key = EmailConfirmationSerializer(expires_in=timedelta( days=expires_in).total_seconds()).create_token(email, {'email': email}) if not reset_key: raise AccountSecurityError( _('Something goes wrong when the cookie has been generated')) email_text = render_template('accounts/email_reset_password.html', reset_key=reset_key, email=email) return send_email(fromaddr=cfg['CFG_SITE_SUPPORT_EMAIL'], subject=_("Password reset request for %(website)s", website=cfg['CFG_SITE_URL']), toaddr=email, content=email_text)
def datacite_register(recid): """Register a DOI for new publication. If it fails, it will retry every 10 minutes for 1 hour. """ record = get_record(recid) if record is None: logger.debug("Record %s not found" % recid) return doi_val = record.get(cfg['PIDSTORE_DATACITE_RECORD_DOI_FIELD'], None) logger.debug("Found DOI %s in record %s" % (doi_val, recid)) pid = PersistentIdentifier.get("doi", doi_val) if not pid: logger.debug("DOI not locally managed.") return else: logger.debug("DOI locally managed.") if not pid.has_object("rec", recid): raise Exception("DOI %s is not assigned to record %s." % (doi_val, recid)) if pid.is_new() or pid.is_reserved(): logger.info("Registering DOI %s for record %s" % (doi_val, recid)) url = "%s/record/%s" % (cfg.get('PIDSTORE_DATACITE_SITE_URL', cfg['CFG_SITE_URL']), recid) doc = format_record(record, cfg['PIDSTORE_DATACITE_OUTPUTFORMAT']) if not pid.register(url=url, doc=doc): m = "Failed to register DOI %s" % doi_val logger.error(m + "\n%s\n%s" % (url, doc)) if not datacite_register.request.is_eager: raise datacite_register.retry(exc=Exception(m)) else: logger.info("Successfully registered DOI %s." % doi_val)
def login(nickname=None, password=None, login_method=None, remember=False, referer=None): """Login.""" if cfg.get('CFG_ACCESS_CONTROL_LEVEL_SITE') > 0: return abort(401) # page is not authorized if 'action' in request.values: warnings.warn( 'Action argument "{}" is not used anymore.'.format( request.values['action']), DeprecationWarning) form = LoginForm(CombinedMultiDict([ ImmutableMultiDict({ 'referer': referer, 'login_method': 'Local' } if referer else {'login_method': 'Local'}), request.values ]), csrf_enabled=False) if request.method == "POST": try: if login_method == 'Local' and form.validate_on_submit() and \ authenticate(nickname, password, login_method=login_method, remember=remember): flash(_("You are logged in as %(nick)s.", nick=nickname), "success") return login_redirect(referer) else: flash(_("Invalid credentials."), "error") except Exception as e: current_app.logger.error('Exception during login process: %s', str(e)) flash(_("Problem with login."), "error") return render_template('accounts/login.html', form=form), 401
def get_current_user_records_that_can_be_displayed(qid): """Return records that current user can display. :param qid: query identifier :return: records in intbitset """ CFG_WEBSEARCH_SEARCH_CACHE_TIMEOUT = cfg.get( 'CFG_WEBSEARCH_SEARCH_CACHE_TIMEOUT') @search_results_cache.memoize(timeout=CFG_WEBSEARCH_SEARCH_CACHE_TIMEOUT) def get_records_for_user(qid, uid): key = get_search_results_cache_key_from_qid(qid) data = search_results_cache.get(key) if data is None: return intbitset([]) cc = search_results_cache.get(key + '::cc') return get_records_that_can_be_displayed( current_user.get('precached_permitted_restricted_collections', []), intbitset().fastload(data), cc) # Simplifies API return get_records_for_user(qid, current_user.get_id())
def setup_app(): """Setup OAuth2 provider.""" # Initialize OAuth2 provider oauth2.init_app(current_app) # Configures the OAuth2 provider to use the SQLALchemy models for getters # and setters for user, client and tokens. bind_sqlalchemy(oauth2, db.session, client=Client) # Flask-OAuthlib does not support CACHE_REDIS_URL if cfg['OAUTH2_CACHE_TYPE'] == 'redis' and \ cfg.get('CACHE_REDIS_URL'): from redis import from_url as redis_from_url cfg.setdefault('OAUTH2_CACHE_REDIS_HOST', redis_from_url(cfg['CACHE_REDIS_URL'])) # Configures an OAuth2Provider instance to use configured caching system # to get and set the grant token. bind_cache_grant(current_app, oauth2, OAuthUserProxy.get_current_user) # Disables oauthlib's secure transport detection in in debug mode. if current_app.debug or current_app.testing: os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1'
def dbexec(version=False, interactive=False): """Runs SQL commands.""" MYSQL = cfg.get('MYSQL', 'mysql') ## is version called? if version: print(__revision__) return 0 params = [ '--default-character-set=utf8', '--max_allowed_packet=1G', '--host=%s' % (cfg['CFG_DATABASE_HOST'], ), '--port=%s' % (cfg['CFG_DATABASE_PORT'], ), '--user=%s' % (cfg['CFG_DATABASE_USER'], ), '--password=%s' % (cfg['CFG_DATABASE_PASS'], ), cfg['CFG_DATABASE_NAME'] ] ## interactive mode asked for? if not interactive: params.insert(0, '-B') return subprocess.call([MYSQL] + params)
def decorated(recid, *args, **kwargs): from invenio_collections.models import Collection from .api import get_record from .access import check_user_can_view_record from .models import Record as Bibrec # ensure recid to be integer recid = int(recid) g.bibrec = Bibrec.query.get(recid) g.record = record = get_record(recid) if record is None: abort(404) g.collection = collection = Collection.query.filter( Collection.name.in_(record['_collections'])).first() (auth_code, auth_msg) = check_user_can_view_record(current_user, record) # only superadmins can use verbose parameter for obtaining debug # information if not current_user.is_super_admin and 'verbose' in kwargs: kwargs['verbose'] = 0 if auth_code: flash(auth_msg, 'error') abort(apache.HTTP_UNAUTHORIZED) # TODO check record status (exists, merged, deleted) title = record.get(cfg.get('RECORDS_BREADCRUMB_TITLE_KEY'), '') tabs = [] def _format_record(record, of='hd', user_info=current_user, *args, **kwargs): from invenio_formatter import format_record return format_record(record, of, user_info=user_info, *args, **kwargs) @register_template_context_processor def record_context(): # from invenio.modules.comments.api import get_mini_reviews return dict( recid=recid, record=record, tabs=tabs, title=title, get_mini_reviews=lambda *args, **kwargs: '', # FIXME get_mini_reviews, collection=collection, format_record=_format_record) pre_template_render.send( "%s.%s" % (blueprint.name, f.__name__), recid=recid, ) return f(recid, *args, **kwargs)
def default_ln(ln): """Default ln.""" cfg.get('CFG_SITE_LANG') if ln is None else ln
def run_sql(sql, param=None, n=0, with_desc=False, with_dict=False, run_on_slave=False, connection=None): """Run SQL on the server with PARAM and return result. @param param: tuple of string params to insert in the query (see notes below) @param n: number of tuples in result (0 for unbounded) @param with_desc: if True, will return a DB API 7-tuple describing columns in query. @param with_dict: if True, will return a list of dictionaries composed of column-value pairs @param connection: if provided, uses the given connection. @return: If SELECT, SHOW, DESCRIBE statements, return tuples of data, followed by description if parameter with_desc is provided. If SELECT and with_dict=True, return a list of dictionaries composed of column-value pairs, followed by description if parameter with_desc is provided. If INSERT, return last row id. Otherwise return SQL result as provided by database. @note: When the site is closed for maintenance (as governed by the config variable CFG_ACCESS_CONTROL_LEVEL_SITE), do not attempt to run any SQL queries but return empty list immediately. Useful to be able to have the website up while MySQL database is down for maintenance, hot copies, table repairs, etc. @note: In case of problems, exceptions are returned according to the Python DB API 2.0. The client code can import them from this file and catch them. """ if cfg['CFG_ACCESS_CONTROL_LEVEL_SITE'] == 3: # do not connect to the database as the site is closed for maintenance: return [] elif cfg['CFG_ACCESS_CONTROL_LEVEL_SITE'] > 0: # Read only website if not sql.upper().startswith("SELECT") \ and not sql.upper().startswith("SHOW"): return if param: param = tuple(param) dbhost = cfg['CFG_DATABASE_HOST'] if run_on_slave and cfg['CFG_DATABASE_SLAVE']: dbhost = cfg['CFG_DATABASE_SLAVE'] if 'sql-logger' in cfg.get('CFG_DEVEL_TOOLS', []): log_sql_query(dbhost, sql, param) try: db = connection or _db_login(dbhost) cur = db.cursor() cur.execute("SET SESSION sql_mode = %s", ['ANSI_QUOTES']) gc.disable() rc = cur.execute(sql, param) gc.enable() except (OperationalError, MySQLdbOperationalError, InterfaceError): # unexpected disconnect, bad malloc error, etc # FIXME: now reconnect is always forced, we may perhaps want to ping() # first? if connection is not None: raise try: db = _db_login(dbhost, relogin=1) cur = db.cursor() cur.execute("SET SESSION sql_mode = %s", ['ANSI_QUOTES']) gc.disable() rc = cur.execute(sql, param) gc.enable() except (OperationalError, MySQLdbOperationalError, InterfaceError): # unexpected disconnect, bad malloc error, etc raise if string.upper(string.split(sql)[0]) in \ ("SELECT", "SHOW", "DESC", "DESCRIBE"): if n: recset = cur.fetchmany(n) else: recset = cur.fetchall() if with_dict: # return list of dictionaries # let's extract column names keys = [row[0] for row in cur.description] # let's construct a list of dictionaries list_dict_results = [ dict(zip(*[keys, values])) for values in recset ] if with_desc: return list_dict_results, cur.description else: return list_dict_results else: if with_desc: return recset, cur.description else: return recset else: if string.upper(string.split(sql)[0]) == "INSERT": rc = cur.lastrowid return rc
def replacements(self): """TODO.""" return cfg.get(self.config_name, [])
def main(): """Main function that analyzes command line input and calls whatever is appropriate. """ from invenio_access.firerole import repair_role_definitions from invenio_access.control import (acc_add_default_settings, acc_reset_default_settings) from invenio_base.globals import cfg from invenio.legacy.bibsched.bibtask import authenticate DEF_DEMO_USER_ROLES = cfg.get('DEF_DEMO_USER_ROLES', tuple()) DEF_DEMO_ROLES = cfg.get('DEF_DEMO_ROLES', tuple()) DEF_DEMO_AUTHS = cfg.get('DEF_DEMO_AUTHS', tuple()) ## parse command line: # set user-defined options: options = {'user' : '', 'reset' : 0, 'compile' : 0, 'add' : 0, 'demo' : 0} try: opts, args = getopt.getopt(sys.argv[1:], "hVu:racD", ["help", "version", "user="******"reset", "add", "compile", "demo"]) except getopt.GetoptError as err: usage(1, err) try: for opt in opts: if opt[0] in ("-h", "--help"): usage(0) elif opt[0] in ("-V", "--version"): print(__revision__) sys.exit(0) elif opt[0] in ("-u", "--user"): options["user"] = opt[1] elif opt[0] in ("-r", "--reset"): options["reset"] = 1 elif opt[0] in ("-a", "--add"): options["add"] = 1 elif opt[0] in ("-c", "--compile"): options["compile"] = 1 elif opt[0] in ("-D", "--demo"): options["demo"] = 1 else: usage(1) if options['add'] or options['reset'] or options['compile']: #if acca.acc_get_action_id('cfgwebaccess'): # # Action exists hence authentication works :-) # options['user'] = authenticate(options['user'], # authorization_msg="WebAccess Administration", # authorization_action="cfgwebaccess") if options['reset'] and options['demo']: acc_reset_default_settings( [cfg['CFG_SITE_ADMIN_EMAIL']], DEF_DEMO_USER_ROLES, DEF_DEMO_ROLES, DEF_DEMO_AUTHS) print("Reset default demo site settings.") elif options['reset']: acc_reset_default_settings([cfg['CFG_SITE_ADMIN_EMAIL']]) print("Reset default settings.") elif options['add'] and options['demo']: acc_add_default_settings( [cfg['CFG_SITE_ADMIN_EMAIL']], DEF_DEMO_USER_ROLES, DEF_DEMO_ROLES, DEF_DEMO_AUTHS) print("Added default demo site settings.") elif options['add']: acc_add_default_settings([cfg['CFG_SITE_ADMIN_EMAIL']]) print("Added default settings.") if options['compile']: repair_role_definitions() print("Compiled firewall like role definitions.") else: usage(1, "You must specify at least one command") except StandardError as e: from invenio.ext.logging import register_exception register_exception() usage(e) return
def run_sql(sql, param=None, n=0, with_desc=False, with_dict=False, run_on_slave=False, connection=None): """Run SQL on the server with PARAM and return result. :param param: tuple of string params to insert in the query (see notes below) :param n: number of tuples in result (0 for unbounded) :param with_desc: if True, will return a DB API 7-tuple describing columns in query. :param with_dict: if True, will return a list of dictionaries composed of column-value pairs :param connection: if provided, uses the given connection. :return: If SELECT, SHOW, DESCRIBE statements, return tuples of data, followed by description if parameter with_desc is provided. If SELECT and with_dict=True, return a list of dictionaries composed of column-value pairs, followed by description if parameter with_desc is provided. If INSERT, return last row id. Otherwise return SQL result as provided by database. @note: When the site is closed for maintenance (as governed by the config variable CFG_ACCESS_CONTROL_LEVEL_SITE), do not attempt to run any SQL queries but return empty list immediately. Useful to be able to have the website up while MySQL database is down for maintenance, hot copies, table repairs, etc. @note: In case of problems, exceptions are returned according to the Python DB API 2.0. The client code can import them from this file and catch them. """ if cfg['CFG_ACCESS_CONTROL_LEVEL_SITE'] == 3: # do not connect to the database as the site is closed for maintenance: return [] elif cfg['CFG_ACCESS_CONTROL_LEVEL_SITE'] > 0: # Read only website if not sql.upper().startswith("SELECT") and \ not sql.upper().startswith("SHOW"): return if param: param = tuple(param) # FIXME port database slave support dbhost = cfg['CFG_DATABASE_HOST'] if run_on_slave and cfg['CFG_DATABASE_SLAVE']: dbhost = cfg['CFG_DATABASE_SLAVE'] if 'sql-logger' in cfg.get('CFG_DEVEL_TOOLS', []): log_sql_query(dbhost, sql, param) gc.disable() engine = db.engine.execution_options(use_unicode=0) sql = sql.replace('`', '"') current_app.logger.info(sql) if param is None: cur = engine.execute(sql.replace('%', '%%')) else: cur = engine.execute(sql, (param, )) gc.enable() if string.upper(string.split(sql)[0]) in \ ("SELECT", "SHOW", "DESC", "DESCRIBE"): if n: recset = cur.fetchmany(n) else: recset = cur.fetchall() from invenio_base.helpers import utf8ifier recset = map(dict if with_dict else tuple, recset) recset = utf8ifier(recset) if with_desc: return recset, cur.description else: return recset else: if string.upper(string.split(sql)[0]) == "INSERT": return cur.lastrowid return cur
def register(): """Register.""" req = request.get_legacy_request() # FIXME if cfg.get('CFG_ACCESS_CONTROL_LEVEL_SITE') > 0: from invenio.legacy import webuser return webuser.page_not_authorized(req, "../youraccount/register?ln=%s" % g.ln, navmenuid='youraccount') form = RegisterForm(request.values, csrf_enabled=False) title = _("Register") messages = [] state = "" if form.validate_on_submit(): from invenio.legacy import webuser ruid = webuser.registerUser(req, form.email.data.encode('utf8'), form.password.data.encode('utf8'), form.nickname.data.encode('utf8'), ln=g.ln) if ruid == 0: title = _("Account created") messages.append(_("Your account has been successfully created.")) state = "success" if cfg.get('CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_NEW_ACCOUNT') \ == 1: messages.append( _("In order to confirm its validity, " "an email message containing an account " "activation key has been sent to the given " "email address.")) messages.append( _("Please follow instructions presented " "there in order to complete the account " "registration process.")) if cfg.get('CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS') >= 1: messages.append( _("A second email will be sent when the " "account has been activated and can be " "used.")) elif cfg['CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_NEW_ACCOUNT'] != 1: user = User.query.filter( User.email == form.email.data.lower()).one() login_user(user.get_id()) messages.append(_("You can now access your account.")) else: title = _("Registration failure") state = "danger" if ruid == 5: messages.append( _("Users cannot register themselves, only " "admin can register them.")) elif ruid == 6 or ruid == 1: # Note, code 1 is used both for invalid email, and email # sending # problems, however the email address is validated by the form, # so we only have to report a problem sending the email here messages.append( _("The site is having troubles in sending " "you an email for confirming your email " "address.")) messages.append( _("The error has been logged and will be " "taken in consideration as soon as possible.")) else: # Errors [-2, (1), 2, 3, 4] taken care of by form validation messages.append(_("Internal error %(ruid)s", ruid=ruid)) elif request.method == 'POST': title = _("Registration failure") state = "warning" return render_template('accounts/register.html', form=form, title=title, messages=messages, state=state)