diff --git a/Dockerfile.run b/Dockerfile.run index 907af71..bd39e27 100644 --- a/Dockerfile.run +++ b/Dockerfile.run @@ -15,6 +15,8 @@ COPY ./dist/${BIN_NAME} /opt/app/${BIN_NAME} ADD ./dist/docs/ /opt/app/docs/ ADD ./dist/static/ /opt/app/static/ ADD ./dist/config.toml /opt/app/config.toml +ADD ./dist/authorization_model.conf /opt/app/authorization_model.conf +ADD ./dist/authorization_policy.csv /opt/app/authorization_policy.csv # Sets and executes the app. WORKDIR /opt/app diff --git a/Makefile b/Makefile index 2c87da1..6811a76 100644 --- a/Makefile +++ b/Makefile @@ -67,6 +67,8 @@ build: clean create-build-container cp -R static/* ./dist/static/ cp -R docs/swagger/ ./dist/docs/ cp config.${DEPLOY_ENV}.toml ./dist/config.toml + cp authorization_model.conf ./dist/authorization_model.conf + cp authorization_policy.csv ./dist/authorization_policy.csv ################################################# ## Building, based on the current environment. ## diff --git a/application/applicationservice/organization.go b/application/applicationservice/organization.go index 9d4d635..e297d7f 100644 --- a/application/applicationservice/organization.go +++ b/application/applicationservice/organization.go @@ -35,37 +35,42 @@ func (s *organizationService) GetAllTypes() ([]viewmodel.OrganizationType, error return s.mapEntity.Organization.ToOrganizationTypeModelSlice(result), nil } -func (s *organizationService) GetByType(organizationTypeKey string) ([]viewmodel.Organization, error) { - result, err := s.svc.Organization.GetByType(organizationTypeKey) +func (s *organizationService) GetByType(organizationTypeKey string, user viewmodel.User) ([]viewmodel.Organization, error) { + userEntity := s.mapEntity.User.ToUserEntity(user) + result, err := s.svc.Organization.GetByType(organizationTypeKey, userEntity) if err != nil { return nil, err } + return s.mapEntity.Organization.ToOrganizationModelSlice(result), nil } -func (s *organizationService) GetByUUID(organizationUUID string) (viewmodel.Organization, error) { - result, err := s.svc.Organization.GetByUUID(organizationUUID) +func (s *organizationService) GetByUUID(organizationUUID string, user viewmodel.User) (viewmodel.Organization, error) { + userEntity := s.mapEntity.User.ToUserEntity(user) + result, err := s.svc.Organization.GetByUUID(organizationUUID, userEntity) if err != nil { return viewmodel.Organization{}, err } return s.mapEntity.Organization.ToOrganizationModel(result), nil } -func (s *organizationService) GetByName(name string, searchType string) ([]viewmodel.Organization, error) { - result, err := s.svc.Organization.GetByName(name, searchType) +func (s *organizationService) GetByName(name string, searchType string, user viewmodel.User) ([]viewmodel.Organization, error) { + userEntity := s.mapEntity.User.ToUserEntity(user) + result, err := s.svc.Organization.GetByName(name, searchType, userEntity) if err != nil { return nil, err } return s.mapEntity.Organization.ToOrganizationModelSlice(result), nil } -func (s *organizationService) SetParentOrganization(organizationUUID string, parentOrganizationUUID string) (viewmodel.Organization, error) { - child, err := s.svc.Organization.GetByUUID(organizationUUID) +func (s *organizationService) SetParentOrganization(organizationUUID string, parentOrganizationUUID string, user viewmodel.User) (viewmodel.Organization, error) { + userEntity := s.mapEntity.User.ToUserEntity(user) + child, err := s.svc.Organization.GetByUUID(organizationUUID, userEntity) if err != nil { return viewmodel.Organization{}, err } - parent, err := s.svc.Organization.GetByUUID(parentOrganizationUUID) + parent, err := s.svc.Organization.GetByUUID(parentOrganizationUUID, userEntity) if err != nil { return viewmodel.Organization{}, err } @@ -74,10 +79,11 @@ func (s *organizationService) SetParentOrganization(organizationUUID string, par return viewmodel.Organization{}, err } - return s.GetByUUID(organizationUUID) + return s.GetByUUID(organizationUUID, user) } -func (s *organizationService) InactivateOrganizationAddress(organizationUUID string, address viewmodel.OrganizationAddress) error { +func (s *organizationService) InactivateOrganizationAddress(organizationUUID string, address viewmodel.OrganizationAddress, userView viewmodel.User) error { + userEntity := s.mapEntity.User.ToUserEntity(userView) entityAddress := s.mapEntity.Organization.ToOrganizationAddressEntity(address) entityAddress.Organization = &entity.Organization{ UUID: organizationUUID, @@ -89,14 +95,15 @@ func (s *organizationService) InactivateOrganizationAddress(organizationUUID str } entityAddress.UpdatedUser = user - if err := s.svc.Organization.InactivateOrganizationAddress(entityAddress); err != nil { + if err := s.svc.Organization.InactivateOrganizationAddress(entityAddress, userEntity); err != nil { return err } else { return nil } } -func (s *organizationService) SetOrganizationAddress(organizationUUID string, address viewmodel.OrganizationAddress) (viewmodel.OrganizationAddress, error) { +func (s *organizationService) SetOrganizationAddress(organizationUUID string, address viewmodel.OrganizationAddress, userView viewmodel.User) (viewmodel.OrganizationAddress, error) { + userEntity := s.mapEntity.User.ToUserEntity(userView) entityAddress := s.mapEntity.Organization.ToOrganizationAddressEntity(address) entityAddress.Organization = &entity.Organization{ UUID: organizationUUID, @@ -110,7 +117,7 @@ func (s *organizationService) SetOrganizationAddress(organizationUUID string, ad entityAddress.CreatedUser = user entityAddress.UpdatedUser = user - entityAddress, err = s.svc.Organization.SetOrganizationAddress(entityAddress) + entityAddress, err = s.svc.Organization.SetOrganizationAddress(entityAddress, userEntity) if err != nil { return viewmodel.OrganizationAddress{}, err } @@ -118,7 +125,8 @@ func (s *organizationService) SetOrganizationAddress(organizationUUID string, ad return s.mapEntity.Organization.ToOrganizationAddressModel(entityAddress), nil } -func (s *organizationService) InactivateOrganizationContact(organizationUUID string, contact viewmodel.OrganizationContact) error { +func (s *organizationService) InactivateOrganizationContact(organizationUUID string, contact viewmodel.OrganizationContact, userView viewmodel.User) error { + userEntity := s.mapEntity.User.ToUserEntity(userView) entityContact := s.mapEntity.Organization.ToOrganizationContactEntity(contact) entityContact.Organization = &entity.Organization{ UUID: organizationUUID, @@ -130,14 +138,15 @@ func (s *organizationService) InactivateOrganizationContact(organizationUUID str } entityContact.UpdatedUser = user - if err := s.svc.Organization.InactivateOrganizationContact(entityContact); err != nil { + if err := s.svc.Organization.InactivateOrganizationContact(entityContact, userEntity); err != nil { return err } else { return nil } } -func (s *organizationService) SetOrganizationContact(organizationUUID string, contact viewmodel.OrganizationContact) (viewmodel.OrganizationContact, error) { +func (s *organizationService) SetOrganizationContact(organizationUUID string, contact viewmodel.OrganizationContact, userView viewmodel.User) (viewmodel.OrganizationContact, error) { + userEntity := s.mapEntity.User.ToUserEntity(userView) entityContact := s.mapEntity.Organization.ToOrganizationContactEntity(contact) entityContact.Organization = &entity.Organization{ UUID: organizationUUID, @@ -151,7 +160,7 @@ func (s *organizationService) SetOrganizationContact(organizationUUID string, co entityContact.CreatedUser = user entityContact.UpdatedUser = user - entityContact, err = s.svc.Organization.SetOrganizationContact(entityContact) + entityContact, err = s.svc.Organization.SetOrganizationContact(entityContact, userEntity) if err != nil { return viewmodel.OrganizationContact{}, err } @@ -221,10 +230,10 @@ func (s *organizationService) AddOrganization(organization viewmodel.Organizatio } } - enOrg, err = s.svc.Organization.AddOrganization(enOrg) + enOrg, err = s.svc.Organization.AddOrganization(enOrg, enUser) if err != nil { return viewmodel.Organization{}, nil } - return s.GetByUUID(enOrg.UUID) + return s.GetByUUID(enOrg.UUID, user) } diff --git a/application/applicationservice/user.go b/application/applicationservice/user.go index 0c9224b..1772486 100644 --- a/application/applicationservice/user.go +++ b/application/applicationservice/user.go @@ -71,11 +71,12 @@ func (s *userService) Login(email string, pass string) (retVal viewmodel.User, e return s.mapEntity.User.ToUserModel(user), nil } -func (s *userService) Create(user viewmodel.User) (retVal viewmodel.User, err error) { +func (s *userService) Create(user viewmodel.User, author viewmodel.User) (retVal viewmodel.User, err error) { entity := s.mapEntity.User.ToUserEntity(user) + enAuthor := s.mapEntity.User.ToUserEntity(author) for i, _ := range entity.Organizations { - entity.Organizations[i], err = s.svc.Organization.GetByUUID(entity.Organizations[i].UUID) + entity.Organizations[i], err = s.svc.Organization.GetByUUID(entity.Organizations[i].UUID, enAuthor) if err != nil { return retVal, err } @@ -89,13 +90,14 @@ func (s *userService) Create(user viewmodel.User) (retVal viewmodel.User, err er return s.mapEntity.User.ToUserModel(entity), nil } -func (s *userService) CreateBulk(users []viewmodel.User) (retVal []viewmodel.User, err error) { +func (s *userService) CreateBulk(users []viewmodel.User, author viewmodel.User) (retVal []viewmodel.User, err error) { entities := s.mapEntity.User.ToUserEntitySlice(users) + enAuthor := s.mapEntity.User.ToUserEntity(author) organizations := make([]entity.Organization, 0) for i, _ := range entities { if i == 0 { for o, _ := range entities[i].Organizations { - org, err := s.svc.Organization.GetByUUID(entities[i].Organizations[o].UUID) + org, err := s.svc.Organization.GetByUUID(entities[i].Organizations[o].UUID, enAuthor) if err != nil { return nil, err } diff --git a/authorization_model.conf b/authorization_model.conf new file mode 100644 index 0000000..afc86f8 --- /dev/null +++ b/authorization_model.conf @@ -0,0 +1,11 @@ +[request_definition] +r = role, objectsRole, orgType, objectsOrgType, orgRelation, objectsRelation, obj, act + +[policy_definition] +p = role, objectsRole, orgType, objectsOrgType, orgRelation, objectsRelation, obj, act + +[policy_effect] +e = some(where (p.eft == allow)) && !some(where (p.eft == deny)) + +[matchers] +m = keyMatch(r.role, p.role) && keyMatch(r.objectsRole, p.objectsRole) && keyMatch(r.orgType, p.orgType) && keyMatch(r.objectsOrgType, p.objectsOrgType) && keyMatch(r.objectsRelation, p.objectsRelation) && keyMatch(r.orgRelation, p.orgRelation) && keyMatch(r.obj, p.obj) && (r.act == p.act || p.act == "*") \ No newline at end of file diff --git a/authorization_policy.csv b/authorization_policy.csv new file mode 100644 index 0000000..107e45f --- /dev/null +++ b/authorization_policy.csv @@ -0,0 +1,111 @@ +p, AD, *, *, *, *, *, *, * +p, *, *, *, *, *, *, /v1/authenticate/portal, POST +p, *, *, *, *, *, *, /v1/notification/ws, GET +p, *, *, *, *, *, *, /health/, GET +p, *, *, *, *, *, *, /v1/nemt/users/contacttype, GET +p, *, *, *, *, *, [self], /v1/nemt/users/*, GET +p, *, *, *, *, *, [self], /v1/nemt/users/portal/*, DELETE +p, *, *, *, *, *, [self], /v1/nemt/users/portal/*, POST +p, *, *, *, *, *, [self], /v1/nemt/users/portal/*, GET +p, *AD, *, *, *, *, *, /v1/nemt/users/, GET +p, BDCAD, SP, *, *, *, [other], /v1/nemt/users/*/, GET +p, BDCAD, SP, *, *, *, [other], /v1/nemt/users/portal/*, DELETE +p, BDCAD, SP, *, *, *, [other], /v1/nemt/users/portal/*, POST +p, BDCAD, SP, *, *, *, [other], /v1/nemt/users/portal/*, GET +p, BDCAD, SPT, *, *, *, [other], /v1/nemt/users/*/, GET +p, BDCAD, SPT, *, *, *, [other], /v1/nemt/users/portal/*, DELETE +p, BDCAD, SPT, *, *, *, [other], /v1/nemt/users/portal/*, POST +p, BDCAD, SPT, *, *, *, [other], /v1/nemt/users/portal/*, GET +p, BDCAD, US, *, *, *, [other], /v1/nemt/users/*/, GET +p, BDCAD, US, *, *, *, [other], /v1/nemt/users/portal/*, DELETE +p, BDCAD, US, *, *, *, [other], /v1/nemt/users/portal/*, POST +p, BDCAD, US, *, *, *, [other], /v1/nemt/users/portal/*, GET +p, BCBSIAD, SP, *, *, *, [other], /v1/nemt/users/*/, GET +p, BCBSIAD, SP, *, *, *, [other], /v1/nemt/users/portal/*, DELETE +p, BCBSIAD, SP, *, *, *, [other], /v1/nemt/users/portal/*, POST +p, BCBSIAD, SP, *, *, *, [other], /v1/nemt/users/portal/*, GET +p, BCBSIAD, US, *, *, *, [other], /v1/nemt/users/*/, GET +p, BCBSIAD, US, *, *, *, [other], /v1/nemt/users/portal/*, DELETE +p, BCBSIAD, US, *, *, *, [other], /v1/nemt/users/portal/*, POST +p, BCBSIAD, US, *, *, *, [other], /v1/nemt/users/portal/*, GET +p, BCBSIAD, SPT, *, *, *, [other], /v1/nemt/users/*/, GET +p, BCBSIAD, SPT, *, *, *, [other], /v1/nemt/users/portal/*, DELETE +p, BCBSIAD, SPT, *, *, *, [other], /v1/nemt/users/portal/*, POST +p, BCBSIAD, SPT, *, *, *, [other], /v1/nemt/users/portal/*, GET +p, PLANAD, SPT, *, *, [equal], [other], /v1/nemt/users/*/, GET +p, PLANAD, SPT, *, *, [equal], [other], /v1/nemt/users/portal/*, DELETE +p, PLANAD, SPT, *, *, [equal], [other], /v1/nemt/users/portal/*, POST +p, PLANAD, SPT, *, *, [equal], [other], /v1/nemt/users/portal/*, GET +p, PLANAD, US, *, *, [equal], [other], /v1/nemt/users/*/, GET +p, PLANAD, US, *, *, [equal], [other], /v1/nemt/users/portal/*, DELETE +p, PLANAD, US, *, *, [equal], [other], /v1/nemt/users/portal/*, POST +p, PLANAD, US, *, *, [equal], [other], /v1/nemt/users/portal/*, GET +p, PLANAD, SP, *, *, [equal], [other], /v1/nemt/users/*/, GET +p, PLANAD, SP, *, *, [equal], [other], /v1/nemt/users/portal/*, DELETE +p, PLANAD, SP, *, *, [equal], [other], /v1/nemt/users/portal/*, POST +p, PLANAD, SP, *, *, [equal], [other], /v1/nemt/users/portal/*, GET +p, SCHDAD, US, *, *, [parent], [other], /v1/nemt/users/*/, GET +p, SCHDAD, US, *, *, [parent], [other], /v1/nemt/users/portal/*, DELETE +p, SCHDAD, US, *, *, [parent], [other], /v1/nemt/users/portal/*, POST +p, SCHDAD, US, *, *, [parent], [other], /v1/nemt/users/portal/*, GET +p, SCHDAD, SP, *, *, [parent], [other], /v1/nemt/users/*/, GET +p, SCHDAD, SP, *, *, [parent], [other], /v1/nemt/users/portal/*, DELETE +p, SCHDAD, SP, *, *, [parent], [other], /v1/nemt/users/portal/*, POST +p, SCHDAD, SP, *, *, [parent], [other], /v1/nemt/users/portal/*, GET +p, SCHDAD, SPT, *, *, [parent], [other], /v1/nemt/users/*/, GET +p, SCHDAD, SPT, *, *, [parent], [other], /v1/nemt/users/portal/*, DELETE +p, SCHDAD, SPT, *, *, [parent], [other], /v1/nemt/users/portal/*, POST +p, SCHDAD, SPT, *, *, [parent], [other], /v1/nemt/users/portal/*, GET +p, SCHDAD, SP, *, *, [equal*], [other], /v1/nemt/users/*/, GET +p, SCHDAD, SP, *, *, [equal*], [other], /v1/nemt/users/portal/*, DELETE +p, SCHDAD, SP, *, *, [equal*], [other], /v1/nemt/users/portal/*, POST +p, SCHDAD, SP, *, *, [equal*], [other], /v1/nemt/users/portal/*, GET +p, SCHDAD, US, *, *, [equal*], [other], /v1/nemt/users/*/, GET +p, SCHDAD, US, *, *, [equal*], [other], /v1/nemt/users/portal/*, DELETE +p, SCHDAD, US, *, *, [equal*], [other], /v1/nemt/users/portal/*, POST +p, SCHDAD, US, *, *, [equal*], [other], /v1/nemt/users/portal/*, GET +p, SCHDAD, SPT, *, *, [equal*], [other], /v1/nemt/users/*/, GET +p, SCHDAD, SPT, *, *, [equal*], [other], /v1/nemt/users/portal/*, DELETE +p, SCHDAD, SPT, *, *, [equal*], [other], /v1/nemt/users/portal/*, POST +p, SCHDAD, SPT, *, *, [equal*], [other], /v1/nemt/users/portal/*, GET +p, SCHDAD, SCHDAD, *, *, [equal*], [other], /v1/nemt/users/*/, GET +p, SCHDAD, SCHDAD, *, *, [equal*], [other], /v1/nemt/users/portal/*, DELETE +p, SCHDAD, SCHDAD, *, *, [equal*], [other], /v1/nemt/users/portal/*, POST +p, SCHDAD, SCHDAD, *, *, [equal*], [other], /v1/nemt/users/portal/*, GET +p, SPT, *, programsupport, *, *, [other], /v1/nemt/users/, GET +p, SPT, *, programsupport, *, *, [other], /v1/nemt/users/*, GET +p, *, *, *, *, *, *, /v1/nemt/organization/name, GET +p, *, *, *, *, *, *, /v1/nemt/organization/type, GET +p, AD, *, *, *, *, *, /v1/nemt/organization/*, GET +p, AD, *, *, *, *, *, /v1/nemt/organization/*, POST +p, AD, *, *, *, *, *, /v1/nemt/organization/*, PUT +p, SCHDAD, *, *, *, [equal*], *, /v1/nemt/organization/*, GET +p, SCHDAD, *, *, *, [equal*], *, /v1/nemt/organization/*, POST +p, SCHDAD, *, *, *, [equal*], *, /v1/nemt/organization/*, PUT +p, PLANAD, *, *, *, [equal*], *, /v1/nemt/organization/*, GET +p, PLANAD, *, *, *, [equal*], *, /v1/nemt/organization/*, POST +p, PLANAD, *, *, *, [equal*], *, /v1/nemt/organization/*, PUT +p, BDCAD, *, *, *, *, *, /v1/nemt/organization/*, GET +p, BDCAD, *, *, *, *, *, /v1/nemt/organization/*, POST +p, BDCAD, *, *, *, *, *, /v1/nemt/organization/*, PUT +p, BCBSIAD, *, *, *, *, *, /v1/nemt/organization/*, GET +p, BCBSIAD, *, *, *, *, *, /v1/nemt/organization/*, POST +p, BCBSIAD, *, *, *, *, *, /v1/nemt/organization/*, PUT +p, SPT, *, programsupport, *, *, *, /v1/nemt/organization/*, GET +p, SP, *, provider, *, *, *, /v1/nemt/organization, GET +p, SP, *, plan, *, *, *, /v1/nemt/organization, GET +p, SPT, *, programsupport, *, *, *, /v1/nemt/organization, GET +p, SCHDAD, *, provider, *, *, *, /v1/nemt/organization, GET +p, BCBSIAD, *, bcbsi, *, *, *, /v1/nemt/organization, GET +p, BDCAD, *, techsupport, *, *, *, /v1/nemt/organization, GET +p, PLANAD, *, plan, *, *, *, /v1/nemt/organization, GET +p, AD, *, *, *, *, *, /v1/nemt/organization, GET +p, SP, *, provider, *, *, *, /v1/nemt/eligibility, POST +p, SP, *, plan, *, *, *, /v1/nemt/eligibility, POST +p, SPT, *, programsupport, *, *, *, /v1/nemt/eligibility, POST +p, SCHDAD, *, provider, *, *, *, /v1/nemt/eligibility, POST +p, BCBSIAD, *, bcbsi, *, *, *, /v1/nemt/eligibility, POST +p, BDCAD, *, techsupport, *, *, *, /v1/nemt/eligibility, POST +p, PLANAD, *, plan, *, *, *, /v1/nemt/eligibility, POST +p, AD, *, *, *, *, *, /v1/nemt/eligibility, POST + diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml index 044ba8c..26efc53 100644 --- a/bitbucket-pipelines.yml +++ b/bitbucket-pipelines.yml @@ -19,6 +19,8 @@ pipelines: - cp -R static/* ./dist/static/ - cp -R docs/swagger/ ./dist/docs/ - cp config.prd.toml ./dist/config.toml + - cp authorization_model.conf ./dist/authorization_model.conf + - cp authorization_policy.csv ./dist/authorization_policy.csv - docker build -f Dockerfile.run -t nemt-portal-api-run:prod --force-rm --build-arg BIN_NAME=nemt-portal-api --build-arg APP_NAME=nemt-portal-api . - apt-get update -y && apt-get upgrade -y && apt-get dist-upgrade -y && apt-get -y autoremove && apt-get clean - apt-get install python3-pip -y @@ -46,6 +48,8 @@ pipelines: - cp -R static/* ./dist/static/ - cp -R docs/swagger/ ./dist/docs/ - cp config.stg.toml ./dist/config.toml + - cp authorization_model.conf ./dist/authorization_model.conf + - cp authorization_policy.csv ./dist/authorization_policy.csv - docker build -f Dockerfile.run -t nemt-portal-api-run:dev --force-rm --build-arg BIN_NAME=nemt-portal-api --build-arg APP_NAME=nemt-portal-api . - apt-get update -y && apt-get upgrade -y && apt-get dist-upgrade -y && apt-get -y autoremove && apt-get clean - apt-get install python3-pip -y diff --git a/config.dev.toml b/config.dev.toml index 9919890..522d299 100644 --- a/config.dev.toml +++ b/config.dev.toml @@ -37,6 +37,7 @@ db = 0 pass = "3rdaP3KL2x%V" prefix = "nemt-portal-api-dev" default-expiration = "5m" +master-name = "devmaster01" [log] log-to-file = false diff --git a/config.prd.toml b/config.prd.toml index d8309e8..cf9324c 100644 --- a/config.prd.toml +++ b/config.prd.toml @@ -37,6 +37,7 @@ db = 0 pass = "3rdaP3KL2x%V" prefix = "portal-api-prod" default-expiration = "5m" +master-name = "master01" [log] log-to-file = false diff --git a/config.stg.toml b/config.stg.toml index 050b811..6375f45 100644 --- a/config.stg.toml +++ b/config.stg.toml @@ -37,6 +37,7 @@ db = 0 pass = "3rdaP3KL2x%V" prefix = "portal-api-test" default-expiration = "5m" +master-name = "devmaster01" [log] log-to-file = false diff --git a/data/datamysql/notification.go b/data/datamysql/notification.go index 3d5f834..c6b9166 100644 --- a/data/datamysql/notification.go +++ b/data/datamysql/notification.go @@ -80,7 +80,9 @@ func (c *notificationRepo) getQuery() string { INNER JOIN tab_login e ON c.user_id = e.user_id INNER JOIN tab_login f - ON d.user_id = f.user_id` + ON d.user_id = f.user_id + INNER JOIN tab_ride g + ON g.ride_id = a.ride_id ` } func (c *notificationRepo) GetLastNotificationFromPhoneNumber(notificationType string, phoneNumber string, status string) (entity.Notification, error) { diff --git a/data/datamysql/organization.go b/data/datamysql/organization.go index 9dccc9b..41d9bf6 100644 --- a/data/datamysql/organization.go +++ b/data/datamysql/organization.go @@ -20,6 +20,63 @@ func newOrganizationRepo(conn executor) *organizationRepo { } } +func (c *organizationRepo) getProfileQuery(user entity.User) (query string, where string, err error) { + if len(user.Profiles) > 0 { + for _, p := range user.Profiles { + switch p.Key { + case "AD", "BCBSIAD", "BDCAD", "PLANAD": + switch p.Organization.Type.Key { + case "techsupport", "bcbsi", "bcbsa": + return + case "plan": + query = ` INNER JOIN tab_provider e + ON e.provider_id = a.organization_reference_id + AND b.organization_type_key = 'plan' + LEFT JOIN tab_organization f + ON f.organization_id = a.organization_parent_id ` + where = fmt.Sprintf(` AND (a.organization_uuid = '%s' OR f.organization_uuid = '%s') `, p.Organization.UUID, p.Organization.UUID) + case "provider": + query = ` INNER JOIN tab_provider e + ON e.provider_id = a.organization_reference_id + AND b.organization_type_key = 'provider' + LEFT JOIN tab_organization f + ON f.organization_id = a.organization_parent_id ` + where = fmt.Sprintf(` AND (a.organization_uuid = '%s' OR f.organization_uuid = '%s') `, p.Organization.UUID, p.Organization.UUID) + return + } + case "SP", "SPT": + switch p.Organization.Type.Key { + case "techsupport", "bcbsi", "bcbsa": + return + case "plan": + query = ` INNER JOIN tab_provider e + ON e.provider_id = a.organization_reference_id + AND b.organization_type_key = 'plan' + LEFT JOIN tab_organization f + ON f.organization_id = a.organization_parent_id ` + where = fmt.Sprintf(` AND (a.organization_uuid = '%s' OR f.organization_uuid = '%s') `, p.Organization.UUID, p.Organization.UUID) + case "provider": + query = ` INNER JOIN tab_provider e + ON e.provider_id = a.organization_reference_id + AND b.organization_type_key = 'provider' + LEFT JOIN tab_organization f + ON f.organization_id = a.organization_parent_id ` + where = fmt.Sprintf(` AND (a.organization_uuid = '%s' OR f.organization_uuid = '%s') `, p.Organization.UUID, p.Organization.UUID) + return + } + } + } + + if query == "" && where == "" { + return "", "", fmt.Errorf("Invalid Query") + } else { + return + } + } else { + return "", "", fmt.Errorf("User has no profile to search") + } +} + func (c *organizationRepo) getQuery() string { const ( query = `SELECT @@ -237,42 +294,73 @@ func (c *organizationRepo) GetAllTypes() ([]entity.OrganizationType, error) { } func (c *organizationRepo) GetTypeByKey(key string) (entity.OrganizationType, error) { - return c.parseTypeEntity(c.conn.QueryRow(c.getTypeQuery()+" WHERE organization_type_key=?", key)) + return c.parseTypeEntity(c.conn.QueryRow(c.getTypeQuery()+" WHERE b.organization_type_key=?", key)) } -func (c *organizationRepo) GetByType(organizationTypeKey string) ([]entity.Organization, error) { +func (c *organizationRepo) GetByType(organizationTypeKey string, user entity.User) ([]entity.Organization, error) { + query, where, err := c.getProfileQuery(user) + if err != nil { + return nil, err + } + if organizationTypeKey == "" { - return c.parseSet(c.conn.Query(c.getQuery())) + return c.parseSet(c.conn.Query(c.getQuery() + query + " WHERE 1 = 1 " + where)) } else { - return c.parseSet(c.conn.Query(c.getQuery()+" WHERE b.organization_type_key = ? ", organizationTypeKey)) + return c.parseSet(c.conn.Query(c.getQuery()+query+" WHERE b.organization_type_key = ? "+where, organizationTypeKey)) } } -func (c *organizationRepo) GetByName(name string, searchType string) ([]entity.Organization, error) { - finalQuery := c.getQuery() + " WHERE a.organization_name LIKE ?" +func (c *organizationRepo) GetByName(name string, searchType string, user entity.User) ([]entity.Organization, error) { + query, where, err := c.getProfileQuery(user) + if err != nil { + return nil, err + } + + finalQuery := c.getQuery() + query + " WHERE a.organization_name LIKE ? " switch searchType { case "parent": - finalQuery += " AND a.organization_parent_id > 0; " + finalQuery += " AND a.organization_parent_id > 0 " case "child": - finalQuery += " AND a.organization_parent_id = 0; " + finalQuery += " AND a.organization_parent_id = 0 " } - name = "%" + name + "%" - fmt.Println(finalQuery) + finalQuery += where + name = "%" + name + "%" return c.parseSet(c.conn.Query(finalQuery, name)) } -func (c *organizationRepo) GetByUUID(organizationUUID string) (entity.Organization, error) { - return c.parseEntity(c.conn.QueryRow(c.getQuery()+" WHERE a.organization_uuid = ? ", organizationUUID)) +func (c *organizationRepo) GetByUUID(organizationUUID string, user entity.User) (entity.Organization, error) { + query, where, err := c.getProfileQuery(user) + if err != nil { + return entity.Organization{}, err + } + + query = c.getQuery() + query + " WHERE a.organization_uuid = ? " + where + + return c.parseEntity(c.conn.QueryRow(query, organizationUUID)) } -func (c *organizationRepo) GetByID(organizationID int64) (entity.Organization, error) { - return c.parseEntity(c.conn.QueryRow(c.getQuery()+" WHERE a.organization_id = ? ", organizationID)) +func (c *organizationRepo) GetByID(organizationID int64, user entity.User) (entity.Organization, error) { + query, where, err := c.getProfileQuery(user) + if err != nil { + return entity.Organization{}, err + } + + query = c.getQuery() + query + " WHERE a.organization_id = ? " + where + + return c.parseEntity(c.conn.QueryRow(query, organizationID)) } -func (c *organizationRepo) GetChildsByID(organizationID int64) ([]entity.Organization, error) { - return c.parseSet(c.conn.Query(c.getQuery()+" WHERE a.organization_parent_id = ? ", organizationID)) +func (c *organizationRepo) GetChildsByID(organizationID int64, user entity.User) ([]entity.Organization, error) { + query, where, err := c.getProfileQuery(user) + if err != nil { + return nil, err + } + + query = c.getQuery() + query + " WHERE a.organization_parent_id = ? " + where + + return c.parseSet(c.conn.Query(query, organizationID)) } func (c *organizationRepo) GetContactsByOrganizationUUID(organizationUUID string) ([]entity.OrganizationContact, error) { @@ -312,7 +400,7 @@ func (c *organizationRepo) SetParentOrganization(organizationID int64, parentOrg } } -func (c *organizationRepo) InactivateOrganizationAddress(address entity.OrganizationAddress) error { +func (c *organizationRepo) InactivateOrganizationAddress(address entity.OrganizationAddress, user entity.User) error { const ( query = "UPDATE tab_organization_address SET active = 0, updated = CURRENT_TIMESTAMP, updated_user_id = ? WHERE organization_id = ? and organization_address_uuid = ?" ) @@ -321,7 +409,7 @@ func (c *organizationRepo) InactivateOrganizationAddress(address entity.Organiza return errors.NewNotFoundError() } - organization, err := c.GetByUUID(address.Organization.UUID) + organization, err := c.GetByUUID(address.Organization.UUID, user) if err != nil { return err } @@ -334,7 +422,7 @@ func (c *organizationRepo) InactivateOrganizationAddress(address entity.Organiza } } -func (c *organizationRepo) SetOrganizationAddress(address entity.OrganizationAddress) (entity.OrganizationAddress, error) { +func (c *organizationRepo) SetOrganizationAddress(address entity.OrganizationAddress, user entity.User) (entity.OrganizationAddress, error) { const ( query = "INSERT INTO tab_organization_address(organization_address_uuid, organization_id, internal_id, `name`, address, `desc`, lat, `long`, created_user_id, updated_user_id) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?);" ) @@ -343,7 +431,7 @@ func (c *organizationRepo) SetOrganizationAddress(address entity.OrganizationAdd return entity.OrganizationAddress{}, errors.NewNotFoundError() } - organization, err := c.GetByUUID(address.Organization.UUID) + organization, err := c.GetByUUID(address.Organization.UUID, user) if err != nil { return entity.OrganizationAddress{}, err } @@ -362,7 +450,7 @@ func (c *organizationRepo) SetOrganizationAddress(address entity.OrganizationAdd return address, nil } -func (c *organizationRepo) InactivateOrganizationContact(contact entity.OrganizationContact) error { +func (c *organizationRepo) InactivateOrganizationContact(contact entity.OrganizationContact, user entity.User) error { const ( query = "UPDATE tab_organization_contact SET active = 0, updated = CURRENT_TIMESTAMP, updated_user_id = ? WHERE organization_id = ? and organization_contact_uuid = ?" ) @@ -371,7 +459,7 @@ func (c *organizationRepo) InactivateOrganizationContact(contact entity.Organiza return errors.NewNotFoundError() } - organization, err := c.GetByUUID(contact.Organization.UUID) + organization, err := c.GetByUUID(contact.Organization.UUID, user) if err != nil { return err } @@ -384,7 +472,7 @@ func (c *organizationRepo) InactivateOrganizationContact(contact entity.Organiza } } -func (c *organizationRepo) SetOrganizationContact(contact entity.OrganizationContact) (entity.OrganizationContact, error) { +func (c *organizationRepo) SetOrganizationContact(contact entity.OrganizationContact, user entity.User) (entity.OrganizationContact, error) { const ( selectQuery = "SELECT a.contact_type_id, a.name, a.key FROM tab_contact_type a WHERE a.key = ?" query = "INSERT INTO tab_organization_contact(organization_contact_uuid, organization_id, contact_type_id, contact, contact_name, contact_desc, created_user_id, updated_user_id) VALUES(?, ?, ?, ?, ?, ?, ?, ?);" @@ -394,7 +482,7 @@ func (c *organizationRepo) SetOrganizationContact(contact entity.OrganizationCon return entity.OrganizationContact{}, errors.NewNotFoundError() } - organization, err := c.GetByUUID(contact.Organization.UUID) + organization, err := c.GetByUUID(contact.Organization.UUID, user) if err != nil { return entity.OrganizationContact{}, err } @@ -419,7 +507,7 @@ func (c *organizationRepo) SetOrganizationContact(contact entity.OrganizationCon return contact, nil } -func (c *organizationRepo) AddOrganization(organization entity.Organization) (entity.Organization, error) { +func (c *organizationRepo) AddOrganization(organization entity.Organization, user entity.User) (entity.Organization, error) { const ( queryOrgType = "SELECT a.organization_type_id FROM tab_organization_type a WHERE a.organization_type_key = ?;" queryParentOrg = "SELECT a.organization_id FROM tab_organization a WHERE a.organization_uuid = ?;" @@ -457,7 +545,7 @@ func (c *organizationRepo) AddOrganization(organization entity.Organization) (en if len(organization.Addresses) > 0 { for i, a := range organization.Addresses { a.Organization = &organization - address, err := c.SetOrganizationAddress(a) + address, err := c.SetOrganizationAddress(a, user) if err != nil { fmt.Println("Error to save addresses") return entity.Organization{}, err @@ -469,7 +557,7 @@ func (c *organizationRepo) AddOrganization(organization entity.Organization) (en if len(organization.Contacts) > 0 { for i, ct := range organization.Contacts { ct.Organization = &organization - contact, err := c.SetOrganizationContact(ct) + contact, err := c.SetOrganizationContact(ct, user) if err != nil { fmt.Println("Error to save contacts") return entity.Organization{}, err diff --git a/domain/contract/repo.go b/domain/contract/repo.go index d2196e8..d1775b8 100644 --- a/domain/contract/repo.go +++ b/domain/contract/repo.go @@ -68,23 +68,23 @@ type NotificationRepo interface { // ProviderRepo defines the data set for Rides type OrganizationRepo interface { GetAllTypes() ([]entity.OrganizationType, error) - GetByType(organizationTypeKey string) ([]entity.Organization, error) - GetByUUID(organizationUUID string) (entity.Organization, error) + GetByType(organizationTypeKey string, user entity.User) ([]entity.Organization, error) + GetByUUID(organizationUUID string, user entity.User) (entity.Organization, error) GetContactsByOrganizationUUID(organizationUUID string) ([]entity.OrganizationContact, error) GetContactsByOrganizationID(organizationID int64) ([]entity.OrganizationContact, error) GetContactsByUUID(contactUUID string) (entity.OrganizationContact, error) GetAddressByOrganizationUUID(organizationUUID string) ([]entity.OrganizationAddress, error) GetAddressByOrganizationID(organizationID int64) ([]entity.OrganizationAddress, error) GetAddressByUUID(contactUUID string) (entity.OrganizationAddress, error) - GetByID(organizationID int64) (entity.Organization, error) - GetChildsByID(organizationID int64) ([]entity.Organization, error) - GetByName(name string, searchType string) ([]entity.Organization, error) + GetByID(organizationID int64, user entity.User) (entity.Organization, error) + GetChildsByID(organizationID int64, user entity.User) ([]entity.Organization, error) + GetByName(name string, searchType string, user entity.User) ([]entity.Organization, error) SetParentOrganization(organizationID int64, parentOrganizationID int64) error - InactivateOrganizationAddress(address entity.OrganizationAddress) error - SetOrganizationAddress(address entity.OrganizationAddress) (entity.OrganizationAddress, error) - InactivateOrganizationContact(contact entity.OrganizationContact) error - SetOrganizationContact(contact entity.OrganizationContact) (entity.OrganizationContact, error) - AddOrganization(organization entity.Organization) (entity.Organization, error) + InactivateOrganizationAddress(address entity.OrganizationAddress, user entity.User) error + SetOrganizationAddress(address entity.OrganizationAddress, user entity.User) (entity.OrganizationAddress, error) + InactivateOrganizationContact(contact entity.OrganizationContact, user entity.User) error + SetOrganizationContact(contact entity.OrganizationContact, user entity.User) (entity.OrganizationContact, error) + AddOrganization(organization entity.Organization, user entity.User) (entity.Organization, error) GetTypeByKey(key string) (entity.OrganizationType, error) } diff --git a/domain/service/organization.go b/domain/service/organization.go index 79e72d2..f26c76b 100644 --- a/domain/service/organization.go +++ b/domain/service/organization.go @@ -20,16 +20,16 @@ func (s *organizationService) GetAllTypes() ([]entity.OrganizationType, error) { return s.svc.db.Organization().GetAllTypes() } -func (s *organizationService) GetByType(organizationTypeKey string) ([]entity.Organization, error) { - return s.svc.db.Organization().GetByType(organizationTypeKey) +func (s *organizationService) GetByType(organizationTypeKey string, user entity.User) ([]entity.Organization, error) { + return s.svc.db.Organization().GetByType(organizationTypeKey, user) } -func (s *organizationService) GetByName(name string, searchType string) ([]entity.Organization, error) { - return s.svc.db.Organization().GetByName(name, searchType) +func (s *organizationService) GetByName(name string, searchType string, user entity.User) ([]entity.Organization, error) { + return s.svc.db.Organization().GetByName(name, searchType, user) } -func (s *organizationService) GetByUUID(organizationUUID string) (entity.Organization, error) { - organization, err := s.svc.db.Organization().GetByUUID(organizationUUID) +func (s *organizationService) GetByUUID(organizationUUID string, user entity.User) (entity.Organization, error) { + organization, err := s.svc.db.Organization().GetByUUID(organizationUUID, user) if err != nil { return organization, err } @@ -44,13 +44,13 @@ func (s *organizationService) GetByUUID(organizationUUID string) (entity.Organiz return organization, err } - organization.ChildOrgs, err = s.svc.db.Organization().GetChildsByID(organization.ID) + organization.ChildOrgs, err = s.svc.db.Organization().GetChildsByID(organization.ID, user) if err != nil { return organization, err } if organization.ParentID > 0 { - parent, err := s.svc.db.Organization().GetByID(organization.ParentID) + parent, err := s.svc.db.Organization().GetByID(organization.ParentID, user) if err != nil { return organization, err } @@ -88,24 +88,24 @@ func (s *organizationService) GetAddressByUUID(contactUUID string) (entity.Organ return s.svc.db.Organization().GetAddressByUUID(contactUUID) } -func (s *organizationService) InactivateOrganizationAddress(address entity.OrganizationAddress) error { - return s.svc.db.Organization().InactivateOrganizationAddress(address) +func (s *organizationService) InactivateOrganizationAddress(address entity.OrganizationAddress, user entity.User) error { + return s.svc.db.Organization().InactivateOrganizationAddress(address, user) } -func (s *organizationService) SetOrganizationAddress(address entity.OrganizationAddress) (entity.OrganizationAddress, error) { - return s.svc.db.Organization().SetOrganizationAddress(address) +func (s *organizationService) SetOrganizationAddress(address entity.OrganizationAddress, user entity.User) (entity.OrganizationAddress, error) { + return s.svc.db.Organization().SetOrganizationAddress(address, user) } -func (s *organizationService) InactivateOrganizationContact(contact entity.OrganizationContact) error { - return s.svc.db.Organization().InactivateOrganizationContact(contact) +func (s *organizationService) InactivateOrganizationContact(contact entity.OrganizationContact, user entity.User) error { + return s.svc.db.Organization().InactivateOrganizationContact(contact, user) } -func (s *organizationService) SetOrganizationContact(contact entity.OrganizationContact) (entity.OrganizationContact, error) { - return s.svc.db.Organization().SetOrganizationContact(contact) +func (s *organizationService) SetOrganizationContact(contact entity.OrganizationContact, user entity.User) (entity.OrganizationContact, error) { + return s.svc.db.Organization().SetOrganizationContact(contact, user) } -func (s *organizationService) AddOrganization(organization entity.Organization) (entity.Organization, error) { - return s.svc.db.Organization().AddOrganization(organization) +func (s *organizationService) AddOrganization(organization entity.Organization, user entity.User) (entity.Organization, error) { + return s.svc.db.Organization().AddOrganization(organization, user) } func (s *organizationService) GetTypeByKey(key string) (entity.OrganizationType, error) { diff --git a/ecs.prd.json b/ecs.prd.json index 076f043..a2e2bfd 100644 --- a/ecs.prd.json +++ b/ecs.prd.json @@ -16,17 +16,8 @@ }, "portMappings": [ { - "containerPort": 5100 - } - ], - "environment": [ - { - "name": "SERVICE_5100_NAME", - "value": "portal-api" - }, - { - "name": "SERVICE_5100_TAGS", - "value": "api" + "containerPort": 5100, + "hostPort": 5100 } ] } diff --git a/ecs.stg.json b/ecs.stg.json index 1ea720d..9ff6ace 100644 --- a/ecs.stg.json +++ b/ecs.stg.json @@ -1,7 +1,7 @@ { "containerDefinitions": [ { - "name": "portal-api-dev", + "name": "portal-api", "image": "105690980714.dkr.ecr.us-east-2.amazonaws.com/nemt-portal-api:dev", "cpu": 128, "memory": 128, @@ -16,17 +16,8 @@ }, "portMappings": [ { - "containerPort": 5100 - } - ], - "environment": [ - { - "name": "SERVICE_5100_NAME", - "value": "portal-api" - }, - { - "name": "SERVICE_5100_TAGS", - "value": "api" + "containerPort": 5100, + "hostPort": 5100 } ] } diff --git a/glide.yaml b/glide.yaml index 09aa359..6c133b7 100644 --- a/glide.yaml +++ b/glide.yaml @@ -42,3 +42,7 @@ import: - package: github.com/ttacon/builder - package: github.com/casbin/casbin version: ~1.5.0 +- package: github.com/Knetic/govaluate + version: 9aa49832a739dcd78a5542ff189fb82c3e423116 +- package: github.com/pkg/errors + version: ^0.8.0 diff --git a/infra/cache/cache.go b/infra/cache/cache.go index d7c4f31..8c4eb11 100644 --- a/infra/cache/cache.go +++ b/infra/cache/cache.go @@ -31,10 +31,11 @@ type RedisCache struct { func Instance(cfg *config.Config) contract.CacheManager { once.Do(func() { client := redis.NewFailoverClient(&redis.FailoverOptions{ - MasterName: "master01", + MasterName: cfg.Cache.Master, SentinelAddrs: []string{fmt.Sprintf("%s:%v", cfg.Cache.Server, cfg.Cache.Port)}, Password: cfg.Cache.Pass, DB: cfg.Cache.DB, + MaxRetries: 10, }) instance = &RedisCache{cfg, client} diff --git a/infra/config/config.go b/infra/config/config.go index 2497985..d3b1f98 100644 --- a/infra/config/config.go +++ b/infra/config/config.go @@ -119,6 +119,7 @@ type CacheConfig struct { Pass string Prefix string DefaultExpiration time.Duration + Master string } // CacheConfig represents the configuration values about the documentation config. @@ -194,6 +195,7 @@ func Read() (*Config, error) { Pass: viper.GetString("cache.pass"), Prefix: viper.GetString("cache.prefix"), DefaultExpiration: viper.GetDuration("cache.default-expiration"), + Master: viper.GetString("cache.master-name"), }, Lyft: LyftConfig{ Client: viper.GetString("lyft.key"), diff --git a/server/router/organizationroute/controller.go b/server/router/organizationroute/controller.go index 527ec09..682a896 100644 --- a/server/router/organizationroute/controller.go +++ b/server/router/organizationroute/controller.go @@ -78,7 +78,12 @@ func (c *controller) handleAddOrganization(ctx echo.Context) error { func (c *controller) handle(ctx echo.Context) error { orgType, _ := routeutils.GetAndValidateStringQueryParam(ctx, "type", "Type is mandatory") - resp, err := c.svc.Organization.GetByType(orgType) + authUser, err := auth.GetUserDetail(ctx, c.cfg) + if err != nil { + return routeutils.HandleAPIError(ctx, err) + } + + resp, err := c.svc.Organization.GetByType(orgType, authUser) if err != nil { return routeutils.HandleAPIError(ctx, err) } @@ -92,7 +97,12 @@ func (c *controller) handleDetail(ctx echo.Context) error { return routeutils.HandleAPIError(ctx, err) } - resp, err := c.svc.Organization.GetByUUID(orgUUID) + authUser, err := auth.GetUserDetail(ctx, c.cfg) + if err != nil { + return routeutils.HandleAPIError(ctx, err) + } + + resp, err := c.svc.Organization.GetByUUID(orgUUID, authUser) if err != nil { return routeutils.HandleAPIError(ctx, err) } @@ -112,7 +122,12 @@ func (c *controller) handleParent(ctx echo.Context) error { return routeutils.HandleAPIError(ctx, err) } - resp, err := c.svc.Organization.SetParentOrganization(orgUUID, parent.UUID) + authUser, err := auth.GetUserDetail(ctx, c.cfg) + if err != nil { + return routeutils.HandleAPIError(ctx, err) + } + + resp, err := c.svc.Organization.SetParentOrganization(orgUUID, parent.UUID, authUser) if err != nil { return routeutils.HandleAPIError(ctx, err) } @@ -132,12 +147,17 @@ func (c *controller) handleChild(ctx echo.Context) error { return routeutils.HandleAPIError(ctx, err) } - _, err = c.svc.Organization.SetParentOrganization(child.UUID, orgUUID) + authUser, err := auth.GetUserDetail(ctx, c.cfg) if err != nil { return routeutils.HandleAPIError(ctx, err) } - resp, err := c.svc.Organization.GetByUUID(orgUUID) + _, err = c.svc.Organization.SetParentOrganization(child.UUID, orgUUID, authUser) + if err != nil { + return routeutils.HandleAPIError(ctx, err) + } + + resp, err := c.svc.Organization.GetByUUID(orgUUID, authUser) if err != nil { return routeutils.HandleAPIError(ctx, err) } @@ -151,11 +171,16 @@ func (c *controller) handleNameSearch(ctx echo.Context) error { return routeutils.HandleAPIError(ctx, err) } + authUser, err := auth.GetUserDetail(ctx, c.cfg) + if err != nil { + return routeutils.HandleAPIError(ctx, err) + } + searchType := "" searchType, _ = routeutils.GetAndValidateStringQueryParam(ctx, "type", "Type is mandatory") cache := cache.Instance(c.cfg) - cacheKey := ctx.Request().Method + ctx.Request().URL.RawPath + ctx.Request().URL.RawQuery + cacheKey := ctx.Request().Method + ctx.Request().URL.RawPath + ctx.Request().URL.RawQuery + authUser.ID resp := []viewmodel.Organization{} err = cache.GetStruct(cacheKey, &resp) @@ -163,7 +188,7 @@ func (c *controller) handleNameSearch(ctx echo.Context) error { if err != domain.ErrCacheMiss { ctx.Logger().Errorf(domain.LogProblemGettingFromCache, err) } - resp, err = c.svc.Organization.GetByName(name, searchType) + resp, err = c.svc.Organization.GetByName(name, searchType, authUser) if err != nil { return routeutils.HandleAPIError(ctx, err) } @@ -180,24 +205,24 @@ func (c *controller) handleRemoveAddress(ctx echo.Context) error { return routeutils.HandleAPIError(ctx, err) } + authUser, err := auth.GetUserDetail(ctx, c.cfg) + if err != nil { + return routeutils.HandleAPIError(ctx, err) + } + orgUUID, err := routeutils.GetAndValidateStringParam(ctx, "org_uuid", "Org ID is mandatory") if err != nil { return routeutils.HandleAPIError(ctx, err) } - uInt, err := auth.GetTokenDetail(ctx, c.cfg) - if err != nil { - return routeutils.HandleAPIError(ctx, err) - } - createdUser := uInt.(map[string]interface{}) - address.UpdatedUser.ID = createdUser["useruuid"].(string) + address.UpdatedUser.ID = authUser.ID - err = c.svc.Organization.InactivateOrganizationAddress(orgUUID, address) + err = c.svc.Organization.InactivateOrganizationAddress(orgUUID, address, authUser) if err != nil { return routeutils.HandleAPIError(ctx, err) } - resp, err := c.svc.Organization.GetByUUID(orgUUID) + resp, err := c.svc.Organization.GetByUUID(orgUUID, authUser) if err != nil { return routeutils.HandleAPIError(ctx, err) } @@ -217,20 +242,19 @@ func (c *controller) handleAddAddress(ctx echo.Context) error { return routeutils.HandleAPIError(ctx, err) } - uInt, err := auth.GetTokenDetail(ctx, c.cfg) + authUser, err := auth.GetUserDetail(ctx, c.cfg) if err != nil { return routeutils.HandleAPIError(ctx, err) } - createdUser := uInt.(map[string]interface{}) - address.CreatedUser.ID = createdUser["useruuid"].(string) - address.UpdatedUser.ID = address.CreatedUser.ID + address.CreatedUser.ID = authUser.ID + address.UpdatedUser.ID = authUser.ID - _, err = c.svc.Organization.SetOrganizationAddress(orgUUID, address) + _, err = c.svc.Organization.SetOrganizationAddress(orgUUID, address, authUser) if err != nil { return routeutils.HandleAPIError(ctx, err) } - resp, err := c.svc.Organization.GetByUUID(orgUUID) + resp, err := c.svc.Organization.GetByUUID(orgUUID, authUser) if err != nil { return routeutils.HandleAPIError(ctx, err) } @@ -250,19 +274,18 @@ func (c *controller) handleRemoveContact(ctx echo.Context) error { return routeutils.HandleAPIError(ctx, err) } - uInt, err := auth.GetTokenDetail(ctx, c.cfg) + authUser, err := auth.GetUserDetail(ctx, c.cfg) if err != nil { return routeutils.HandleAPIError(ctx, err) } - createdUser := uInt.(map[string]interface{}) - contact.UpdatedUser.ID = createdUser["useruuid"].(string) + contact.UpdatedUser.ID = authUser.ID - err = c.svc.Organization.InactivateOrganizationContact(orgUUID, contact) + err = c.svc.Organization.InactivateOrganizationContact(orgUUID, contact, authUser) if err != nil { return routeutils.HandleAPIError(ctx, err) } - resp, err := c.svc.Organization.GetByUUID(orgUUID) + resp, err := c.svc.Organization.GetByUUID(orgUUID, authUser) if err != nil { return routeutils.HandleAPIError(ctx, err) } @@ -282,20 +305,19 @@ func (c *controller) handleAddContact(ctx echo.Context) error { return routeutils.HandleAPIError(ctx, err) } - uInt, err := auth.GetTokenDetail(ctx, c.cfg) + authUser, err := auth.GetUserDetail(ctx, c.cfg) if err != nil { return routeutils.HandleAPIError(ctx, err) } - createdUser := uInt.(map[string]interface{}) - contact.CreatedUser.ID = createdUser["useruuid"].(string) - contact.UpdatedUser.ID = contact.CreatedUser.ID + contact.CreatedUser.ID = authUser.ID + contact.UpdatedUser.ID = authUser.ID - _, err = c.svc.Organization.SetOrganizationContact(orgUUID, contact) + _, err = c.svc.Organization.SetOrganizationContact(orgUUID, contact, authUser) if err != nil { return routeutils.HandleAPIError(ctx, err) } - resp, err := c.svc.Organization.GetByUUID(orgUUID) + resp, err := c.svc.Organization.GetByUUID(orgUUID, authUser) if err != nil { return routeutils.HandleAPIError(ctx, err) } diff --git a/server/router/providerroute/controller.go b/server/router/providerroute/controller.go index 360801c..0931520 100644 --- a/server/router/providerroute/controller.go +++ b/server/router/providerroute/controller.go @@ -104,8 +104,8 @@ func (c *controller) handleParticipating(ctx echo.Context) error { // long = -87.624225 // } - lat = 40.442875 - long = -80.003112 + lat = 41.819078 + long = -87.623129 if len(mukID) > 0 { query = "" @@ -152,8 +152,8 @@ func (c *controller) handleList(ctx echo.Context) error { providerParams := npdmodel.ProviderSearchParams{ Name: name, SearchBy: searchBy, - Latitude: 40.442875, - Longitude: -80.003112, + Latitude: 41.819078, + Longitude: -87.623129, Distance: distance, Limit: limit, Offset: 0, diff --git a/server/router/usersroute/controller.go b/server/router/usersroute/controller.go index 833bd5e..34ff679 100644 --- a/server/router/usersroute/controller.go +++ b/server/router/usersroute/controller.go @@ -274,6 +274,11 @@ func (c *controller) handleMember(ctx echo.Context) error { return routeutils.HandleAPIError(ctx, err) } + authUser, err := auth.GetUserDetail(ctx, c.cfg) + if err != nil { + return routeutils.HandleAPIError(ctx, err) + } + if user.PhoneNumber == nil && user.Email == nil || len(*user.PhoneNumber) == 0 && len(*user.Email) == 0 { return routeutils.ResponseAPIAuthError(ctx, "phonenumber or email is required", false) } @@ -314,7 +319,7 @@ func (c *controller) handleMember(ctx echo.Context) error { } user.Profiles = append(user.Profiles, profile) - user, err = c.svc.Users.Create(user) + user, err = c.svc.Users.Create(user, authUser) if err != nil { return routeutils.HandleAPIError(ctx, err) } @@ -328,6 +333,11 @@ func (c *controller) handleBulkPortal(ctx echo.Context) error { return routeutils.HandleAPIError(ctx, err) } + authUser, err := auth.GetUserDetail(ctx, c.cfg) + if err != nil { + return routeutils.HandleAPIError(ctx, err) + } + for i, _ := range users { if len(users[i].Profiles) == 0 { return routeutils.ResponseAPIAuthError(ctx, "profile is required", false) @@ -360,7 +370,7 @@ func (c *controller) handleBulkPortal(ctx echo.Context) error { } } - returnUser, err := c.svc.Users.CreateBulk(users) + returnUser, err := c.svc.Users.CreateBulk(users, authUser) if err != nil { return routeutils.HandleAPIError(ctx, err) } @@ -374,6 +384,11 @@ func (c *controller) handlePortal(ctx echo.Context) error { return routeutils.HandleAPIError(ctx, err) } + authUser, err := auth.GetUserDetail(ctx, c.cfg) + if err != nil { + return routeutils.HandleAPIError(ctx, err) + } + if len(user.Profiles) == 0 { return routeutils.ResponseAPIAuthError(ctx, "profile is required", false) } @@ -404,7 +419,7 @@ func (c *controller) handlePortal(ctx echo.Context) error { user.Name = fmt.Sprintf("%s %s", user.First, user.Last) } - user, err = c.svc.Users.Create(user) + user, err = c.svc.Users.Create(user, authUser) if err != nil { return routeutils.HandleAPIError(ctx, err) } diff --git a/server/server.go b/server/server.go index bb3202a..703acf0 100644 --- a/server/server.go +++ b/server/server.go @@ -52,16 +52,16 @@ func (s *Server) Run() error { s.srv.Debug = s.cfg.App.Debug - err := serverconfig.SetMiddlewares(s.srv, s.cfg, s.log, s.svc) - if err != nil { - return errors.Wrap(err) - } - entityMapper := entitymapping.New() notificationService := notificationservice.New(s.svc, entityMapper, s.cfg, s.cache) appService := applicationservice.New(s.svc, entityMapper, notificationService, s.cfg) tncService := tncservice.New(s.svc, entityMapper, s.cfg, notificationService) + err := serverconfig.SetMiddlewares(s.srv, s.cfg, s.log, s.svc, appService) + if err != nil { + return errors.Wrap(err) + } + router.Register(s.srv, s.cfg, appService, tncService, notificationService) err = s.srv.Start(fmt.Sprintf(":%d", s.cfg.HTTP.Port)) diff --git a/server/serverconfig/authorization.go b/server/serverconfig/authorization.go index ee59064..1797e3f 100644 --- a/server/serverconfig/authorization.go +++ b/server/serverconfig/authorization.go @@ -1,6 +1,8 @@ package serverconfig import ( + "fmt" + "bitbucket.org/nemt/nemt-portal-api/application/applicationservice" "bitbucket.org/nemt/nemt-portal-api/application/viewmodel" "bitbucket.org/nemt/nemt-portal-api/infra/auth" @@ -44,6 +46,8 @@ func MiddlewareWithConfig(cfg *config.Config, svc *applicationservice.Service, l config := &DefaultConfig + config.Enforcer = casbin.NewEnforcer("authorization_model.conf", "authorization_policy.csv") + config.Svc = svc config.Logger = log config.Application = cfg @@ -68,26 +72,130 @@ func setAuthorizationMiddleware(e *echo.Echo, log *logger.Logger, cfg *config.Co func (a *Config) CheckPermission(c echo.Context) bool { user, err := auth.GetUserDetail(c, a.Application) if err != nil { - a.Logger.Warningf("Cannot get user details. %v ", err) + a.Logger.Warningf("Cannot get user details. %v\n", err) + user = viewmodel.User{} } method := c.Request().Method path := c.Request().URL.Path - //objectOrganization := a.organizationGoverningObject(c, user) - return a.Enforcer.Enforce(user, path, method) + objectsRole, objectsOrganization, objectsOrganizationType, object := a.policyObjectAttributes(c, user) + + currentUsersOrganization := viewmodel.Organization{} + if len(user.Organizations) > 0 { + currentUsersOrganization = user.Organizations[0] + } + + currentUsersRole := viewmodel.Profile{} + if len(user.Profiles) > 0 { + currentUsersRole = user.Profiles[0] + } + + currentUsersOrganizationType := "" + if len(user.Profiles) > 0 { + currentUsersOrganizationType = user.Profiles[0].Organization.Type.Key + } + + orgRelation := organizationsRelation(currentUsersOrganization, objectsOrganization) + objRelation := a.objectRelation(object, user) + + // parameters to Enforce must match the request section of the authorization_model.conf + return a.Enforcer.Enforce(currentUsersRole.Key, + objectsRole.Key, + objectsOrganizationType, + currentUsersOrganizationType, + orgRelation, + objRelation, + path, + method) + } -func (a *Config) organizationGoverningObject(c echo.Context, userDetails viewmodel.User) (result viewmodel.Organization) { +// policyObjectAttributes returns all information about the object being accessed for the policy +// in case object exists and returns users information if it is a new object +func (a *Config) policyObjectAttributes(c echo.Context, userDetails viewmodel.User) (viewmodel.Profile, viewmodel.Organization, string, interface{}) { - existingUser := strings.Contains(c.Request().URL.Path, "/users") && len(c.ParamValues()) > 0 - newUser := strings.Contains(c.Request().URL.Path, "/users") && len(c.ParamValues()) <= 0 + var object interface{} + + const userIDParamName = "user_uuid" + existingUser := strings.Contains(c.Request().URL.Path, "/users/") && c.Param(userIDParamName) != "" + newUser := strings.Contains(c.Request().URL.Path, "/users/") && c.Param(userIDParamName) == "" + + const organizationIDParamName = "org_uuid" + existingOrganization := strings.Contains(c.Request().URL.Path, "/organization") && c.Param(organizationIDParamName) != "" + newOrganization := strings.Contains(c.Request().URL.Path, "/organization") && c.Param(organizationIDParamName) == "" + + fmt.Println("**********") + fmt.Printf("url %v\n", c.Param(userIDParamName)) + fmt.Printf("user %v\n", userDetails.ID) + fmt.Printf("existing %v\n", existingUser) + fmt.Printf("new %v\n", newUser) + fmt.Println("**********") switch { case existingUser: - user, _ := a.Svc.Users.GetByUUID(c.ParamValues()[0], "") - result = user.Organizations[0] - case newUser: - result = userDetails.Organizations[0] + object, _ = a.Svc.Users.GetByUUID(c.Param(userIDParamName), "") + case newUser && len(userDetails.Organizations) > 0: + object = userDetails + case existingOrganization: + object, _ = a.Svc.Organization.GetByUUID(c.Param(organizationIDParamName), userDetails) + case newOrganization: + object = viewmodel.Organization{} } - return + + objectsRole := viewmodel.Profile{} + switch obj := object.(type) { + case viewmodel.User: + if len(obj.Profiles) > 0 { + objectsRole = obj.Profiles[0] + } + } + + objectsOrganization := viewmodel.Organization{} + switch obj := object.(type) { + case viewmodel.User: + if len(obj.Profiles) > 0 { + objectsOrganization = obj.Profiles[0].Organization + } + case viewmodel.Organization: + objectsOrganization = obj + } + + objectsOrganizationType := objectsOrganization.Type.Key + + return objectsRole, objectsOrganization, objectsOrganizationType, object +} + +func organizationsRelation(requestOrganization, currentUsersOrganization viewmodel.Organization) string { + if requestOrganization.UUID == currentUsersOrganization.UUID { + return "[equal]" + } + + for _, childOrg := range currentUsersOrganization.ChildOrgs { + if childOrg.UUID == requestOrganization.UUID { + return "[equal-or-child]" + } + } + + for _, childOrg := range requestOrganization.ChildOrgs { + if childOrg.UUID == currentUsersOrganization.UUID { + return "[parent]" + } + } + + return "[unrelated]" +} + +// organizationGoverningObject returns the role that is the owner of the object that is being accessed +// in case object exists and returns users role if it is a new object +func (a *Config) objectRelation(object interface{}, currentUser viewmodel.User) string { + + switch obj := object.(type) { + case viewmodel.User: + if obj.ID == currentUser.ID { + return "[self]" + } else { + return "[other]" + } + } + return "[other]" } diff --git a/server/serverconfig/authorization_model.conf b/server/serverconfig/authorization_model.conf deleted file mode 100644 index 5f72726..0000000 --- a/server/serverconfig/authorization_model.conf +++ /dev/null @@ -1,11 +0,0 @@ -[request_definition] -r = role, obj, act - -[policy_definition] -p = role, obj, act - -[policy_effect] -e = some(where (p.eft == allow)) && !some(where (p.eft == deny)) - -[matchers] -m = keymatch(r.role, p.role) && keyMatch(r.obj, p.obj) && (r.act == p.act || p.act == "*") \ No newline at end of file diff --git a/server/serverconfig/serverconfig.go b/server/serverconfig/serverconfig.go index 0d3be75..04687a3 100644 --- a/server/serverconfig/serverconfig.go +++ b/server/serverconfig/serverconfig.go @@ -1,6 +1,7 @@ package serverconfig import ( + "bitbucket.org/nemt/nemt-portal-api/application/applicationservice" "bitbucket.org/nemt/nemt-portal-api/domain/service" "bitbucket.org/nemt/nemt-portal-api/infra/config" "bitbucket.org/nemt/nemt-portal-api/infra/errors" @@ -9,7 +10,7 @@ import ( ) // SetMiddlewares attaches middlewares to server -func SetMiddlewares(server *echo.Echo, cfg *config.Config, log *logger.Logger, svc *service.Service) error { +func SetMiddlewares(server *echo.Echo, cfg *config.Config, log *logger.Logger, svc *service.Service, appsvc *applicationservice.Service) error { setRecoverMiddleware(server) setGZIPMiddleware(server) setRequestIDMiddleware(server) @@ -17,7 +18,7 @@ func SetMiddlewares(server *echo.Echo, cfg *config.Config, log *logger.Logger, s setCORSMiddleware(server, cfg) setBodyLimitMiddleware(server) setRateLimitMiddleware(server) - //setAuthorizationMiddleware(server, log, svc) + //setAuthorizationMiddleware(server, log, cfg, appsvc) err := setJWTMiddleware(server, cfg) if err != nil {