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

COR-476: add user/id endpoint #410

Open
wants to merge 3 commits into
base: COR-475_identity
Choose a base branch
from
Open
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
9 changes: 7 additions & 2 deletions frontend/apps/core-app/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
} from '@expo-google-fonts/roboto';

import { AuthWrapper } from './src/components/AuthWrapper';
import { formsClient } from './src/clients/formsClient';
import { adminClient, formsClient } from './src/clients';
import { RootNavigator } from './src/navigation';
import { linkingConfig } from './src/navigation/linking.config';

Expand All @@ -40,10 +40,15 @@ const App: React.FC = () => {

if (!fontsLoaded) return null;

const handleTokenChange = (token: string) => {
formsClient.setToken(token);
adminClient.setToken(token);
};

return (
<NativeBaseProvider theme={theme}>
<NavigationContainer linking={linkingConfig} theme={navTheme}>
<AuthWrapper onTokenChange={formsClient.setToken}>
<AuthWrapper onTokenChange={handleTokenChange}>
<RootNavigator />
</AuthWrapper>
</NavigationContainer>
Expand Down
6 changes: 6 additions & 0 deletions frontend/apps/core-app/src/clients/adminClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import Constants from 'expo-constants';
import { AdminClient } from 'core-api-client';

export const adminClient = new AdminClient(
Constants.manifest?.extra?.server_uri,
);
2 changes: 2 additions & 0 deletions frontend/apps/core-app/src/clients/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { formsClient } from './client';
export { adminClient } from './adminClient';
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
} from 'core-api-client';
import { Recipient } from 'core-api-client/src/types/client/Recipient';

import { formsClient } from '../../../../clients/formsClient';
import { formsClient } from '../../../../clients';
import { render } from '../../../../testUtils/render';
import { RecipientEditorComponent } from '../RecipientEditor.component';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
} from 'core-api-client';
import { Recipient } from 'core-api-client/src/types/client/Recipient';

import { formsClient } from '../../../../clients/formsClient';
import { formsClient } from '../../../../clients';
import { render } from '../../../../testUtils/render';
import { RecipientViewerComponent } from '../RecipientViewer.component';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { FormControl, Select } from 'native-base';
import { useFormContext, useController } from 'react-hook-form';
import { FormDefinition, Record, Validation } from 'core-api-client';

import { formsClient } from '../../../clients/formsClient';
import { formsClient } from '../../../clients';

