Skip to content

Commit

Permalink
Merge pull request #618 from bcgov/yj
Browse files Browse the repository at this point in the history
Yj
  • Loading branch information
ychung-mot committed Sep 11, 2024
2 parents 6491a47 + f9f8792 commit 788744f
Show file tree
Hide file tree
Showing 11 changed files with 252 additions and 6 deletions.
3 changes: 1 addition & 2 deletions crunchydb/charts/crunchy-postgres/values.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
fullnameOverride: strdss-db

# crunchyImage: # it's not necessary to specify an image as the images specified in the Crunchy Postgres Operator will be pulled by default
crunchyImage: artifacts.developer.gov.bc.ca/bcgov-docker-local/crunchy-postgres-gis:ubi8-15.2-3.3-0 # use this image for POSTGIS
crunchyImage: artifacts.developer.gov.bc.ca/bcgov-docker-local/crunchy-postgres-gis:ubi8-15.6-3.3-0 # use this image for POSTGIS
postgresVersion: 15
postGISVersion: '3.3' # use this version of POSTGIS. both crunchyImage and this property needs to have valid values for POSTGIS to be enabled.
imagePullPolicy: IfNotPresent
Expand Down Expand Up @@ -36,7 +36,6 @@ pgBackRest:
repos:
schedules:
full: 0 8 * * *
incremental: 0 0,4,12,16,20 * * *
volume:
accessModes: "ReadWriteOnce"
storage: 64Mi
Expand Down
2 changes: 1 addition & 1 deletion crunchydb/values-prod.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ crunchy-postgres:
name: ha # high availability
replicas: 2
dataVolumeClaimSpec:
storage: 20Gi
storage: 25Gi
storageClassName: netapp-block-standard
requests:
cpu: 20m
Expand Down
81 changes: 81 additions & 0 deletions gateway/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# KONG API Service Portal Setup

The public API is accessible at

* DEV: https://dev.strdata.api.gov.bc.ca
* UAT: https://test.strdata.api.gov.bc.ca
* PROD: https://strdata.api.gov.bc.ca

API access is controlled via Kong, administered via the BC Gov API Programme Services API Gateway.
**Kong configuration is not updated via Github Actions, and must be updated manually when there are changes.**

For an overview of the API Gateway update process, see:
https://bcgov.github.io/aps-infra-platform/guides/owner-journey-v1/


## Publication

