Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

record: User resource creation #72

Merged
merged 1 commit into from
Oct 9, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,4 @@ recursive-include data *.crt
# added by check_manifest.py
recursive-include sonar *.babelrc
recursive-include sonar *.eslintignore
recursive-include tests *.json
39 changes: 39 additions & 0 deletions data/users.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
[
{
"first_name": "Jorg",
"last_name": "Mueller",
"email": "[email protected]",
"password": "123456",
"roles": ["superadmin"]
},
{
"first_name": "Elia",
"last_name": "Rossi",
"email": "[email protected]",
"password": "123456",
"roles": ["admin"],
"institution": {
"$ref": "https://sonar.ch/api/institutions/usi"
}
},
{
"first_name": "Emanuele",
"last_name": "Fiorentini",
"email": "[email protected]",
"password": "123456",
"roles": ["moderator"],
"institution": {
"$ref": "https://sonar.ch/api/institutions/usi"
}
},
{
"first_name": "Jules",
"last_name": "Brochu",
"email": "[email protected]",
"password": "123456",
"roles": ["user"],
"institution": {
"$ref": "https://sonar.ch/api/institutions/usi"
}
}
]
12 changes: 6 additions & 6 deletions scripts/setup
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,16 @@ pipenv run invenio index init --force
pipenv run invenio index queue init purge

# Create admin role to restrict access
pipenv run invenio roles create superadmin
pipenv run invenio roles create admin
pipenv run invenio roles create moderator
pipenv run invenio roles create user
pipenv run invenio roles create librarian
pipenv run invenio access allow superuser-access role admin

# Create admin user and assign admin role
pipenv run invenio users create [email protected] --password 123456 --active
pipenv run invenio roles add [email protected] admin
pipenv run invenio access allow superuser-access role superadmin
pipenv run invenio access allow admin-access role admin
pipenv run invenio access allow admin-access role moderator

# Import fixtures
pipenv run invenio fixtures institutions import
pipenv run invenio fixtures users import $(pipenv --where)/data/users.json
pipenv run invenio fixtures documents import hevs
pipenv run invenio fixtures documents import usi
16 changes: 11 additions & 5 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,26 +84,32 @@
'invenio_base.api_apps': [
'documents = sonar.modules.documents:Documents',
'institutions = sonar.modules.institutions:Institutions'
],
],
'invenio_jsonschemas.schemas': [
'documents = sonar.modules.documents.jsonschemas',
'institutions = sonar.modules.institutions.jsonschemas'
'institutions = sonar.modules.institutions.jsonschemas',
'users = sonar.modules.users.jsonschemas'
],
'invenio_search.mappings': [
'documents = sonar.modules.documents.mappings',
'institutions = sonar.modules.institutions.mappings'
'institutions = sonar.modules.institutions.mappings',
'users = sonar.modules.users.mappings'
],
'invenio_pidstore.minters': [
'document_id = \
sonar.modules.documents.api:document_pid_minter',
'institution_id = \
sonar.modules.institutions.api:institution_pid_minter'
sonar.modules.institutions.api:institution_pid_minter',
'user_id = \
sonar.modules.users.api:user_pid_minter'
],
'invenio_pidstore.fetchers': [
'document_id = \
sonar.modules.documents.api:document_pid_fetcher',
'institution_id = \
sonar.modules.institutions.api:institution_pid_fetcher'
sonar.modules.institutions.api:institution_pid_fetcher',
'user_id = \
sonar.modules.users.api:user_pid_fetcher'
],
"invenio_records.jsonresolver": [
"institution = sonar.modules.institutions.jsonresolvers"
Expand Down
34 changes: 34 additions & 0 deletions sonar/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@

from sonar.modules.documents.api import DocumentRecord, DocumentSearch
from sonar.modules.institutions.api import InstitutionRecord, InstitutionSearch
from sonar.modules.users.api import UserRecord, UserSearch


def _(x):
Expand Down Expand Up @@ -279,6 +280,39 @@ def _(x):
update_permission_factory_imp=allow_all,
delete_permission_factory_imp=allow_all,
list_permission_factory_imp=allow_all
),
'user': dict(
pid_type='user',
pid_minter='user_id',
pid_fetcher='user_id',
default_endpoint_prefix=True,
record_class=UserRecord,
search_class=UserSearch,
indexer_class=RecordIndexer,
search_index='users',
search_type=None,
record_serializers={
'application/json': ('sonar.modules.users.serializers'
':json_v1_response'),
},
search_serializers={
'application/json': ('sonar.modules.users.serializers'
':json_v1_search'),
},
record_loaders={
'application/json': ('sonar.modules.users.loaders'
':json_v1'),
},
list_route='/users/',
item_route='/users/<pid(user):pid_value>',
default_media_type='application/json',
max_result_window=10000,
error_handlers=dict(),
create_permission_factory_imp=allow_all,
jma marked this conversation as resolved.
Show resolved Hide resolved
read_permission_factory_imp=check_elasticsearch,
update_permission_factory_imp=allow_all,
delete_permission_factory_imp=allow_all,
list_permission_factory_imp=allow_all
)
}
"""REST endpoints."""
Expand Down
5 changes: 5 additions & 0 deletions sonar/modules/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

