Compare commits

3 Commits

Author SHA1 Message Date
GotPPay
ffab9fe30f fix grabbing organization from user 2018-05-11 14:48:21 +02:00
Senad Uka
4852a5586c upstream sync 2018-05-11 09:07:54 +02:00
Senad Uka
50a6362b67 upstream sync 2018-05-03 07:55:09 +02:00
42 changed files with 1210 additions and 215 deletions

View File

@@ -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

View File

@@ -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. ##

View File

@@ -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)
}

View File

@@ -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
}
@@ -147,3 +149,23 @@ func (s *userService) GetContactType() (retVal []viewmodel.ContactType, err erro
return s.mapEntity.User.ToContactTypeModelSlice(entity), nil
}
func (s *userService) SaveContact(contact viewmodel.Contact) (retVal viewmodel.Contact, err error) {
entity := s.mapEntity.User.ToContactEntity(contact)
entity, err = s.svc.Users.SaveContact(entity)
if err != nil {
return retVal, errors.Wrap(err)
}
return s.mapEntity.User.ToContactModel(entity), err
}
func (s *userService) RemoveContact(contact viewmodel.Contact) (retVal viewmodel.Contact, err error) {
entity := s.mapEntity.User.ToContactEntity(contact)
entity, err = s.svc.Users.RemoveContact(entity)
if err != nil {
return retVal, errors.Wrap(err)
}
return s.mapEntity.User.ToContactModel(entity), err
}

View File