### Prerequisites
1. In the API Services Portal (https://api.gov.bc.ca/), the namespace strdata has already been created.
2. In the namespace, authorization profile has been created as follows:
* Flow: Client Credential Flow, using Client ID and Secret
* Mode: Automatic
* Client Mappers (Audience): gateway-awp


### Publication


1. Log into https://api.gov.bc.ca/
2. Select the strdata namespace
3. Create a service account with `GatewayConfig.Publish` scope and note down the client id and client secret
4. Download the GWA CLI from https://github.com/bcgov/gwa-cli/releases
5. In command prompt run the following commands (the first command create a .env file locally, which will need to be deleted if you need to create one for the other environment):

```sh
gwa config set host api.gov.bc.ca
gwa config set --namespace strdata

export SCID="<<client id>>"
export SCSC="<<client secret>>"
export SURL="https://authz.apps.gov.bc.ca/auth/realms/aps/protocol/openid-connect/token"

gwa login --client-id $SCID --client-secret $SCSC
gwa pg strdata-{env}.yaml
```
5. (optional for Windows GWA) In command prompt of Windows run the following commands (the first command create a .env file locally, which will need to be deleted if you need to create one for the other environment):

```sh
gwa config set host api.gov.bc.ca
gwa config set --namespace strdata
gwa login --client-id "<<client id>>" --client-secret "<<client secret>>"
gwa pg strdata-{env}.yaml
```
6. Check the Gateway in the API Service Portal to make sure that the routes have been published
7. Create a dataset if it doesn't exist.
https://bcgov.github.io/aps-infra-platform/guides/owner-journey-v1/#91-setup-your-draft-dataset
```
{
"name": "strdata-dataset",
"license_title": "Open Government Licence - British Columbia",
"security_class": "PUBLIC",
"view_audience": "Public",
"download_audience": "Public",
"record_publish_date": "2024-09-11",
"notes": "Short-Term Rental Data API Services",
"title": "Short-Term Rental Data API Services",
"tags": [
"openapi",
"standards"
],
"organization": "ministry-of-housing",
"organizationUnit": "planning-and-land-use-management"
}
```
8. Create a product if it doesn't exist.

### Consumer Request & Approval

56 changes: 56 additions & 0 deletions gateway/strdata-dev.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
services:
- name: strdata
host: strdss-dev-backend.b0471a-dev.svc
tags: [ns.strdata]
port: 8080
protocol: http
retries: 0
routes:
- name: strdata
tags: [ns.strdata]
hosts:
- dev.strdata.api.gov.bc.ca
methods:
- GET
paths: [/api/organizations/types]
strip_path: false
https_redirect_status_code: 426
path_handling: v0
request_buffering: true
response_buffering: true
plugins:
- name: jwt-keycloak
tags: [ns.strdata]
enabled: true
config:
allowed_iss: [https://loginproxy.gov.bc.ca/auth/realms/apigw, https://dev.loginproxy.gov.bc.ca/auth/realms/apigw, https://test.loginproxy.gov.bc.ca/auth/realms/apigw]
allowed_aud: gateway-strdata
run_on_preflight: true
iss_key_grace_period: 10
maximum_expiration: 0
algorithm: RS256
claims_to_verify:
- exp
uri_param_names:
- jwt
cookie_names: []
scope:
roles:
realm_roles:
client_roles:
anonymous:
consumer_match: true
consumer_match_claim: azp
consumer_match_claim_custom_id: true
consumer_match_ignore_not_found: false
- name: request-transformer
tags: [ns.strdata]
enabled: true
config:
http_method:
- name: kong-upstream-jwt
enabled: true
tags: [ns.strdata]
config:
header: GW-JWT
include_credential_type: false
14 changes: 14 additions & 0 deletions server/StrDss.Api/Controllers/UsersController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -150,5 +150,19 @@ public async Task<ActionResult> GetBceidUserInfo()
var userinfo = await _userService.GetBceidUserInfo();
return Ok(userinfo);
}

[ApiAuthorize(Permissions.UserWrite)]
[HttpPost("aps", Name = "CreateApsUser")]
public async Task<ActionResult> CreateApsUser(ApsUserCreateDto dto)
{
var errors = await _userService.CreateApsUserAsync(dto);

if (errors.Count > 0)
{
return ValidationUtils.GetValidationErrorResult(errors, ControllerContext);
}

return Ok();
}
}
}
1 change: 1 addition & 0 deletions server/StrDss.Data/Mappings/ModelToEntityProfile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public class ModelToEntityProfile : Profile
public ModelToEntityProfile()
{
CreateMap<UserCreateDto, DssUserIdentity>();
CreateMap<ApsUserCreateDto, DssUserIdentity>();
CreateMap<UserDto, DssUserIdentity>();
CreateMap<UserUpdateDto, DssUserIdentity>();
CreateMap<AccessRequestDenyDto, DssUserIdentity>();
Expand Down
16 changes: 16 additions & 0 deletions server/StrDss.Data/Repositories/UserRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public interface IUserRepository
Task<List<DropdownStrDto>> GetAccessRequestStatuses();
Task AcceptTermsConditions();
Task UpdateUserNamesAsync(long userId, string firstName, string lastName);
Task CreateApsUserAsync(ApsUserCreateDto dto);
}
public class UserRepository : RepositoryBase<DssUserIdentity>, IUserRepository
{
Expand Down Expand Up @@ -207,5 +208,20 @@ public async Task UpdateUserNamesAsync(long userId, string firstName, string las
entity.FamilyNm = lastName;
entity.GivenNm = firstName;
}

public async Task CreateApsUserAsync(ApsUserCreateDto dto)
{
var userEntity = _mapper.Map<DssUserIdentity>(dto);

var roleCds = dto.RoleCds.Distinct();

foreach (var roleCd in roleCds)
{
userEntity.DssUserRoleAssignments
.Add(new DssUserRoleAssignment { UserRoleCd = roleCd });
}

await _dbContext.AddAsync(userEntity);
}
}
}
45 changes: 45 additions & 0 deletions server/StrDss.Model/UserDtos/ApsUserCreateDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using System.Text.Json.Serialization;