type Props = {
formId: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {

import { RecordEditor } from '..';
import * as ReactHookFormTransformer from '../../../utils/ReactHookFormTransformer';
import { formsClient } from '../../../clients/formsClient';
import { formsClient } from '../../../clients';
import { SubFormTable } from '../../SubFormTable';

type Props = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { act, fireEvent, waitFor } from '@testing-library/react-native';
import { FormDefinition, Record } from 'core-api-client';

import { formsClient } from '../../../../clients/formsClient';
import { formsClient } from '../../../../clients';
import { render } from '../../../../testUtils/render';
import { withFormContext } from '../../../../testUtils/withFormContext';
import { ReferenceFieldInput } from '../ReferenceFieldInput.component';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as React from 'react';
import { RouteProp, useRoute } from '@react-navigation/native';

import { RootParamList } from '../../navigation/types';
import { formsClient } from '../../clients/formsClient';
import { formsClient } from '../../clients';
import config from '../../config';
import { useAPICall } from '../../hooks/useAPICall';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { FormWithRecord } from 'core-api-client';
import { Recipient } from 'core-api-client/src/types/client/Recipient';
import { useNavigation } from '@react-navigation/native';

import { formsClient } from '../../clients/formsClient';
import { formsClient } from '../../clients';
import configuration from '../../config';
import { useAPICall } from '../../hooks/useAPICall';
import { routes } from '../../constants/routes';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { fireEvent, waitFor } from '@testing-library/react-native';
import { FormType, FormWithRecord } from 'core-api-client';
import { Recipient } from 'core-api-client/src/types/client/Recipient';

import { formsClient } from '../../../clients/formsClient';
import { formsClient } from '../../../clients';
import { render } from '../../../testUtils/render';
import * as ReactHookFormTransformer from '../../../utils/ReactHookFormTransformer';
import { RecipientRegistrationScreenComponent } from '../RecipientRegistrationScreen.component';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { render } from '../../../testUtils/render';
import { RecipientRegistrationScreenContainer } from '../RecipientRegistrationScreen.container';
import * as hooks from '../../../hooks/useAPICall';
import configuration from '../../../config';
import { formsClient } from '../../../clients/formsClient';
import { formsClient } from '../../../clients';
import { routes } from '../../../constants/routes';

const makeForm = (i: number): FormDefinition => ({
Expand Down
4 changes: 4 additions & 0 deletions frontend/packages/core-api-client/src/client/Client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { FolderClient } from './Folder';
import { FormClient } from './Form';
import { RecordClient } from './Record';
import { RecipientClient } from './Recipient';
import { IdentityClient } from './Identity';

export class Client extends BaseRESTClient implements ClientDefinition {
static corev1 = 'apis/core.nrc.no/v1';
Expand All @@ -20,12 +21,15 @@ export class Client extends BaseRESTClient implements ClientDefinition {

public Recipient: RecipientClient;

public Identity: IdentityClient;

constructor(address: string) {
super(`${address}/${Client.corev1}`);
this.Database = new DatabaseClient(this);
this.Folder = new FolderClient(this);
this.Form = new FormClient(this);
this.Record = new RecordClient(this, this.Form);
this.Recipient = new RecipientClient(this.Record, this.Form);
this.Identity = new IdentityClient(this);
}
}
20 changes: 20 additions & 0 deletions frontend/packages/core-api-client/src/client/Identity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import {
Identity,
IdentityClientDefinition,
IdentityGetRequest,
IdentityGetResponse,
} from '../types';

import { BaseRESTClient } from './BaseRESTClient';

export class IdentityClient implements IdentityClientDefinition {
restClient: BaseRESTClient;

constructor(restClient: BaseRESTClient) {
this.restClient = restClient;
}

get = async (request: IdentityGetRequest): Promise<IdentityGetResponse> => {
return this.restClient.get<Identity>(`/identities/${request.id}`);
};
}
2 changes: 1 addition & 1 deletion frontend/packages/core-api-client/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as Validation from './validation';

export { Client } from './client';
export * from './client';
export { BaseRESTClient } from './client/BaseRESTClient';
export * from './types';
export * from './utils';
Expand Down
2 changes: 2 additions & 0 deletions frontend/packages/core-api-client/src/types/client/Client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import { FolderClientDefinition } from './Folder';
import { FormClientDefinition } from './Form';
import { RecordClientDefinition } from './Record';
import { RecipientClientDefinition } from './Recipient';
import { IdentityClientDefinition } from './Identity';

export interface ClientDefinition {
Database: DatabaseClientDefinition;
Folder: FolderClientDefinition;
Form: FormClientDefinition;
Record: RecordClientDefinition;
Recipient: RecipientClientDefinition;
Identity: IdentityClientDefinition;
}
2 changes: 1 addition & 1 deletion frontend/packages/core-api-client/src/types/client/Form.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FormDefinition, FormDefinitionList } from '../model/Form';
import { FormDefinition, FormDefinitionList } from '../model';

import { DataOperation, PartialObjectWrapper, Response } from './utils';

Expand Down
18 changes: 18 additions & 0 deletions frontend/packages/core-api-client/src/types/client/Identity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { DataOperation, Response } from './utils';

export type Identity = {
id: string;
subject: string;
displayName: string;
fullName: string;
email: string;
emailVerified: boolean;
};

export type IdentityGetRequest = { id: string };

export type IdentityGetResponse = Response<undefined, Identity>;

export interface IdentityClientDefinition {
get: DataOperation<IdentityGetRequest, IdentityGetResponse>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ export * from './Database';
export * from './Folder';
export * from './Form';
export * from './Record';
export * from './Identity';

export * from './utils';
11 changes: 11 additions & 0 deletions pkg/api/types/identity_profile.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package types

// IdentityProfile represents a user including the claims from the auth request
type IdentityProfile struct {
ID string `json:"id"`
Subject string `json:"subject"`
DisplayName string `json:"displayName"`
FullName string `json:"fullName"`
Email string `json:"email"`
EmailVerified bool `json:"emailVerified"`
}
1 change: 1 addition & 0 deletions pkg/constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ const (
ParamFolderID = "folderId"
ParamOrganizationID = "organizationId"
ParamIdentityProviderID = "identityProviderId"
ParamIdentityID = "identityId"
)
5 changes: 5 additions & 0 deletions pkg/server/authnzapi/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/nrc-no/core/pkg/server/authnzapi/handlers/identityprovider"
"github.com/nrc-no/core/pkg/server/authnzapi/handlers/organization"
"github.com/nrc-no/core/pkg/server/generic"
"github.com/nrc-no/core/pkg/server/login/handlers/identity"
"github.com/nrc-no/core/pkg/server/options"
"github.com/nrc-no/core/pkg/store"
"github.com/ory/hydra-client-go/client/admin"
Expand Down Expand Up @@ -51,6 +52,10 @@ func NewServer(options Options) (*Server, error) {
idpHandler := identityprovider.NewHandler(idpStore)
container.Add(idpHandler.WebService())

identityStore := store.NewIdentityProfileStore(options.StoreFactory)
identityHandler := identity.NewHandler(identityStore)
container.Add(identityHandler.WebService())

s := &Server{
Server: genericServer,
options: options,
Expand Down
42 changes: 42 additions & 0 deletions pkg/server/login/handlers/identity/get.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package identity

import (
"github.com/emicklei/go-restful/v3"
"github.com/nrc-no/core/pkg/api/meta"
"github.com/nrc-no/core/pkg/constants"
"github.com/nrc-no/core/pkg/logging"
"github.com/nrc-no/core/pkg/utils"
uuid "github.com/satori/go.uuid"
"go.uber.org/zap"
"net/http"
)

func (h *Handler) Get(identityId string) http.HandlerFunc {
return func(w http.ResponseWriter, req *http.Request) {
l := logging.NewLogger(req.Context()).With(zap.String("identity_id", identityId))

l.Debug("validating identity id")
if _, err := uuid.FromString(identityId); err != nil {
l.Error("failed to validate identity id", zap.Error(err))
utils.ErrorResponse(w, meta.NewBadRequest("invalid identity id"))
return
}

l.Debug("getting identity")
identity, err := h.store.Get(req.Context(), identityId)
if err != nil {
l.Error("failed to get identity", zap.Error(err))
utils.ErrorResponse(w, err)
return
}

l.Debug("successfully got identity")
utils.JSONResponse(w, http.StatusOK, identity)
}
}

func (h *Handler) RestfulGet(request *restful.Request, response *restful.Response) {
identityId := request.PathParameter(constants.ParamIdentityID)
handler := h.Get(identityId)
handler(response.ResponseWriter, request.Request)
}
43 changes: 43 additions & 0 deletions pkg/server/login/handlers/identity/handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package identity

import (
"fmt"
"github.com/emicklei/go-restful/v3"
"github.com/nrc-no/core/pkg/api/mimetypes"
"github.com/nrc-no/core/pkg/api/types"
"github.com/nrc-no/core/pkg/constants"
"github.com/nrc-no/core/pkg/store"
"net/http"
)

type Handler struct {
store store.IdentityProfileStore
webService *restful.WebService
}

func (h *Handler) WebService() *restful.WebService {
return h.webService
}

func NewHandler(store store.IdentityProfileStore) *Handler {
h := &Handler{store: store}

ws := new(restful.WebService).
Path("/apis/core.nrc.no/v1/identities").
Doc("identities.admin.nrc.no API")

h.webService = ws

ws.Route(ws.GET(fmt.Sprintf("/{%s}", constants.ParamIdentityID)).To(h.RestfulGet).
Doc("gets an identity").
Operation("getIdentity").
Param(restful.PathParameter(constants.ParamIdentityID, "identity id").
DataType("string").
DataFormat("uuid").
Required(true)).
Produces(mimetypes.ApplicationJson).
Writes(types.IdentityProfile{}).
Returns(http.StatusOK, "OK", types.IdentityProfile{}))

return h
}
4 changes: 4 additions & 0 deletions pkg/store/constants.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
package store

const idpStoreName = "identity_provider"
const identityStoreName = "identity_profile"
const databaseStoreName = "database"
const formStoreName = "form"
const organizationStoreName = "organization"
4 changes: 2 additions & 2 deletions pkg/store/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ func (d *databaseStore) Delete(ctx context.Context, databaseID string) error {

// List implements DatabaseStore.List
func (d *databaseStore) List(ctx context.Context) (*types.DatabaseList, error) {
ctx, db, l, done, err := actionContext(ctx, d.db, "database", "list")
ctx, db, l, done, err := actionContext(ctx, d.db, databaseStoreName, "list")
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -172,7 +172,7 @@ func (d *databaseStore) List(ctx context.Context) (*types.DatabaseList, error) {

// Create implements DatabaseStore.Create
func (d *databaseStore) Create(ctx context.Context, database *types.Database) (*types.Database, error) {
ctx, db, l, done, err := actionContext(ctx, d.db, "database", "create")
ctx, db, l, done, err := actionContext(ctx, d.db, databaseStoreName, "create")
if err != nil {
return nil, err
}
Expand Down
Loading