@@ -158,8 +158,11 @@ func (mapping *userMapping) ToContactTypeModelSlice(list []entity.ContactType) (
// ToContactModel maps a Contact entity to Contact view model
func (mapping *userMapping) ToContactModel(item entity.ContactInfo) viewmodel.Contact {
return viewmodel.Contact{
Type: mapping.ToContactTypeModel(item.Type),
Value: item.Value,
ID: item.UUID,
User: mapping.ToUserModel(item.User),
Author: mapping.ToUserModel(item.Author),
Type: mapping.ToContactTypeModel(item.Type),
Value: item.Value,
}
}
@@ -177,8 +180,11 @@ func (mapping *userMapping) ToContactModelSlice(list []entity.ContactInfo) (retV
// ToContactEntity maps a Contact entity to Contact view model
func (mapping *userMapping) ToContactEntity(item viewmodel.Contact) entity.ContactInfo {
return entity.ContactInfo{
Type: mapping.ToContactTypeEntity(item.Type),
Value: item.Value,
UUID: item.ID,
User: mapping.ToUserEntity(item.User),
Author: mapping.ToUserEntity(item.Author),
Type: mapping.ToContactTypeEntity(item.Type),
Value: item.Value,
}
}

View File

@@ -15,6 +15,7 @@ type WebhookResponse struct {
//RideRequest has the data to dispatch a ride
type RideRequest struct {
UserUUID string `json:"user_uuid,omitempty"`
UserConsent bool `json:"user_consent,omitempty"`
Status string `json:"status,omitempty"`
RideID string `json:"ride_id,omitempty"`
RideType string `json:"ride_type,omitempty"`
@@ -158,4 +159,4 @@ type RideRoute struct {
Duration int64 `json:"duration,omitempty"`
ETA int64 `json:"eta,omitempty"`
Bearing int64 `json:"bearing,omitempty"`
}
}

View File

@@ -26,8 +26,11 @@ type User struct {
}
type Contact struct {
Type ContactType `json:"type,omitempty"`
Value string `json:"contact,omitempty"`
ID string `json:"id,omitempty"`
User User `json:"-"`
Type ContactType `json:"type,omitempty"`
Value string `json:"contact,omitempty"`
Author User `json:"-"`
}
type ContactType struct {

11
authorization_model.conf Normal file
View File

@@ -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 == "*")

115
authorization_policy.csv Normal file
View File

@@ -0,0 +1,115 @@
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, AD, *, *, *, *, *, /v1/nemt/organization/*, DELETE
p, SCHDAD, *, *, *, [equal*], *, /v1/nemt/organization/*, GET
p, SCHDAD, *, *, *, [equal*], *, /v1/nemt/organization/*, POST
p, SCHDAD, *, *, *, [equal*], *, /v1/nemt/organization/*, PUT
p, SCHDAD, *, *, *, [equal*], *, /v1/nemt/organization/*, DELETE
p, PLANAD, *, *, *, [equal*], *, /v1/nemt/organization/*, GET
p, PLANAD, *, *, *, [equal*], *, /v1/nemt/organization/*, POST
p, PLANAD, *, *, *, [equal*], *, /v1/nemt/organization/*, PUT
p, PLANAD, *, *, *, [equal*], *, /v1/nemt/organization/*, DELETE
p, BDCAD, *, *, *, *, *, /v1/nemt/organization/*, GET
p, BDCAD, *, *, *, *, *, /v1/nemt/organization/*, POST
p, BDCAD, *, *, *, *, *, /v1/nemt/organization/*, PUT
p, BDCAD, *, *, *, *, *, /v1/nemt/organization/*, DELETE
p, BCBSIAD, *, *, *, *, *, /v1/nemt/organization/*, GET
p, BCBSIAD, *, *, *, *, *, /v1/nemt/organization/*, POST
p, BCBSIAD, *, *, *, *, *, /v1/nemt/organization/*, PUT
p, BCBSIAD, *, *, *, *, *, /v1/nemt/organization/*, DELETE
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
1 p AD * * * * * * *
2 p * * * * * * /v1/authenticate/portal POST
3 p * * * * * * /v1/notification/ws GET
4 p * * * * * * /health/ GET
5 p * * * * * * /v1/nemt/users/contacttype GET
6 p * * * * * [self] /v1/nemt/users/* GET
7 p * * * * * [self] /v1/nemt/users/portal/* DELETE
8 p * * * * * [self] /v1/nemt/users/portal/* POST
9 p * * * * * [self] /v1/nemt/users/portal/* GET
10 p *AD * * * * * /v1/nemt/users/ GET
11 p BDCAD SP * * * [other] /v1/nemt/users/*/ GET
12 p BDCAD SP * * * [other] /v1/nemt/users/portal/* DELETE
13 p BDCAD SP * * * [other] /v1/nemt/users/portal/* POST
14 p BDCAD SP * * * [other] /v1/nemt/users/portal/* GET
15 p BDCAD SPT * * * [other] /v1/nemt/users/*/ GET
16 p BDCAD SPT * * * [other] /v1/nemt/users/portal/* DELETE
17 p BDCAD SPT * * * [other] /v1/nemt/users/portal/* POST
18 p BDCAD SPT * * * [other] /v1/nemt/users/portal/* GET
19 p BDCAD US * * * [other] /v1/nemt/users/*/ GET
20 p BDCAD US * * * [other] /v1/nemt/users/portal/* DELETE
21 p BDCAD US * * * [other] /v1/nemt/users/portal/* POST
22 p BDCAD US * * * [other] /v1/nemt/users/portal/* GET
23 p BCBSIAD SP * * * [other] /v1/nemt/users/*/ GET
24 p BCBSIAD SP * * * [other] /v1/nemt/users/portal/* DELETE
25 p BCBSIAD SP * * * [other] /v1/nemt/users/portal/* POST
26 p BCBSIAD SP * * * [other] /v1/nemt/users/portal/* GET
27 p BCBSIAD US * * * [other] /v1/nemt/users/*/ GET
28 p BCBSIAD US * * * [other] /v1/nemt/users/portal/* DELETE
29 p BCBSIAD US * * * [other] /v1/nemt/users/portal/* POST
30 p BCBSIAD US * * * [other] /v1/nemt/users/portal/* GET
31 p BCBSIAD SPT * * * [other] /v1/nemt/users/*/ GET
32 p BCBSIAD SPT * * * [other] /v1/nemt/users/portal/* DELETE
33 p BCBSIAD SPT * * * [other] /v1/nemt/users/portal/* POST
34 p BCBSIAD SPT * * * [other] /v1/nemt/users/portal/* GET
35 p PLANAD SPT * * [equal] [other] /v1/nemt/users/*/ GET
36 p PLANAD SPT * * [equal] [other] /v1/nemt/users/portal/* DELETE
37 p PLANAD SPT * * [equal] [other] /v1/nemt/users/portal/* POST
38 p PLANAD SPT * * [equal] [other] /v1/nemt/users/portal/* GET
39 p PLANAD US * * [equal] [other] /v1/nemt/users/*/ GET
40 p PLANAD US * * [equal] [other] /v1/nemt/users/portal/* DELETE
41 p PLANAD US * * [equal] [other] /v1/nemt/users/portal/* POST
42 p PLANAD US * * [equal] [other] /v1/nemt/users/portal/* GET
43 p PLANAD SP * * [equal] [other] /v1/nemt/users/*/ GET
44 p PLANAD SP * * [equal] [other] /v1/nemt/users/portal/* DELETE
45 p PLANAD SP * * [equal] [other] /v1/nemt/users/portal/* POST
46 p PLANAD SP * * [equal] [other] /v1/nemt/users/portal/* GET
47 p SCHDAD US * * [parent] [other] /v1/nemt/users/*/ GET
48 p SCHDAD US * * [parent] [other] /v1/nemt/users/portal/* DELETE
49 p SCHDAD US * * [parent] [other] /v1/nemt/users/portal/* POST
50 p SCHDAD US * * [parent] [other] /v1/nemt/users/portal/* GET
51 p SCHDAD SP * * [parent] [other] /v1/nemt/users/*/ GET
52 p SCHDAD SP * * [parent] [other] /v1/nemt/users/portal/* DELETE
53 p SCHDAD SP * * [parent] [other] /v1/nemt/users/portal/* POST
54 p SCHDAD SP * * [parent] [other] /v1/nemt/users/portal/* GET
55 p SCHDAD SPT * * [parent] [other] /v1/nemt/users/*/ GET
56 p SCHDAD SPT * * [parent] [other] /v1/nemt/users/portal/* DELETE
57 p SCHDAD SPT * * [parent] [other] /v1/nemt/users/portal/* POST
58 p SCHDAD SPT * * [parent] [other] /v1/nemt/users/portal/* GET
59 p SCHDAD SP * * [equal*] [other] /v1/nemt/users/*/ GET
60 p SCHDAD SP * * [equal*] [other] /v1/nemt/users/portal/* DELETE
61 p SCHDAD SP * * [equal*] [other] /v1/nemt/users/portal/* POST
62 p SCHDAD SP * * [equal*] [other] /v1/nemt/users/portal/* GET
63 p SCHDAD US * * [equal*] [other] /v1/nemt/users/*/ GET
64 p SCHDAD US * * [equal*] [other] /v1/nemt/users/portal/* DELETE
65 p SCHDAD US * * [equal*] [other] /v1/nemt/users/portal/* POST
66 p SCHDAD US * * [equal*] [other] /v1/nemt/users/portal/* GET
67 p SCHDAD SPT * * [equal*] [other] /v1/nemt/users/*/ GET
68 p SCHDAD SPT * * [equal*] [other] /v1/nemt/users/portal/* DELETE
69 p SCHDAD SPT * * [equal*] [other] /v1/nemt/users/portal/* POST
70 p SCHDAD SPT * * [equal*] [other] /v1/nemt/users/portal/* GET
71 p SCHDAD SCHDAD * * [equal*] [other] /v1/nemt/users/*/ GET
72 p SCHDAD SCHDAD * * [equal*] [other] /v1/nemt/users/portal/* DELETE
73 p SCHDAD SCHDAD * * [equal*] [other] /v1/nemt/users/portal/* POST
74 p SCHDAD SCHDAD * * [equal*] [other] /v1/nemt/users/portal/* GET
75 p SPT * programsupport * * [other] /v1/nemt/users/ GET
76 p SPT * programsupport * * [other] /v1/nemt/users/* GET
77 p * * * * * * /v1/nemt/organization/name GET
78 p * * * * * * /v1/nemt/organization/type GET
79 p AD * * * * * /v1/nemt/organization/* GET
80 p AD * * * * * /v1/nemt/organization/* POST
81 p AD * * * * * /v1/nemt/organization/* PUT
82 p AD * * * * * /v1/nemt/organization/* DELETE
83 p SCHDAD * * * [equal*] * /v1/nemt/organization/* GET
84 p SCHDAD * * * [equal*] * /v1/nemt/organization/* POST
85 p SCHDAD * * * [equal*] * /v1/nemt/organization/* PUT
86 p SCHDAD * * * [equal*] * /v1/nemt/organization/* DELETE
87 p PLANAD * * * [equal*] * /v1/nemt/organization/* GET
88 p PLANAD * * * [equal*] * /v1/nemt/organization/* POST
89 p PLANAD * * * [equal*] * /v1/nemt/organization/* PUT
90 p PLANAD * * * [equal*] * /v1/nemt/organization/* DELETE
91 p BDCAD * * * * * /v1/nemt/organization/* GET
92 p BDCAD * * * * * /v1/nemt/organization/* POST
93 p BDCAD * * * * * /v1/nemt/organization/* PUT
94 p BDCAD * * * * * /v1/nemt/organization/* DELETE
95 p BCBSIAD * * * * * /v1/nemt/organization/* GET
96 p BCBSIAD * * * * * /v1/nemt/organization/* POST
97 p BCBSIAD * * * * * /v1/nemt/organization/* PUT
98 p BCBSIAD * * * * * /v1/nemt/organization/* DELETE
99 p SPT * programsupport * * * /v1/nemt/organization/* GET
100 p SP * provider * * * /v1/nemt/organization GET
101 p SP * plan * * * /v1/nemt/organization GET
102 p SPT * programsupport * * * /v1/nemt/organization GET
103 p SCHDAD * provider * * * /v1/nemt/organization GET
104 p BCBSIAD * bcbsi * * * /v1/nemt/organization GET
105 p BDCAD * techsupport * * * /v1/nemt/organization GET
106 p PLANAD * plan * * * /v1/nemt/organization GET
107 p AD * * * * * /v1/nemt/organization GET
108 p SP * provider * * * /v1/nemt/eligibility POST
109 p SP * plan * * * /v1/nemt/eligibility POST
110 p SPT * programsupport * * * /v1/nemt/eligibility POST
111 p SCHDAD * provider * * * /v1/nemt/eligibility POST
112 p BCBSIAD * bcbsi * * * /v1/nemt/eligibility POST
113 p BDCAD * techsupport * * * /v1/nemt/eligibility POST
114 p PLANAD * plan * * * /v1/nemt/eligibility POST
115 p AD * * * * * /v1/nemt/eligibility POST

View File

@@ -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

View File

@@ -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

View File

@@ -37,6 +37,7 @@ db = 0
pass = "3rdaP3KL2x%V"
prefix = "portal-api-prod"
default-expiration = "5m"
master-name = "master01"
[log]
log-to-file = false

View File

@@ -37,6 +37,7 @@ db = 0
pass = "3rdaP3KL2x%V"
prefix = "portal-api-test"
default-expiration = "5m"
master-name = "devmaster01"
[log]
log-to-file = false

View File

@@ -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) {

View File

@@ -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 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

View File

@@ -63,7 +63,8 @@ func (c *userRepo) GetContacts(userID int64) ([]entity.ContactInfo, error) {
a.user_id,
b.contact_type_id,
b.key contact_type_key,
b.name contact_type_name
b.name contact_type_name,
a.contact_uuid
FROM tab_contact a
INNER JOIN tab_contact_type b
ON a.contact_type_id = b.contact_type_id
@@ -79,7 +80,7 @@ func (c *userRepo) GetContacts(userID int64) ([]entity.ContactInfo, error) {
retVal := make([]entity.ContactInfo, 0)
for rows.Next() {
contact := entity.ContactInfo{}
err = rows.Scan(&contact.ID, &contact.Value, &contact.UserID, &contact.Type.ID, &contact.Type.Key, &contact.Type.Value)
err = rows.Scan(&contact.ID, &contact.Value, &contact.User.ID, &contact.Type.ID, &contact.Type.Key, &contact.Type.Value, &contact.UUID)
if err != nil {
return nil, err
}
@@ -354,9 +355,11 @@ func (c *userRepo) Create(user entity.User) (retVal entity.User, err error) {
if retVal.Email != "" {
contact := entity.ContactInfo{
Type: entity.ContactType{Key: "email"},
Value: retVal.Email,
UserID: retVal.ID,
Type: entity.ContactType{Key: "email"},
Value: retVal.Email,
User: entity.User{
ID: retVal.ID,
},
}
contact, err = c.addContactInfo(contact)
@@ -367,9 +370,11 @@ func (c *userRepo) Create(user entity.User) (retVal entity.User, err error) {
if retVal.PhoneNumber != "" {
contact := entity.ContactInfo{
Type: entity.ContactType{Key: "phone"},
Value: retVal.PhoneNumber,
UserID: retVal.ID,
Type: entity.ContactType{Key: "phone"},
Value: retVal.PhoneNumber,
User: entity.User{
ID: retVal.ID,
},
}
contact, err = c.addContactInfo(contact)
@@ -387,19 +392,10 @@ func (c *userRepo) SaveContact(contact entity.ContactInfo) (entity.ContactInfo,
func (c *userRepo) RemoveContact(contact entity.ContactInfo) (entity.ContactInfo, error) {
const (
query = `INSERT INTO tab_contact(contact_type_id, user_id, contact)
SELECT a.contact_type_id, ? user_id, ? contact
FROM
tab_contact_type a
LEFT JOIN tab_contact b
ON a.contact_type_id = b.contact_type_id
AND b.user_id = ?
AND b.contact = ?
WHERE a.key = ?
AND b.contact_id IS NULL;`
query = `DELETE FROM tab_contact WHERE contact_uuid = ?;`
)
result, err := c.conn.Exec(query, contact.UserID, contact.Value, contact.UserID, contact.Value, contact.Type.Key)
result, err := c.conn.Exec(query, contact.UUID)
if err != nil {
return contact, err
}
@@ -415,8 +411,8 @@ func (c *userRepo) RemoveContact(contact entity.ContactInfo) (entity.ContactInfo
func (c *userRepo) addContactInfo(contact entity.ContactInfo) (entity.ContactInfo, error) {
const (
query = `INSERT INTO tab_contact(contact_type_id, user_id, contact)
SELECT a.contact_type_id, ? user_id, ? contact
query = `INSERT INTO tab_contact(contact_type_id, user_id, contact, contact_uuid)
SELECT a.contact_type_id, ? user_id, ? contact, ? contact_uuid
FROM
tab_contact_type a
LEFT JOIN tab_contact b
@@ -427,7 +423,13 @@ func (c *userRepo) addContactInfo(contact entity.ContactInfo) (entity.ContactInf
AND b.contact_id IS NULL;`
)
result, err := c.conn.Exec(query, contact.UserID, contact.Value, contact.UserID, contact.Value, contact.Type.Key)
sUUID, err := uuid.NewV4()
if err != nil {
return contact, err
}
contact.UUID = sUUID.String()
result, err := c.conn.Exec(query, contact.User.ID, contact.Value, contact.UUID, contact.User.ID, contact.Value, contact.Type.Key)
if err != nil {
return contact, err
}

View File

@@ -27,6 +27,8 @@ type UserRepo interface {
GetAddressByUUID(addressUUID string) (entity.Address, error)
GetContactType() (retVal []entity.ContactType, err error)
RemoveAddress(addressUUID string) error
SaveContact(contact entity.ContactInfo) (entity.ContactInfo, error)
RemoveContact(contact entity.ContactInfo) (entity.ContactInfo, error)
}
// RideRepo defines the data set for Rides
@@ -68,23 +70,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)
}

View File

@@ -34,8 +34,10 @@ type User struct {
type ContactInfo struct {
ID int64 `db:"contact_id" json:"contact_id"`
UUID string `db:"contact_uuid" json:"contact_uuid"`
Type ContactType `db:"contact_type" json:"contact_type"`
UserID int64 `db:"user_id" json:"-"`
User User `db:"user" json:"-"`
Author User `db:"author" json:"-"`
Value string `db:"value" json:"value"`
}

View File

@@ -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) {

View File

@@ -82,6 +82,26 @@ func (s *userService) SaveAddress(address entity.Address) (entity.Address, error
return s.svc.db.Users().SaveAddress(address)
}
func (s *userService) SaveContact(contact entity.ContactInfo) (entity.ContactInfo, error) {
user, err := s.svc.db.Users().GetByUUID(contact.User.UUID, "")
if err != nil {
return entity.ContactInfo{}, nil
}
contact.User = user
return s.svc.db.Users().SaveContact(contact)
}
func (s *userService) RemoveContact(contact entity.ContactInfo) (entity.ContactInfo, error) {
user, err := s.svc.db.Users().GetByUUID(contact.User.UUID, "")
if err != nil {
return entity.ContactInfo{}, nil
}
contact.User = user
return s.svc.db.Users().RemoveContact(contact)
}
// GetAddressByUUID returns a list of users by profile
func (s *userService) GetAddressByUUID(addressUUID string) (entity.Address, error) {
return s.svc.db.Users().GetAddressByUUID(addressUUID)

View File

@@ -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
}
]
}

View File

@@ -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
}
]
}

View File

@@ -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

View File

@@ -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}

View File

@@ -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"),

View File

@@ -0,0 +1,15 @@
package authorization
import (
"bitbucket.org/nemt/nemt-portal-api/application/viewmodel"
)
func CanCreateAddress(user viewmodel.User, organization viewmodel.Organization) bool {
//rules are the same for address creation and for organization creation
return CanCreateOrganization(user, organization)
}
func CanUpdateAddress(user viewmodel.User, organization viewmodel.Organization) bool {
return CanCreateAddress(user, organization)
}

View File

@@ -0,0 +1 @@
package authorization

View File

@@ -0,0 +1,15 @@
package authorization
import (
"bitbucket.org/nemt/nemt-portal-api/application/viewmodel"
)
func CanCreateContact(user viewmodel.User, organization viewmodel.Organization) bool {
//rules are the same for contact creation and for organization creation
return CanCreateOrganization(user, organization)
}
func CanUpdateContact(user viewmodel.User, organization viewmodel.Organization) bool {
return CanCreateAddress(user, organization)
}

View File

@@ -0,0 +1,76 @@
package authorization
import (
"fmt"
"bitbucket.org/nemt/nemt-portal-api/application/viewmodel"
)
func isAChildOrganization(potentialParent viewmodel.Organization, potentialChild viewmodel.Organization) bool {
for _, org := range potentialParent.ChildOrgs {
if potentialChild.UUID == org.UUID {
return true
}
}
return false
}
func isSameOrganization(organizationA viewmodel.Organization, organizationB viewmodel.Organization) bool {
return organizationA.UUID == organizationB.UUID
}
func grabOrgFromUser(user viewmodel.User) (viewmodel.Organization, error) {
if len(user.Profiles) < 1 {
return viewmodel.Organization{}, fmt.Errorf("User has no organizations %v", user)
}
return user.Profiles[0].Organization, nil
}
func grabOrgFromUserDirectly(user viewmodel.User) (viewmodel.Organization, error) {
if len(user.Organizations) < 1 {
return viewmodel.Organization{}, fmt.Errorf("User has no organizations %v", user)
}
return user.Organizations[0], nil
}
func CanCreateOrganization(user viewmodel.User, organization viewmodel.Organization ) bool {
userRole, err := grabProfileFromUser(user)
if err != nil {
return false
}
/*
Admin BCBSI
Admin Technical Support
Super Admin Technical Support
Manage all Organizations*/
if userRole.Key == bcbsiAdmin || userRole.Key == brighterDevAdmin || userRole.Key == superAdmin{
return true
}
userOrg, err := grabOrgFromUser(user)
if err != nil{
return false
}
/*
Admin Provider
Admin Plan
Manage the authenticated Authorized User's Organization and child Organizations */
if userRole.Key == providerAdmin || userRole.Key == planAdmin{
if isSameOrganization(userOrg, organization) || isAChildOrganization(userOrg, organization) {
return true
}
return false
}
return false
}
func CanUpdateOrganization(user viewmodel.User, organization viewmodel.Organization) bool{
return CanCreateOrganization(user, organization)
}

View File

@@ -0,0 +1,62 @@
package authorization
import (
"fmt"
"bitbucket.org/nemt/nemt-portal-api/application/viewmodel"
)
const (
superAdmin = "AD"
scheduler = "SP"
support = "SPT"
member = "US"
brighterDevAdmin = "BDCAD"
bcbsiAdmin = "BCBSIAD"
planAdmin = "PLANAD"
providerAdmin = "SCHDAD"
)
func grabProfileFromUser(user viewmodel.User) (viewmodel.Profile, error) {
if len(user.Profiles) < 1 {
return viewmodel.Profile{}, fmt.Errorf("User has no profiles %v", user)
}
return user.Profiles[0], nil
}
func morePrivileged(who viewmodel.Profile, towardsWhom viewmodel.Profile) bool {
order := []string{superAdmin, brighterDevAdmin, bcbsiAdmin, planAdmin, providerAdmin, support, scheduler, member}
for _, value := range order {
if value == who.Key {
return true
}
if value == towardsWhom.Key {
return false
}
}
// should hapen only in case profile key is empty
// and that's something fishy so let's deny!
return false
}
func equallyOrMorePrivileged(who viewmodel.Profile, towardsWhom viewmodel.Profile) bool {
if who.Key == towardsWhom.Key {
return true
}
return morePrivileged(who, towardsWhom)
}
func lessPrivilegedThanAdmin(who viewmodel.Profile) bool {
switch who.Key {
case member:
return true
case scheduler:
return true
case support:
return true
}
return false
}

View File

@@ -0,0 +1,86 @@
package authorization
import "bitbucket.org/nemt/nemt-portal-api/application/viewmodel"
/*
CanCreateUser returns true if currentUser is allowed to create updatingUser according to
authorization rules
*/
func CanCreateUser(currentUser viewmodel.User, updatingUser viewmodel.User) bool {
if len(currentUser.Profiles) < 1 {
return false
}
if len(updatingUser.Profiles) < 1 {
return false
}
currentUserOrganization, err := grabOrgFromUser(currentUser)
if err != nil {
return false
}
updatingUserOrganization, err := grabOrgFromUserDirectly(updatingUser)
if err != nil {
return false
}
currentUserRole, err := grabProfileFromUser(currentUser)
if err != nil {
return false
}
updatingUserRole, err := grabProfileFromUser(updatingUser)
if err != nil {
return false
}
/*
Admin Provider
Manage all Authorized Users of the provider Organization or child organization
The (Provider) Admin can manage Authorized Users of their Parent/ Top-level Org , but not Admins
*/
currentUserHigherOrEqualOrg := isSameOrganization(currentUserOrganization, updatingUserOrganization) || isAChildOrganization(currentUserOrganization, updatingUserOrganization)
currentUserLowerOrg := isAChildOrganization(updatingUserOrganization, currentUserOrganization)
if currentUserRole.Key == providerAdmin && currentUserHigherOrEqualOrg && equallyOrMorePrivileged(currentUserRole, updatingUserRole) {
return true
}
if currentUserRole.Key == providerAdmin && currentUserLowerOrg && lessPrivilegedThanAdmin(updatingUserRole) {
return true
}
/* Admin BCBSI
Manage all Authorized Users except Admins
return false
*/
if currentUserRole.Key == bcbsiAdmin && lessPrivilegedThanAdmin(updatingUserRole) {
return true
}
/* Admin Technical Support Manage all Authorized Users except Admins */
if currentUserRole.Key == brighterDevAdmin && lessPrivilegedThanAdmin(updatingUserRole) {
return true
}
/* Admin Plan Manage all Authorized Users of a single participating Plan except Admins */
if currentUserRole.Key == planAdmin && lessPrivilegedThanAdmin(updatingUserRole) && isSameOrganization(currentUserOrganization, updatingUserOrganization) {
return true
}
/* Super Admin Technical Support
Manage all Members, INCLUDING Admins */
if currentUserRole.Key == superAdmin {
return true
}
return false
}

View File

@@ -10,6 +10,7 @@ import (
"bitbucket.org/nemt/nemt-portal-api/infra/cache"
"bitbucket.org/nemt/nemt-portal-api/infra/config"
"bitbucket.org/nemt/nemt-portal-api/server/router/routeutils"
"bitbucket.org/nemt/nemt-portal-api/server/authorization"
"github.com/labstack/echo"
)
@@ -64,6 +65,11 @@ func (c *controller) handleAddOrganization(ctx echo.Context) error {
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
if !authorization.CanCreateOrganization(authUser, org) {
return routeutils.ResponseAPIAuthorizationError(ctx)
}
org.Author.ID = authUser.ID
org.LastEditor.ID = authUser.ID
@@ -78,7 +84,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 +103,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 +128,21 @@ 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)
}
organization, err := c.svc.Organization.GetByUUID(orgUUID, authUser)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
if !authorization.CanUpdateOrganization(authUser, organization){
return routeutils.ResponseAPIAuthorizationError(ctx)
}
resp, err := c.svc.Organization.SetParentOrganization(orgUUID, parent.UUID, authUser)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
@@ -132,12 +162,26 @@ 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)
organization, err := c.svc.Organization.GetByUUID(orgUUID, authUser)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
if !authorization.CanUpdateOrganization(authUser, organization){
return routeutils.ResponseAPIAuthorizationError(ctx)
}
_, 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 +195,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 +212,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 +229,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 +266,31 @@ func (c *controller) handleAddAddress(ctx echo.Context) error {
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.CreatedUser.ID = createdUser["useruuid"].(string)
address.UpdatedUser.ID = address.CreatedUser.ID
_, err = c.svc.Organization.SetOrganizationAddress(orgUUID, address)
authUser, err := auth.GetUserDetail(ctx, c.cfg)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
resp, err := c.svc.Organization.GetByUUID(orgUUID)
organization, err := c.svc.Organization.GetByUUID(orgUUID, authUser)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
if !authorization.CanCreateAddress(authUser, organization) {
return routeutils.ResponseAPIAuthorizationError(ctx)
}
return routeutils.ResponseAPIAuthorizationError(ctx)
address.CreatedUser.ID = authUser.ID
address.UpdatedUser.ID = authUser.ID
_, err = c.svc.Organization.SetOrganizationAddress(orgUUID, address, 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)
}
@@ -250,19 +310,19 @@ func (c *controller) handleRemoveContact(ctx echo.Context) error {
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{})
contact.UpdatedUser.ID = createdUser["useruuid"].(string)
err = c.svc.Organization.InactivateOrganizationContact(orgUUID, contact)
authUser, err := auth.GetUserDetail(ctx, c.cfg)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
resp, err := c.svc.Organization.GetByUUID(orgUUID)
contact.UpdatedUser.ID = authUser.ID
err = c.svc.Organization.InactivateOrganizationContact(orgUUID, contact, 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)
}
@@ -282,20 +342,29 @@ func (c *controller) handleAddContact(ctx echo.Context) error {
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{})
contact.CreatedUser.ID = createdUser["useruuid"].(string)
contact.UpdatedUser.ID = contact.CreatedUser.ID
_, err = c.svc.Organization.SetOrganizationContact(orgUUID, contact)
authUser, err := auth.GetUserDetail(ctx, c.cfg)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
resp, err := c.svc.Organization.GetByUUID(orgUUID)
organization, err := c.svc.Organization.GetByUUID(orgUUID, authUser)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
if !authorization.CanCreateContact(authUser, organization) {
return routeutils.ResponseAPIAuthorizationError(ctx)
}
contact.CreatedUser.ID = authUser.ID
contact.UpdatedUser.ID = authUser.ID
_, err = c.svc.Organization.SetOrganizationContact(orgUUID, contact, 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)
}