from flask import current_app
from invenio_db import db
from invenio_indexer.api import RecordIndexer
from invenio_jsonschemas import current_jsonschemas
from invenio_pidstore.errors import PIDDoesNotExistError
from invenio_pidstore.models import PersistentIdentifier
Expand Down Expand Up @@ -90,6 +91,10 @@ def dbcommit(self):
"""Commit changes to db."""
db.session.commit()

def reindex(self):
"""Reindex record."""
RecordIndexer().index(self)


class SonarSearch(RecordsSearch):
"""Search Class SONAR."""
Expand Down
2 changes: 2 additions & 0 deletions sonar/modules/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

from .documents.cli import documents
from .institutions.cli import institutions
from .users.cli import users


@click.group()
Expand All @@ -29,3 +30,4 @@ def fixtures():

fixtures.add_command(documents)
fixtures.add_command(institutions)
fixtures.add_command(users)
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,7 @@
"title": "ISBN",
"description": "ISBN of the resource.",
"type": "string",
"pattern": "^97[8|9][0-9]{10}$",
"validationMessage": "Should be a valid ISBN-13 without dashes."
"pattern": "^97[8|9][0-9]{10}$"
}
}
},
Expand All @@ -111,7 +110,6 @@
"description": "Bibligraphic code of language.",
"type": "string",
"default": "fre",
"validationMessage": "Bibliographic language code is required.",
"enum": [
"fre",
"ger",
Expand Down
21 changes: 21 additions & 0 deletions sonar/modules/users/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
#
# Swiss Open Access Repository
# Copyright (C) 2019 RERO
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, version 3 of the License.
#
# 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 Affero 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 <http://www.gnu.org/licenses/>.


"""User init script."""

from __future__ import absolute_import, print_function
56 changes: 56 additions & 0 deletions sonar/modules/users/api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# -*- coding: utf-8 -*-
#
# Swiss Open Access Repository
# Copyright (C) 2019 RERO
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, version 3 of the License.
#
# 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 Affero 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 <http://www.gnu.org/licenses/>.

"""User Api."""


from functools import partial

from ..api import SonarRecord, SonarSearch
from ..fetchers import id_fetcher
from ..minters import id_minter
from ..providers import Provider

# provider
UserProvider = type(
'UserProvider',
(Provider,),
dict(pid_type='user')
)
# minter
user_pid_minter = partial(id_minter, provider=UserProvider)
# fetcher
user_pid_fetcher = partial(id_fetcher, provider=UserProvider)


class UserSearch(SonarSearch):
"""Search users."""

class Meta:
"""Search only on item index."""

index = 'users'
doc_types = []


class UserRecord(SonarRecord):
"""User record class."""

minter = user_pid_minter
fetcher = user_pid_fetcher
provider = UserProvider
schema = 'user'
99 changes: 99 additions & 0 deletions sonar/modules/users/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# -*- coding: utf-8 -*-
#
# Swiss Open Access Repository
# Copyright (C) 2019 RERO
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, version 3 of the License.
#
# 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 Affero 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 <http://www.gnu.org/licenses/>.

"""Click command-line interface for user management."""

from __future__ import absolute_import, print_function

import json

import click
from click.exceptions import ClickException
from flask import current_app
from flask.cli import with_appcontext
from flask_security.confirmable import confirm_user
from invenio_accounts.ext import hash_password
from werkzeug.local import LocalProxy

from ..users.api import UserRecord

datastore = LocalProxy(lambda: current_app.extensions['security'].datastore)


@click.group()
def users():
"""Users CLI commands."""


@users.command('import')
@click.argument('infile', type=click.File('r'))
@with_appcontext
def import_users(infile):
"""Import users."""
click.secho('Importing users from {file}'.format(file=infile))

data = json.load(infile)
for user_data in data:
try:
email = user_data.get('email')

# No email found in user's data, account cannot be created
if not email:
raise ClickException('Email not defined')

user = datastore.find_user(email=email)

# User already exists, skip account creation
if user:
raise ClickException(
'User with email {email} already exists'.format(
email=email))

password = user_data.get('password', '123456')
password = hash_password(password)
del user_data['password']

roles = user_data.get('roles', []).copy()
if not roles or not isinstance(roles, list):
roles = []

# Create account and activate it
datastore.create_user(email=email, password=password, roles=roles)
datastore.commit()
user = datastore.find_user(email=email)
confirm_user(user)
datastore.commit()

# Store account ID in user resource
user_data['user_id'] = user.id

click.secho(
'User {email} with ID #{id} created successfully'.format(
email=email, id=user.id),
fg='green')

# Create user resource
user = UserRecord.create(user_data, dbcommit=True)
user.reindex()

except Exception as error:
click.secho(
'User {user} could not be imported: {error}'.format(
user=user_data, error=str(error)),
fg='red')

click.secho('Finished', fg='green')
Loading