namespace StrDss.Model.UserDtos
{
public class ApsUserCreateDto : IOrgRoles
{
[JsonIgnore]
public Guid UserGuid { get; set; }

public string DisplayNm { get; set; } = "";

[JsonIgnore]
public string IdentityProviderNm { get; set; } = "aps";

public bool IsEnabled { get; set; } = true;

[JsonIgnore]
public string AccessRequestStatusCd { get; set; } = "Approved";

[JsonIgnore]
public DateTime? AccessRequestDtm { get; set; } = DateTime.UtcNow;

[JsonIgnore]
public string? AccessRequestJustificationTxt { get; set; } = "";

[JsonIgnore]
public string? GivenNm { get; set; } = "";

[JsonIgnore]
public string? FamilyNm { get; set; } = "";

[JsonIgnore]
public string? EmailAddressDsc { get; set; } = "";

[JsonIgnore]
public string? BusinessNm { get; set; } = "";

[JsonIgnore]
public DateTime? TermsAcceptanceDtm { get; set; } = DateTime.UtcNow;

public long RepresentedByOrganizationId { get; set; }

public List<string> RoleCds { get; set; } = new List<string>();
}
}
8 changes: 8 additions & 0 deletions server/StrDss.Model/UserDtos/IOrgRoles.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace StrDss.Model.UserDtos
{
public interface IOrgRoles
{
public long RepresentedByOrganizationId { get; set; }
public List<string> RoleCds { get; set; }
}
}
2 changes: 1 addition & 1 deletion server/StrDss.Model/UserDtos/UserUpdateDto.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace StrDss.Model.UserDtos
{
public class UserUpdateDto
public class UserUpdateDto : IOrgRoles
{
public long UserIdentityId { get; set; }
public long RepresentedByOrganizationId { get; set; }
Expand Down
30 changes: 28 additions & 2 deletions server/StrDss.Service/UserService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public interface IUserService
Task<UserDto?> GetUserByIdAsync(long userId);
Task<Dictionary<string, List<string>>> UpdateUserAsync(UserUpdateDto dto);
Task<BceidAccount?> GetBceidUserInfo();
Task<Dictionary<string, List<string>>> CreateApsUserAsync(ApsUserCreateDto dto);
}
public class UserService : ServiceBase, IUserService
{
Expand Down Expand Up @@ -480,6 +481,13 @@ public async Task<Dictionary<string, List<string>>> ValidateUserUpdateDto(UserUp
return errors;
}

await ValidateOrgAndRoles(dto, errors);

return errors;
}

private async Task ValidateOrgAndRoles(IOrgRoles dto, Dictionary<string, List<string>> errors)
{
var org = await _orgRepo.GetOrganizationByIdAsync(dto.RepresentedByOrganizationId);
if (org == null)
{
Expand All @@ -494,8 +502,6 @@ public async Task<Dictionary<string, List<string>>> ValidateUserUpdateDto(UserUp
errors.AddItem("roleCd", $"Role ({roleCd}) doesn't exist");
}
}

return errors;
}

public async Task<BceidAccount?> GetBceidUserInfo()
Expand All @@ -516,5 +522,25 @@ public async Task<Dictionary<string, List<string>>> ValidateUserUpdateDto(UserUp

return null;
}

public async Task<Dictionary<string, List<string>>> CreateApsUserAsync(ApsUserCreateDto dto)
{
var errors = new Dictionary<string, List<string>>();

if (string.IsNullOrEmpty(dto.DisplayNm))
{
errors.AddItem("client_id", "Client ID is mandatory.");
}

await ValidateOrgAndRoles(dto, errors);

if (errors.Count > 0) return errors;

await _userRepo.CreateApsUserAsync(dto);

_unitOfWork.Commit();

return errors;
}
}
}

0 comments on commit 788744f

Please sign in to comment.