View File

@@ -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,

View File

@@ -33,6 +33,18 @@ func ResponseAPIOK(c echo.Context, data interface{}) error {
return c.JSON(http.StatusOK, data)
}
// ResponseAPIErrorWithData returns a standard API error with additional data to the response
func ResponseAPIErrorWithData(c echo.Context, status int, message string, redirect bool, data interface{}) error {
returnValue := resultWrapper{
Error: true,
Message: message,
Redirect: redirect,
Data: data,
}
return c.JSON(status, returnValue)
}
// ResponseAPIError returns a standard API error to the response
func ResponseAPIError(c echo.Context, status int, message string, redirect bool) error {
returnValue := resultWrapper{
@@ -49,6 +61,11 @@ func ResponseAPIAuthError(c echo.Context, message string, redirect bool) error {
return ResponseAPIError(c, http.StatusUnauthorized, message, redirect)
}
// ResponseAPIAuthorizationError returns a standard API auth error to the response
func ResponseAPIAuthorizationError(c echo.Context) error {
return ResponseAPIError(c, http.StatusForbidden, "Forbidden by controller", false)
}
// ResponseAPIServiceError returns a standard API service unavailable error to the response
func ResponseAPIServiceError(c echo.Context, message string) error {
return ResponseAPIError(c, http.StatusServiceUnavailable, message, false)
@@ -59,6 +76,11 @@ func ResponseAPIValidationError(c echo.Context, message string) error {
return ResponseAPIError(c, http.StatusUnprocessableEntity, message, false)
}
// ResponseAPICustomValidationError returns a standard API validation error with custom data to the response
func ResponseAPICustomValidationError(c echo.Context, message string, data interface{}) error {
return ResponseAPIErrorWithData(c, http.StatusUnprocessableEntity, message, false, data)
}
// ResponseAPIFieldValidationError returns a standard API field validation error to the response
func ResponseAPIFieldValidationError(c echo.Context, field string, message string) error {
err := errors.NewValidationError(field, message)

View File

@@ -15,6 +15,7 @@ import (
"bitbucket.org/nemt/nemt-portal-api/infra/auth"
"bitbucket.org/nemt/nemt-portal-api/infra/config"
"bitbucket.org/nemt/nemt-portal-api/server/router/routeutils"
"bitbucket.org/nemt/nemt-portal-api/server/validation"
"github.com/labstack/echo"
uuid "github.com/satori/go.uuid"
"google.golang.org/api/googleapi/transport"
@@ -231,6 +232,11 @@ func (c *controller) handle(ctx echo.Context) error {
return routeutils.ResponseAPIValidationError(ctx, "User not found")
}
//Validate ride request
if validationErrors := validation.ValidateRide(&requestRide, &user) ; len(validationErrors) > 0 {
return routeutils.ResponseAPICustomValidationError(ctx, "ride validation failed", validationErrors)
}
createdUser, err := auth.GetUserDetail(ctx, c.cfg)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
@@ -869,4 +875,4 @@ func (c *controller) handleReady(ctx echo.Context) error {
}()
return routeutils.ResponseAPIOK(ctx, nextRide)
}
}

View File

@@ -13,6 +13,7 @@ import (
"bitbucket.org/nemt/nemt-portal-api/infra/auth"
"bitbucket.org/nemt/nemt-portal-api/infra/cache"
"bitbucket.org/nemt/nemt-portal-api/infra/config"
"bitbucket.org/nemt/nemt-portal-api/server/authorization"
"bitbucket.org/nemt/nemt-portal-api/server/router/routeutils"
"github.com/labstack/echo"
)
@@ -127,6 +128,62 @@ func (c *controller) handleRemoveAddress(ctx echo.Context) error {
return routeutils.ResponseNoContent(ctx, addressID)
}
func (c *controller) handlePortalContact(ctx echo.Context) error {
userID, err := routeutils.GetAndValidateStringParam(ctx, "user_uuid", "mandatory field")
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
item, err := c.svc.Users.GetByUUID(userID, "")
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
createdUser, err := auth.GetUserDetail(ctx, c.cfg)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
if item.ID == "" {
return routeutils.ResponseAPIValidationError(ctx, "User not found")
} else {
var Contact viewmodel.Contact
if err := ctx.Bind(&Contact); err != nil {
return routeutils.HandleAPIError(ctx, err)
}
Contact.User = item
Contact.Author = createdUser
Contact, err = c.svc.Users.SaveContact(Contact)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
// Contact.User, err = c.svc.Users.GetByUUID(userID, "")
// if err != nil {
// return routeutils.HandleAPIError(ctx, err)
// }
return routeutils.ResponseAPIOK(ctx, Contact)
}
}
func (c *controller) handleRemoveContact(ctx echo.Context) error {
contactUUID, err := routeutils.GetAndValidateStringParam(ctx, "contact_uuid", "mandatory field")
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
contact := viewmodel.Contact{
ID: contactUUID,
}
contact, err = c.svc.Users.RemoveContact(contact)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
return routeutils.ResponseNoContent(ctx, contact)
}
func (c *controller) handleMemberAddress(ctx echo.Context) error {
userID, err := routeutils.GetAndValidateStringParam(ctx, "user_uuid", "mandatory field")
if err != nil {
@@ -274,6 +331,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 +376,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 +390,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 +427,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 +441,15 @@ 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 !authorization.CanCreateUser(authUser, user) {
return routeutils.ResponseAPIAuthorizationError(ctx)
}
if len(user.Profiles) == 0 {
return routeutils.ResponseAPIAuthError(ctx, "profile is required", false)
}
@@ -404,7 +480,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)
}

View File

@@ -17,6 +17,8 @@ const (
userDetailRoute = "/portal/:user_uuid"
userAddressRoute = "/portal/:user_uuid/address"
userRemoveAddressRoute = "/portal/:user_uuid/address/:address_uuid"
userContactRoute = "/portal/:user_uuid/contact"
userRemoveContactRoute = "/portal/:user_uuid/contact/:contact_uuid"
portalRoute = "/portal"
portalBulkRoute = "/portal/bulk"
contacttypeRoute = "/contacttype"
@@ -41,6 +43,9 @@ func Register(r *echo.Group, cfg *config.Config, svc *applicationservice.Service
r.POST(userAddressRoute, ctrl.handlePortalAddress)
r.PUT(userRemoveAddressRoute, ctrl.handleRemoveAddress)
r.POST(userContactRoute, ctrl.handlePortalContact)
r.PUT(userRemoveContactRoute, ctrl.handleRemoveContact)
//Can be cached
r.GET(contacttypeRoute, ctrl.handleContactType)

View File

@@ -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))

View File

@@ -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,128 @@ 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]"
}
}
return "[other]"
}

