Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • andretol/greg
1 result
Show changes
Commits on Source (11)
Showing
with 592 additions and 27 deletions
File added
This diff is collapsed.
File added
File added
File added
/* generated using https://google-webfonts-helper.herokuapp.com/fonts/roboto?subsets=latin */
/* roboto-regular - latin */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: url('./fonts/roboto-v29-latin-regular.eot'); /* IE9 Compat Modes */
src: local(''),
url('./fonts/roboto-v29-latin-regular.eot?#iefix')
format('embedded-opentype'),
/* IE6-IE8 */ url('./fonts/roboto-v29-latin-regular.woff2') format('woff2'),
/* Super Modern Browsers */ url('./fonts/roboto-v29-latin-regular.woff')
format('woff'),
/* Modern Browsers */ url('./fonts/roboto-v29-latin-regular.ttf')
format('truetype'),
/* Safari, Android, iOS */
url('./fonts/roboto-v29-latin-regular.svg#Roboto') format('svg'); /* Legacy iOS */
}
......@@ -13,6 +13,8 @@ import { UserProvider } from 'providers'
import reportWebVitals from './reportWebVitals'
import FeatureProvider from './providers/featureProvider'
import './index.css'
function appRoot() {
return (
<React.StrictMode>
......
......@@ -2,6 +2,8 @@ import { styled } from '@mui/material/styles'
import { Box, Link, useMediaQuery } from '@mui/material'
import LogoutIcon from '@mui/icons-material/Logout'
import { useTranslation } from 'react-i18next'
import { Link as RouterLink } from 'react-router-dom'
import { useUserContext } from 'contexts'
import LanguageSelector from 'components/languageselector'
import UserInfo from 'routes/components/userInfo'
......@@ -56,7 +58,11 @@ const Header = () => {
return (
<StyledHeader>
<MainContainer>
<LogoContainer>{getHeaderLogo()}</LogoContainer>
<LogoContainer>
<Link component={RouterLink} to="/">
{getHeaderLogo()}
</Link>
</LogoContainer>
<Menu>
<LanguageSelector noPadding />
{spaceAvailable && <UserInfo />}
......
......@@ -233,7 +233,10 @@ const InvitedGuests = ({ persons }: GuestProps) => {
</Typography>
</StyledAccordionSummary>
<AccordionDetails>
<Typography variant="body1">
<Typography
variant="body1"
sx={{ marginTop: '1rem', marginBottom: '1rem' }}
>
{t('common:sentInvitationsDescription')}
</Typography>
<GuestTable guests={guests} emptyText={t('common:noInvitations')} />
......@@ -266,7 +269,10 @@ const ActiveGuests = ({ persons }: GuestProps) => {
</Typography>
</StyledAccordionSummary>
<AccordionDetails>
<Typography variant="body1">
<Typography
variant="body1"
sx={{ marginTop: '1rem', marginBottom: '1rem' }}
>
{t('common:activeGuestsDescription')}
</Typography>
<GuestTable guests={guests} emptyText={t('common:noActiveGuests')} />
......@@ -305,7 +311,10 @@ const WaitingGuests = ({ persons }: GuestProps) => {
</Typography>
</StyledAccordionSummary>
<AccordionDetails>
<Typography variant="body1">
<Typography
variant="body1"
sx={{ marginTop: '1rem', marginBottom: '1rem' }}
>
<Trans i18nKey="common:waitingGuestsDescription">
Her godkjenner du gjester som har <b>registrert seg manuelt</b>.
Gjester som har FEIDE-bruker trenger ikke godkjenning.
......
......@@ -162,8 +162,10 @@ export default function GuestInfo({
<SponsorInfoButtons to="/sponsor" name={`${guest.first} ${guest.last}`} />
<form onSubmit={onSubmit}>
<Box sx={{ marginBottom: '2rem' }}>
<Typography variant="h2">{t('guestInfo.contactInfo')}</Typography>
<Typography variant="body1">
<Typography sx={{ marginBottom: '1rem' }} variant="h2">
{t('guestInfo.contactInfo')}
</Typography>
<Typography sx={{ marginBottom: '1rem' }} variant="body1">
{t('guestInfo.contactInfoBody')}
</Typography>
......@@ -173,7 +175,6 @@ export default function GuestInfo({
<TableRow>
<TableHeadCell align="left" colSpan={2}>
{t('guestInfo.contactInfoTableText')}
</TableHeadCell>
</TableRow>
</TableHead>
......@@ -204,7 +205,7 @@ export default function GuestInfo({
defaultValue={guest.email}
inputRef={ref}
onChange={onChange}
{...inputProps}
{...inputProps}
/>
{/* If the guest has not completed the registration process, he should have an invitation he has not responded to */}
......@@ -284,8 +285,12 @@ export default function GuestInfo({
</Button>
</Box>
</form>
<Typography variant="h2">{t('guestInfo.roleInfoHead')}</Typography>
<Typography variant="body1">{t('guestInfo.roleInfoBody')}</Typography>
<Typography sx={{ marginBottom: '1rem' }} variant="h2">
{t('guestInfo.roleInfoHead')}
</Typography>
<Typography sx={{ marginBottom: '1rem' }} variant="body1">
{t('guestInfo.roleInfoBody')}
</Typography>
<TableContainer>
<Table sx={{ minWidth: 650 }} aria-label="simple table">
<TableHead>
......
......@@ -84,7 +84,7 @@ const TableCell = styled(TableCellMui)({
})
const TableContainer = styled(TableContainerMui)({
marginBottom: '0.8rem',
marginBottom: '1rem',
borderRadius: '1%',
borderStyle: 'solid',
borderColor: 'black',
......@@ -194,8 +194,12 @@ export default function GuestRoleInfo({
to={`/sponsor/guest/${pid}`}
name={`${guest.first} ${guest.last}`}
/>
<Typography variant="h2">{t('sponsor.roleInfoHead')}</Typography>
<Typography variant="body1">{t('sponsor.roleInfoText')}</Typography>
<Typography sx={{ marginBottom: '1rem' }} variant="h2">
{t('sponsor.roleInfoHead')}
</Typography>
<Typography sx={{ marginBottom: '1rem' }} variant="body1">
{t('sponsor.roleInfoText')}
</Typography>
<form onSubmit={onSubmit}>
<TableContainer>
<Table sx={{ minWidth: 650 }} aria-label="simple table">
......
......@@ -182,8 +182,12 @@ function NewGuestRole({ guest, reloadGuestInfo }: NewGuestRoleProps) {
to={`/sponsor/guest/${pid}`}
name={`${guest.first} ${guest.last}`}
/>
<Typography variant="h2">{t('guest.headerText')}</Typography>
<Typography variant="body1">{t('guest.bodyText')}</Typography>
<Typography sx={{ marginBottom: '1rem' }} variant="h2">
{t('guest.headerText')}
</Typography>
<Typography sx={{ marginBottom: '1rem' }} variant="body1">
{t('guest.bodyText')}
</Typography>
<form onSubmit={onSubmit}>
<Stack spacing={2}>
<FormControl>
......
......@@ -57,14 +57,19 @@ function FrontPage() {
return (
<Page>
<SponsorGuestButtons registerNewGuestActive />
<Typography variant="h2">{t('register.registerHeading')}</Typography>
<Typography variant="body1">{t('register.registerText')}</Typography>
<Typography variant="h2" sx={{ marginBottom: '1rem' }}>
{t('register.registerHeading')}
</Typography>
<Typography variant="body1" sx={{ marginBottom: '1rem' }}>
{t('register.registerText')}
</Typography>
<Box
sx={{
borderStyle: () => (guests.length > 0 ? 'solid' : 'none'),
borderRadius: '0.3125rem',
borderColor: 'secondary.main',
padding: '0.4375 0.75rem',
marginBottom: '2rem',
}}
>
<TextField
......@@ -105,7 +110,6 @@ function FrontPage() {
})
)}
</Box>
<br />
<Button
variant="contained"
color="secondary"
......
......@@ -114,6 +114,24 @@ def test_persons_verified_filter_exclude(
assert results[0]["last_name"] == "Bar"
@pytest.mark.django_db
def test_person_create(client):
url = reverse("v1:person-list")
response = client.post(url, {"first_name": "foo木👍أ", "last_name": "غbar水"})
results = response.json()
assert results == {
"id": 1,
"first_name": "foo木👍أ",
"last_name": "غbar水",
"gender": None,
"date_of_birth": None,
"registration_completed_date": None,
"identities": [],
"roles": [],
"consents": [],
}
@pytest.mark.django_db
def test_add_role(
client, person, role_type_visiting_professor, sponsor_guy, unit_human_resources
......
......@@ -5,6 +5,9 @@ import pytest
from django.utils import timezone
from greg.models import (
Consent,
ConsentChoice,
ConsentType,
OrganizationalUnit,
Person,
Identity,
......@@ -190,3 +193,50 @@ def test_person_str(person: Person):
@pytest.mark.django_db
def test_person_repr(person: Person):
assert repr(person) == "Person(id=1, first_name='Test', last_name='Tester')"
@pytest.mark.django_db
def test_person_has_mandatory_consents(person):
"""
No consents exists.
Should return True
"""
assert person.has_mandatory_consents is True
@pytest.mark.django_db
def test_person_has_mandatory_consents_only_optional(person):
"""
An optional consent exists, and the person has not given consent.
Should return True
"""
ConsentType.objects.create(user_allowed_to_change=True)
assert person.has_mandatory_consents is True
@pytest.mark.django_db
def test_person_has_mandatory_consents_mandatory_exists(person):
"""
A mandatory consent exists, and the person has not given it.
Should return False
"""
ConsentType.objects.create(user_allowed_to_change=True, mandatory=True)
assert person.has_mandatory_consents is False
@pytest.mark.django_db
def test_person_has_mandatory_consents_mandatory_given(person):
"""
A mandatory consent exists, and the person has given it.
Should return True
"""
ct = ConsentType.objects.create(user_allowed_to_change=True, mandatory=True)
cc = ConsentChoice.objects.create(consent_type=ct)
Consent.objects.create(
person=person, type=ct, choice=cc, consent_given_at=timezone.now()
)
assert person.has_mandatory_consents is True
......@@ -31,8 +31,7 @@ class RoleInfoViewSet(ModelViewSet):
context={"sponsor": sponsor},
)
serializer.is_valid(raise_exception=True)
instance = serializer.update(role, serializer.validated_data)
instance.save()
serializer.update(role, serializer.validated_data)
return Response(status=status.HTTP_200_OK)
def create(self, request):
......
......@@ -243,7 +243,7 @@ class GregOIDCBackend(ValidatingOIDCBackend):
extended_userinfo = self.get_extended_userinfo(access_token)
if "norEduPersonNIN" in extended_userinfo:
userinfo["userid_nin"] = extended_userinfo["norEduPersonNIN"]
self._get_or_create_greg_user_profile(userinfo, user)
return user
def _get_sponsor_from_userinfo(self, userinfo: dict) -> Optional[Sponsor]:
......
......@@ -21,8 +21,8 @@ def test_invite_guest(client, user_sponsor, unit_foo, role_type_foo, mocker):
role_end_date = datetime.datetime.today() + datetime.timedelta(days=10)
data = {
"first_name": "Foo",
"last_name": "Bar",
"first_name": "foo木👍أ",
"last_name": "غbar",
"email": "test@example.com",
"role": {
"start_date": (role_start_date).strftime("%Y-%m-%d"),
......@@ -46,8 +46,8 @@ def test_invite_guest(client, user_sponsor, unit_foo, role_type_foo, mocker):
assert Person.objects.count() == 1
person = Person.objects.first()
assert person.first_name == "Foo"
assert person.last_name == "Bar"
assert person.first_name == "foo木👍أ"
assert person.last_name == "غbar"
assert Identity.objects.filter(
person=person,
......
......@@ -24,7 +24,7 @@ def test_userinfo_invited_get(client, invitation_link):
"sponsor_id": None,
"person_id": 1,
"first_name": "Foo",
"last_name": "Bar",
"last_name": "Baمr",
"email": "foo@example.org",
"mobile_phone": None,
"fnr": None,
......@@ -85,7 +85,7 @@ def test_userinfo_guest_get(client, log_in, user_person):
"person_id": 1,
"roles": [],
"consents": [],
"first_name": "Foo",
"first_name": "Fooم",
"last_name": "Bar",
"email": "foo@bar.com",
"mobile_phone": None,
......
......@@ -318,3 +318,141 @@ def test_invited_existing_user(invited_person, user_person):
user_profile = GregUserProfile.objects.get(user=user)
assert user_profile.person == old_person
@pytest.mark.django_db
def test_invited_feide_nin_from_extended(requests_mock, invited_person):
"""
Invitation login, user with existing person and no sponsor object,
nin only present in extended userinfo.
A GregUserProfile should be created, and the person should get feide-id and nin set
"""
person, invitation_link = invited_person
email = "foo@example.com"
feide_id = "bar@example.com"
nin = "12345678901"
access_token = "foo"
id_token = "foo"
payload = "foo"
requests_mock.get(
"https://auth.dataporten.no/openid/userinfo",
json={
"sub": "subsub",
"dataporten-userid_sec": [f"feide:{feide_id}"],
"name": f"{person.first_name} {person.last_name}",
"email": f"{email}",
},
)
requests_mock.get(
"https://api.dataporten.no/userinfo/v1/userinfo", json={"norEduPersonNIN": nin}
)
auth_request = RequestFactory().get("/foo", {"code": "foo", "state": "bar"})
auth_request.session = {"invite_id": invitation_link.uuid}
backend = GregOIDCBackend()
backend.request = auth_request
# No profile beforehand
assert len(GregUserProfile.objects.filter(person_id=person.id)) == 0
user = backend.get_or_create_user(access_token, id_token, payload)
# User profile exists and all things set correctly after
user_profile = GregUserProfile.objects.get(user=user)
assert user_profile.person == person
assert user_profile.person.fnr and user_profile.person.fnr.value == nin
assert (
user_profile.person.feide_id and user_profile.person.feide_id.value == feide_id
)
assert user_profile.sponsor is None
@pytest.mark.django_db
def test_invited_feide_nin_from_regular(requests_mock, invited_person):
"""
Invitation login, user with existing person and no sponsor object,
nin present in regular userinfo.
A GregUserProfile should be created, and the person should get feide-id and nin set
"""
person, invitation_link = invited_person
email = "foo@example.com"
feide_id = "bar@example.com"
nin = "12345678901"
access_token = "foo"
id_token = "foo"
payload = "foo"
requests_mock.get(
"https://auth.dataporten.no/openid/userinfo",
json={
"sub": "subsub",
"dataporten-userid_sec": [f"feide:{feide_id}", f"nin:{nin}"],
"name": f"{person.first_name} {person.last_name}",
"email": f"{email}",
},
)
requests_mock.get("https://api.dataporten.no/userinfo/v1/userinfo", json={})
auth_request = RequestFactory().get("/foo", {"code": "foo", "state": "bar"})
auth_request.session = {"invite_id": invitation_link.uuid}
backend = GregOIDCBackend()
backend.request = auth_request
# No profile beforehand
assert len(GregUserProfile.objects.filter(person_id=person.id)) == 0
user = backend.get_or_create_user(access_token, id_token, payload)
# User profile exists and all things set correctly after
user_profile = GregUserProfile.objects.get(user=user)
assert user_profile.person == person
assert user_profile.person.fnr and user_profile.person.fnr.value == nin
assert (
user_profile.person.feide_id and user_profile.person.feide_id.value == feide_id
)
assert user_profile.sponsor is None
@pytest.mark.django_db
def test_invited_feide_no_nin(requests_mock, invited_person):
"""
Invitation login, user with existing person and no sponsor object,
nin not present
A GregUserProfile should be created, and the person should get feide-id set
"""
person, invitation_link = invited_person
email = "foo@example.com"
feide_id = "bar@example.com"
access_token = "foo"
id_token = "foo"
payload = "foo"
requests_mock.get(
"https://auth.dataporten.no/openid/userinfo",
json={
"sub": "subsub",
"dataporten-userid_sec": [f"feide:{feide_id}"],
"name": f"{person.first_name} {person.last_name}",
"email": f"{email}",
},
)
requests_mock.get("https://api.dataporten.no/userinfo/v1/userinfo", json={})
auth_request = RequestFactory().get("/foo", {"code": "foo", "state": "bar"})
auth_request.session = {"invite_id": invitation_link.uuid}
backend = GregOIDCBackend()
backend.request = auth_request
# No profile beforehand
assert len(GregUserProfile.objects.filter(person_id=person.id)) == 0
user = backend.get_or_create_user(access_token, id_token, payload)
# User profile exists and all things set correctly after
user_profile = GregUserProfile.objects.get(user=user)
assert user_profile.person == person
assert user_profile.person.fnr is None
assert (
user_profile.person.feide_id and user_profile.person.feide_id.value == feide_id
)
assert user_profile.sponsor is None