View File

@@ -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 == "*")

View File

@@ -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 {

183
server/validation/tnc.go Normal file
View File

@@ -0,0 +1,183 @@
package validation
import (
"time"
"fmt"
"strconv"
"bitbucket.org/nemt/nemt-portal-api/application/viewmodel"
"bitbucket.org/nemt/nemt-portal-api/infra/errors"
)
const (
tripTypeFromVisit = "From Visit"
tripTypeToVisit = "To Visit"
tripTypeFromVisitWillCall = "From Visit / Will Call"
tripTypeRoundTrip = "Round Trip"
tripTypeRountTripWillCall = "Round Trip / Will Call"
)
const (
loadingTime = 30 //in minutes
minimumLoadTime = 30 //in minutes
minimumPickupTime = 10 //in minutes
)
const (
hoursInDay = 24
hoursIn180Days = 24*180
time8Hours = 8
time10Minutes = 10
)
func ValidateRide(requestRide *viewmodel.RideRequest, user *viewmodel.User) []errors.ValidationError {
var result []errors.ValidationError
//Step #1 validation
if userID, err := strconv.Atoi(user.ID) ; err != nil || userID <= 0 {
result = append(result, errors.ValidationError{Field : "user_uuid", Message : "Step #1 - Choose a Member" })
}
if originID, err := strconv.Atoi(requestRide.Origin.ID) ; err != nil || originID <= 0 {
result = append (result, errors.ValidationError{Field : "origin.id", Message : "Step #1 - Choose a Pickup Address"})
}
if !requestRide.UserConsent {
result = append (result, errors.ValidationError{Field : "user_consent", Message : "Step #1 - Member must consent to Terms of Use"})
}
//Step #2 validation
if destinationID, err := strconv.Atoi(requestRide.Destination.ID) ; err!= nil || destinationID <= 0 {
result = append (result, errors.ValidationError{Field : "destination.id", Message : "Step #2 - Choose a Provider"})
}
//Step #3 validation
isVisitDayToday := requestRide.VisitDate.Day() == time.Now().Day() && requestRide.VisitDate.Month() == time.Now().Month() && requestRide.VisitDate.Year() == time.Now().Year()
before8Hours := time.Now().Add(-time.Hour*time8Hours)
if requestRide.VisitDate == nil {
result = append (result, errors.ValidationError{Field : "visit_date", Message : "Step #3 - Choose a Date for the Visit"})
}else{
dayBeforeToday := time.Now().Add(-time.Hour*hoursInDay)
if requestRide.VisitDate.Before(dayBeforeToday) {
result = append (result, errors.ValidationError{Field : "visit_date", Message : "Step #3 - Visit cannot occur more than one day before today"})
}
dayAfter180Days := time.Now().Add(time.Hour*hoursIn180Days)
if requestRide.VisitDate.After(dayAfter180Days) {
result = append (result, errors.ValidationError{Field : "visit_date", Message : "Step #3 - Visit cannot occur more than 180 days after today"})
}
if requestRide.VisitTime == nil {
result = append (result, errors.ValidationError{Field : "visit_time", Message : "Step #3 - Choose a Time for the Visit"})
}else{
if isVisitDayToday && requestRide.VisitTime.Before(before8Hours) {
result = append (result, errors.ValidationError{Field : "visit_time", Message : "Step #3 - Visit is more than 8 hours in the past"})
}
}
}
//Step #4 validation
if requestRide.TripType.Value == "" {
result = append (result, errors.ValidationError{Field : "trip_type.value", Message : "Step #4 - Choose a Trip Type"})
}
timeWithDurationAndLoadingTime := requestRide.VisitTime.Add(-time.Duration(requestRide.Duration)*time.Second).Add(-loadingTime*time.Minute)
after10Minutes := time.Now().Add(time.Minute*time10Minutes)
switch requestRide.TripType.Value {
case tripTypeToVisit:
if requestRide.PickupTime == nil {
result = append (result, errors.ValidationError{Field : "pickup_time", Message : "Step #4 - Choose a Pickup Time"})
}else{
if requestRide.PickupTime.After(*requestRide.VisitTime) {
result = append (result, errors.ValidationError{Field : "pickup_time", Message : "Step #4 - Pickup Time must occur before Visit Time"})
}
if requestRide.PickupTime.After(timeWithDurationAndLoadingTime) {
result = append (result, errors.ValidationError{Field : "pickup_time", Message : "Step #4 - Pickup Time less than required time before Visit Time"})
}
if isVisitDayToday && requestRide.PickupTime.Before(before8Hours) {
result = append (result, errors.ValidationError{Field : "pickup_time", Message : "Step #4 - Visit cannot occour in the past"})
}
}
case tripTypeFromVisit :
if requestRide.PickupTime == nil {
result = append (result, errors.ValidationError{Field : "pickup_time", Message : "Step #4 - Choose a Pickup Time"})
}else{
timeWithMinimumPickupTime := time.Now().Add(minimumPickupTime*time.Minute)
if isVisitDayToday && requestRide.PickupTime.Before(timeWithMinimumPickupTime) {
result = append (result, errors.ValidationError{Field : "pickup_time", Message : fmt.Sprint("Step #4 - Time must be more than %d minutes from now",minimumPickupTime)})
}
if requestRide.PickupTime.Before(*requestRide.VisitTime) {
result = append (result, errors.ValidationError{Field : "pickup_time", Message : "Step #4 - Pickup Time less than required time after Visit Time"})
}
timeWithMinimumLoadTime := time.Now().Add(minimumLoadTime*time.Minute)
if requestRide.PickupTime.Before(timeWithMinimumLoadTime) {
result = append (result, errors.ValidationError{Field : "pickup_time", Message : "Step #4 - Pickup Time less than Minimum Load Time before Visit Time"})
}
}
case tripTypeFromVisitWillCall:
//no special validation for this case
case tripTypeRoundTrip:
if requestRide.PickupTime == nil {
result = append (result, errors.ValidationError{Field : "pickup_time", Message : "Step #4 - Choose a Pickup Time"})
}else{
if requestRide.PickupTime.After(*requestRide.VisitTime) {
result = append (result, errors.ValidationError{Field : "pickup_time", Message : "Step #4 - Pickup Time must occur before Visit Time"})
}
if requestRide.PickupTime.After(timeWithDurationAndLoadingTime) {
result = append (result, errors.ValidationError{Field : "pickup_time", Message : "Step #4 - Pickup Time less than required time before Visit Time"})
}
if isVisitDayToday && requestRide.PickupTime.Before(after10Minutes) {
result = append (result, errors.ValidationError{Field : "pickup_time", Message : "Step #4 - Pickup Time must be at least 10 minutes from now"})
}
}
if requestRide.ReturnTime == nil {
result = append (result, errors.ValidationError{Field : "return_time", Message : "Step #4 - Choose a Pickup Time"})
}else{
if isVisitDayToday {
if requestRide.ReturnTime.Before(before8Hours) {
result = append (result, errors.ValidationError{Field : "return_time", Message : "Step #4 - Return Time is more than 8 hours after Visit Time"})
}
if requestRide.ReturnTime.Before(after10Minutes) {
result = append (result, errors.ValidationError{Field : "return_time", Message : "Step #4 - Return Time must be at least 10 minutes from now"})
}
}
}
case tripTypeRountTripWillCall:
if requestRide.PickupTime == nil {
result = append (result, errors.ValidationError{Field : "pickup_time", Message : "Step #4 - Choose a Pickup Time"})
}else{
if requestRide.PickupTime.After(*requestRide.VisitTime) {
result = append (result, errors.ValidationError{Field : "pickup_time", Message : "Step #4 - Pickup Time must occur before Visit Time"})
}
if requestRide.PickupTime.After(timeWithDurationAndLoadingTime) {
result = append (result, errors.ValidationError{Field : "pickup_time", Message : "Step #4 - Pickup Time less than required time after Visit Time"})
}
if isVisitDayToday && requestRide.PickupTime.Before(before8Hours) {
result = append (result, errors.ValidationError{Field : "pickup_time", Message : "Step #4 - Visit cannot occur in the past "})
}
}
}
return result
}