diff --git a/.env b/.env index 34e6d145e..dfe9a079f 100644 --- a/.env +++ b/.env @@ -1 +1 @@ -RELEASE_VERSION=v0.0.13 +RELEASE_VERSION=v1.0.0 diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 07a767e83..34a910e2b 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -53,7 +53,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v1 + uses: github/codeql-action/autobuild@v2 # ℹī¸ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl diff --git a/.github/workflows/manual.yml b/.github/workflows/manual.yml new file mode 100644 index 000000000..ffe1b5dbf --- /dev/null +++ b/.github/workflows/manual.yml @@ -0,0 +1,85 @@ +# This is a basic workflow that is manually triggered + +name: Build and Publish + +# Controls when the action will run. Workflow runs when manually triggered using the UI +# or API. +on: + workflow_dispatch: # Inputs the workflow accepts. + inputs: + version: + description: 'Enter the version number of the build to be generated' + required: true + type: string + +env: + DOCKER_REGISTRY: ghcr.io + DOCKER_NAMESPACE: sunbird-rc + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: # This workflow contains a single job called "greet" + release-all: + name: Build Docker Images and Push to Github Container Registry + # The type of runner that the job will run on + runs-on: ubuntu-latest + needs: [release-sunbird-rc-core] + steps: + - name: Test + run: echo "Test" + + install-dependencies: + name: Install and configure dependencies + runs-on: ubuntu-latest + # Steps represent a sequence of tasks that will be executed as part of the job + steps: # Runs a single command using the runners shell + - name: Checkout Code + uses: actions/checkout@v3 + - name: Set up JDK 11 + uses: actions/setup-java@v3 + with: + java-version: '11' + distribution: 'adopt' + cache: 'maven' + - name: Setup Golang + uses: actions/setup-go@v3 + with: + go-version: 1.19 + - name: Setup NodeJS + uses: actions/setup-node@v3 + with: + node-version: 18 + - name: Log in to GHCR Docker Registry + uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9 + with: + registry: ${{ env.DOCKER_REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Configure Dependencies + run: | + sh configure-dependencies.sh + cd java && ./mvnw clean install + rm -rf java/claim/target/*.jar + jar xvf ../java/registry/target/registry.jar && cp ../java/Dockerfile ./ + + release-sunbird-rc-core: + name: Build and release Sunbird-RC core + needs: install-dependencies + runs-on: ubuntu-latest + env: + IMAGE_NAME: sunbird-rc-core + steps: + - name: Extract metadata (tags, labels) for Sunbird-RC Core + id: registry-meta + uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38 + with: + images: ${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_NAMESPACE }}/${{ env.IMAGE_NAME }} + tags: | + type=raw,value=latest + type=raw,value=${{ inputs.version }} + - name: Build and push Docker image + uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4 + with: + context: target/Dockerfile + push: true + tags: ${{ steps.registry-meta.outputs.tags }} + labels: ${{ steps.registry-meta.outputs.labels }} diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index d5789c7cc..b911613d8 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -22,6 +22,12 @@ jobs: cache: 'maven' - name: Set up properties run: sh configure-dependencies.sh +# # debug step + # - name: Setup upterm session + # uses: lhotari/action-upterm@v1 + # with: + # ## limits ssh access and adds the ssh public key for the user which triggered the workflow ie Sreejith-K + # limit-access-to-actor: true - name: Build and test run: make test # test: diff --git a/.gitignore b/.gitignore index e8d1d0496..cb833ca7d 100644 --- a/.gitignore +++ b/.gitignore @@ -87,5 +87,6 @@ coverage out .ipynb_checkpoints -db-data +db-data* +es-data* keycloak-mobile*.jar \ No newline at end of file diff --git a/Jenkinsfile b/Jenkinsfile index 6497f8a2a..42c0991eb 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -12,8 +12,8 @@ node { } stage('Build image') { - app = docker.build("dockerhub/sunbird-rc-core","target") - claimApp = docker.build("dockerhub/sunbird-rc-claim-ms","java/claim") + app = docker.build("ghcr.io/sunbird-rc/sunbird-rc-core","target") + claimApp = docker.build("ghcr.io/sunbird-rc/sunbird-rc-claim-ms","java/claim") } // stage('Test image') { @@ -27,11 +27,11 @@ node { stage('Push image') { - docker.withRegistry('', 'dockerhub') { + docker.withRegistry('ghcr.io', 'sunbird-rc') { app.push("${env.BUILD_NUMBER}") app.push("latest") } - docker.withRegistry('', 'dockerhub') { + docker.withRegistry('ghrc.io', 'sunbird-rc') { claimApp.push("${env.BUILD_NUMBER}") claimApp.push("latest") } @@ -39,8 +39,8 @@ node { // stage('Deploy image') { // sh "ssh kesavan@10.4.0.6 'kubectl get pods -n ndear'" -// sh "ssh kesavan@10.4.0.6 'kubectl set image deployment/registry registry=dockerhub/sunbird-rc:${env.BUILD_NUMBER} --record --namespace=ndear'" -// sh "ssh kesavan@10.4.0.6 'kubectl set image deployment/claim-ms claim-ms=dockerhub/sunbird-rc-claim-ms:${env.BUILD_NUMBER} --record --namespace=ndear'" +// sh "ssh kesavan@10.4.0.6 'kubectl set image deployment/registry registry=ghcr.io/sunbird-rc/sunbird-rc:${env.BUILD_NUMBER} --record --namespace=ndear'" +// sh "ssh kesavan@10.4.0.6 'kubectl set image deployment/claim-ms claim-ms=ghcr.io/sunbird-rc/sunbird-rc-claim-ms:${env.BUILD_NUMBER} --record --namespace=ndear'" // } } diff --git a/Makefile b/Makefile index 857212c83..e3bd22afa 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,16 @@ #SOURCES = $(wildcard java/**/*.java) rwildcard=$(wildcard $1$2) $(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2)) SOURCES := $(call rwildcard,java/,*.java) -RELEASE_VERSION = v0.0.13 +RELEASE_VERSION = v1.0.0 +IMAGES := ghcr.io/sunbird-rc/sunbird-rc-core ghcr.io/sunbird-rc/sunbird-rc-nginx ghcr.io/sunbird-rc/sunbird-rc-context-proxy-service \ + ghcr.io/sunbird-rc/sunbird-rc-public-key-service ghcr.io/sunbird-rc/sunbird-rc-keycloak ghcr.io/sunbird-rc/sunbird-rc-certificate-api \ + ghcr.io/sunbird-rc/sunbird-rc-certificate-signer ghcr.io/sunbird-rc/sunbird-rc-notification-service ghcr.io/sunbird-rc/sunbird-rc-claim-ms \ + ghcr.io/sunbird-rc/sunbird-rc-digilocker-certificate-api ghcr.io/sunbird-rc/sunbird-rc-bulk-issuance ghcr.io/sunbird-rc/sunbird-rc-metrics \ + ghcr.io/sunbird-rc/id-gen-service ghcr.io/sunbird-rc/encryption-service build: java/registry/target/registry.jar echo ${SOURCES} rm -rf java/claim/target/*.jar - cd target && rm -rf * && jar xvf ../java/registry/target/registry.jar && cp ../java/Dockerfile ./ && docker build -t dockerhub/sunbird-rc-core . + cd target && rm -rf * && jar xvf ../java/registry/target/registry.jar && cp ../java/Dockerfile ./ && docker build -t ghcr.io/sunbird-rc/sunbird-rc-core . make -C java/claim make -C services/certificate-api docker make -C services/certificate-signer docker @@ -13,96 +18,66 @@ build: java/registry/target/registry.jar make -C deps/keycloak build make -C services/public-key-service docker make -C services/context-proxy-service docker - docker build -t dockerhub/sunbird-rc-nginx . + make -C services/metrics docker + make -C services/digilocker-certificate-api docker + make -C services/bulk_issuance docker + make -C services/id-gen-service docker + make -C services/encryption-service docker + docker build -t ghcr.io/sunbird-rc/sunbird-rc-nginx . java/registry/target/registry.jar: $(SOURCES) echo $(SOURCES) sh configure-dependencies.sh cd java && ./mvnw clean install -test: +test: build @docker-compose down @rm -rf db-data* || echo "no permission to delete" - # test with ES & standard definition manager - @RELEASE_VERSION=latest KEYCLOAK_IMPORT_DIR=java/apitest/src/test/resources KEYCLOAK_SECRET=a52c5f4a-89fd-40b9-aea2-3f711f14c889 DB_DIR=db-data-1 docker-compose up -d db es keycloak registry certificate-signer certificate-api + # test with distributed definition manager and native search + @docker-compose --env-file test_environments/test_with_distributedDefManager_nativeSearch.env up -d db keycloak registry certificate-signer certificate-api redis @echo "Starting the test" && sh build/wait_for_port.sh 8080 @echo "Starting the test" && sh build/wait_for_port.sh 8081 @docker-compose ps @curl -v http://localhost:8081/health - @cd java/apitest && ../mvnw -Pe2e test || echo 'Tests failed' + @cd java/apitest && ../mvnw -Pe2e test @docker-compose down @rm -rf db-data-1 || echo "no permission to delete" - # test with distributed definition manager - @RELEASE_VERSION=latest KEYCLOAK_IMPORT_DIR=java/apitest/src/test/resources KEYCLOAK_SECRET=a52c5f4a-89fd-40b9-aea2-3f711f14c889 MANAGER_TYPE=DistributedDefinitionsManager DB_DIR=db-data-2 docker-compose up -d db es keycloak registry certificate-signer certificate-api redis + # test with kafka(async), events, notifications, + @docker-compose --env-file test_environments/test_with_asyncCreate_events_notifications.env up -d db es clickhouse redis keycloak registry certificate-signer certificate-api kafka zookeeper notification-ms metrics @echo "Starting the test" && sh build/wait_for_port.sh 8080 @echo "Starting the test" && sh build/wait_for_port.sh 8081 @docker-compose ps @curl -v http://localhost:8081/health - @cd java/apitest && ../mvnw -Pe2e test || echo 'Tests failed' + @cd java/apitest && MODE=async ../mvnw -Pe2e test @docker-compose down @rm -rf db-data-2 || echo "no permission to delete" - # test with native search service - @RELEASE_VERSION=latest KEYCLOAK_IMPORT_DIR=java/apitest/src/test/resources KEYCLOAK_SECRET=a52c5f4a-89fd-40b9-aea2-3f711f14c889 DB_DIR=db-data-3 SEARCH_PROVIDER_NAME=dev.sunbirdrc.registry.service.NativeSearchService EXPAND_REFERENCE=false docker-compose up -d db keycloak registry certificate-signer certificate-api - @echo "Starting the test" && sh build/wait_for_port.sh 8080 + # test with fusionauth + @docker-compose --env-file test_environments/test_with_fusionauth.env -f docker-compose.yml -f services/sample-fusionauth-service/docker-compose.yml up -d db es fusionauth fusionauthwrapper + sleep 20 + @echo "Starting the test" && sh build/wait_for_port.sh 9011 + @echo "Starting the test" && sh build/wait_for_port.sh 3990 + sleep 20 + @docker-compose --env-file test_environments/test_with_fusionauth.env -f docker-compose.yml -f services/sample-fusionauth-service/docker-compose.yml up -d --no-deps registry @echo "Starting the test" && sh build/wait_for_port.sh 8081 - @docker-compose ps + @docker-compose -f docker-compose.yml -f services/sample-fusionauth-service/docker-compose.yml ps @curl -v http://localhost:8081/health - @cd java/apitest && ../mvnw -Pe2e test || echo 'Tests failed' - @docker-compose down + @cd java/apitest && MODE=fusionauth ../mvnw -Pe2e test || echo 'Tests failed' + @docker-compose -f docker-compose.yml -f services/sample-fusionauth-service/docker-compose.yml down @rm -rf db-data-3 || echo "no permission to delete" - # test with kafka(async) - @ASYNC_ENABLED=true RELEASE_VERSION=latest KEYCLOAK_IMPORT_DIR=java/apitest/src/test/resources KEYCLOAK_SECRET=a52c5f4a-89fd-40b9-aea2-3f711f14c889 DB_DIR=db-data-4 docker-compose up -d db es keycloak registry certificate-signer certificate-api kafka zookeeper - @echo "Starting the test" && sh build/wait_for_port.sh 8080 - @echo "Starting the test" && sh build/wait_for_port.sh 8081 - @docker-compose ps - @curl -v http://localhost:8081/health - @cd java/apitest && MODE=async ../mvnw -Pe2e test || echo 'Tests failed' - @docker-compose down - @rm -rf db-data-4 || echo "no permission to delete" - # test with elastic search service - @RELEASE_VERSION=latest KEYCLOAK_IMPORT_DIR=java/apitest/src/test/resources KEYCLOAK_SECRET=a52c5f4a-89fd-40b9-aea2-3f711f14c889 DB_DIR=db-data-5 SEARCH_PROVIDER_NAME=dev.sunbirdrc.registry.service.ElasticSearchService EXPAND_REFERENCE=false docker-compose up -d db keycloak registry certificate-signer certificate-api - @echo "Starting the test" && sh build/wait_for_port.sh 8080 - @echo "Starting the test" && sh build/wait_for_port.sh 8081 - @docker-compose ps - @curl -v http://localhost:8081/health - @cd java/apitest && ../mvnw -Pe2e test || echo 'Tests failed' - @docker-compose down - @rm -rf db-data-5 || echo "no permission to delete" make -C services/certificate-signer test make -C services/public-key-service test make -C services/context-proxy-service test + make -C services/bulk_issuance test clean: @rm -rf target || true @rm java/registry/target/registry.jar || true release: test - docker tag dockerhub/sunbird-rc-core dockerhub/sunbird-rc-core:$(RELEASE_VERSION) - docker tag dockerhub/sunbird-rc-claim-ms dockerhub/sunbird-rc-claim-ms:$(RELEASE_VERSION) - docker tag dockerhub/sunbird-rc-notification-service dockerhub/sunbird-rc-notification-service:$(RELEASE_VERSION) - docker tag dockerhub/sunbird-rc-certificate-signer dockerhub/sunbird-rc-certificate-signer:$(RELEASE_VERSION) - docker tag dockerhub/sunbird-rc-certificate-api dockerhub/sunbird-rc-certificate-api:$(RELEASE_VERSION) - docker tag dockerhub/sunbird-rc-keycloak dockerhub/sunbird-rc-keycloak:$(RELEASE_VERSION) - docker tag dockerhub/sunbird-rc-public-key-service dockerhub/sunbird-rc-public-key-service:$(RELEASE_VERSION) - docker tag dockerhub/sunbird-rc-context-proxy-service dockerhub/sunbird-rc-context-proxy-service:$(RELEASE_VERSION) - docker tag dockerhub/sunbird-rc-nginx dockerhub/sunbird-rc-nginx:$(RELEASE_VERSION) - docker push dockerhub/sunbird-rc-core:latest - docker push dockerhub/sunbird-rc-core:$(RELEASE_VERSION) - docker push dockerhub/sunbird-rc-claim-ms:latest - docker push dockerhub/sunbird-rc-claim-ms:$(RELEASE_VERSION) - docker push dockerhub/sunbird-rc-notification-service:latest - docker push dockerhub/sunbird-rc-notification-service:$(RELEASE_VERSION) - docker push dockerhub/sunbird-rc-certificate-signer:latest - docker push dockerhub/sunbird-rc-certificate-signer:$(RELEASE_VERSION) - docker push dockerhub/sunbird-rc-certificate-api:latest - docker push dockerhub/sunbird-rc-certificate-api:$(RELEASE_VERSION) - docker push dockerhub/sunbird-rc-keycloak:latest - docker push dockerhub/sunbird-rc-keycloak:$(RELEASE_VERSION) - docker push dockerhub/sunbird-rc-public-key-service:latest - docker push dockerhub/sunbird-rc-public-key-service:$(RELEASE_VERSION) - docker push dockerhub/sunbird-rc-context-proxy-service:latest - docker push dockerhub/sunbird-rc-context-proxy-service:$(RELEASE_VERSION) - docker push dockerhub/sunbird-rc-nginx:latest - docker push dockerhub/sunbird-rc-nginx:$(RELEASE_VERSION) - @cd tools/cli/ && npm publish - - + for image in $(IMAGES); \ + do \ + echo $$image; \ + docker tag $$image:latest $$image:$(RELEASE_VERSION);\ + docker push $$image:latest;\ + docker push $$image:$(RELEASE_VERSION);\ + done + @cd tools/cli/ && npm publish \ No newline at end of file diff --git a/api-documentation/identity-api.yaml b/api-documentation/identity-api.yaml new file mode 100644 index 000000000..2128544a5 --- /dev/null +++ b/api-documentation/identity-api.yaml @@ -0,0 +1,382 @@ +--- +openapi: 3.0.0 +info: + title: Sunbird RC - Identity APIs + description: Identity APIs + termsOfService: https://sunbirdrc.dev/ + contact: + email: sunbird@example.com + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0.html + version: 1.0.0 +servers: +- url: https://sunbirdrc.dev/api/v1 +security: +- Authorization: [] +tags: +- name: Health + description: Health Check API +- name: Identity + description: The following APIs are defined for generating and resolving a DID +- name: Utils + description: Some common utilities +paths: + /health: + get: + tags: + - Health + operationId: AppController_checkHealth + parameters: [] + responses: + '200': + description: The Health Check is successful + content: + application/json: + schema: + type: object + properties: + status: + type: string + example: ok + info: + type: object + example: + database: + status: up + additionalProperties: + type: object + properties: + status: + type: string + additionalProperties: + type: string + nullable: true + error: + type: object + example: {} + additionalProperties: + type: object + properties: + status: + type: string + additionalProperties: + type: string + nullable: true + details: + type: object + example: + database: + status: up + additionalProperties: + type: object + properties: + status: + type: string + additionalProperties: + type: string + '503': + description: The Health Check is not successful + content: + application/json: + schema: + type: object + properties: + status: + type: string + example: error + info: + type: object + example: + database: + status: up + additionalProperties: + type: object + properties: + status: + type: string + additionalProperties: + type: string + nullable: true + error: + type: object + example: + redis: + status: down + message: Could not connect + additionalProperties: + type: object + properties: + status: + type: string + additionalProperties: + type: string + nullable: true + details: + type: object + example: + database: + status: up + redis: + status: down + message: Could not connect + additionalProperties: + type: object + properties: + status: + type: string + additionalProperties: + type: string + /did/generate: + post: + tags: + - Identity + operationId: DidController_generateDID + summary: Generate a new DID + parameters: [] + requestBody: + required: true + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/GenerateDidDTO' + responses: + '200': + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/DidResponseDTO' + description: DID Generated + '400': + description: Bad request + /did/resolve/{id}: + get: + tags: + - Identity + operationId: DidController_resolveDID + summary: Resolve a DID ID + parameters: + - name: id + required: true + in: path + description: The DID ID to resolve + schema: + type: string + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/DidResponseDTO' + description: DID resolved + '400': + description: Bad Request + '404': + description: DID not found + /utils/sign: + post: + tags: + - Utils + operationId: VcController_sign + summary: Sign an unsigned VC + parameters: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/SignJsonDTO' + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/SignedVCDTO' + description: VC Signed + '400': + description: Bad Request + '500': + description: Internal Server Error + /utils/verify: + post: + tags: + - Utils + operationId: VcController_verify + summary: Verify a signed VC + parameters: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/VerifyJsonDTO' + responses: + '201': + content: + application/json: + schema: + type: boolean + example: true + description: '' +components: + schemas: + GenerateDidDTO: + type: object + properties: + alsoKnownAs: + description: AlsoKnownAs property is a unique combination aadhaar and username. + type: array + items: + type: string + example: ramesh@gmail.com + services: + description: An array of services that are used, for example a user registration + service. + type: array + items: + type: object + properties: + id: + type: string + type: + type: string + serviceEndpoint: + type: object + additionalProperties: true + example: '{ + "id": "IdentityHub", + "type": "IdentityHub", + "serviceEndpoint": { + "@context": "schema.identity.foundation/hub", + "@type": "UserServiceEndpoint", + "instance": [ + "did:test:hub.id" + ] + } + }' + method: + type: string + description: The method of DID. + required: + - alsoKnownAs + - services + - method + SignJsonDTO: + type: object + properties: + DID: + type: string + description: The unique DID id of the issuer. + payload: + type: string + description: JSON string of the unsigned VC. + example: '{ + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://www.w3.org/2018/credentials/examples/v1" + ], + "id": "did:ulp:b4a191af-d86e-453c-9d0e-dd4771067235", + "type": [ + "VerifiableCredential", + "UniversityDegreeCredential" + ], + "issuer": "did:rcw:6b9d7b31-bc7f-454a-be30-b6c7447b1cff", + "expirationDate": "2023-02-08T11:56:27.259Z", + "credentialSubject": { + "id": "did:rcw:6b9d7b31-bc7f-454a-be30-b6c7447b1cff", + "grade": "9.23", + "programme": "B.Tech", + "certifyingInstitute": "IIIT Sonepat", + "evaluatingInstitute": "NIT Kurukshetra" + }, + "options": { + "created": "2020-04-02T18:48:36Z", + "credentialStatus": { + "type": "RevocationList2020Status" + } + } + }' + required: + - DID + - payload + VerifyJsonDTO: + type: object + properties: + DID: + type: string + required: + - DID + DidResponseDTO: + type: object + properties: + id: + type: string + example: did:rcw:71364c7e-0abe-49cf-9269-4661699f274b + alsoKnownAs: + description: AlsoKnownAs property is a unique combination aadhaar and username. + type: array + items: + type: string + example: ramesh@gmail.com + service: + description: An array of services that are used, for example a user registration + service. + type: array + items: + type: object + properties: + id: + type: string + type: + type: string + serviceEndpoint: + type: object + additionalProperties: true + example: '{ + "id": "IdentityHub", + "type": "IdentityHub", + "serviceEndpoint": { + "@context": "schema.identity.foundation/hub", + "@type": "UserServiceEndpoint", + "instance": [ + "did:test:hub.id" + ] + } + }' + verificationMethod: + type: object + properties: + id: + type: string + example: auth-key + type: + type: string + example: RS256 + publicKeyJwk: + type: object + controller: + type: string + example: did:rcw:71364c7e-0abe-49cf-9269-4661699f274b + description: The method of DID + authentication: + type: string + example: auth-key + SignedVCDTO: + type: object + properties: + publicKey: + type: object + description: Public Key of issuer + example: '{"kty": "EC","crv": "secp256k1","x": "1iTtnvgP141NM-4qC6BgmkeTAjV7u-gZWni71G7cAKo","y": "VSGqq6yS0w7riXXRqFxXwKvHgIpQaUNMlFQKh-xgKMI"}' + signed: + type: string + description: Signed VC + securitySchemes: + Authorization: + type: apiKey + name: Token + in: header diff --git a/api-documentation/issue-api.yaml b/api-documentation/issue-api.yaml new file mode 100644 index 000000000..341956b44 --- /dev/null +++ b/api-documentation/issue-api.yaml @@ -0,0 +1,399 @@ +openapi: 3.0.1 +info: + title: Sunbird RC - Issuance APIs + description: Schema APIs + termsOfService: https://sunbirdrc.dev/ + contact: + email: sunbird@example.com + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0.html + version: 1.0.0 +servers: +- url: https://sunbirdrc.dev/api/v1 +security: +- Authorization: [] +tags: +- name: Issuing + description: Issuing api +- name: Verifying + description: The following APIs are defined for verifying a Verifiable Credential +paths: + /credentials/issue: + post: + tags: + - Issuing + summary: Credential claim API + operationId: credentialIssue + requestBody: + description: Credential payload + content: + 'appliation/json': + schema: + $ref: '#/components/schemas/credentialRequest' + required: true + responses: + 201: + description: Credential successfully issued! + content: + 'application/json': + schema: + $ref: '#/components/schemas/credentialResponse' + 400: + description: Invalid Input + content: {} + 500: + description: error + content: {} + x-codegen-request-body-name: body + /credentials/{id}: + get: + tags: + - Issuing + summary: Get credentials by id + operationId: getCredentialsById + parameters: + - name: id + in: path + required: true + schema: + type: string + - name: templateId + in: header + required: false + schema: + type: string + - name: accept + in: header + required: true + schema: + type: string + enum: + - application/json + - application/vc+ld+json + - text/html + - image/svg+xml + - text/plain + - image/jpeg + responses: + 200: + description: OK + content: + 'appliation/json': + schema: + $ref: '#/components/schemas/verifiableCredential' + 401: + description: Not Authorized + content: {} + 404: + description: Not Found + content: {} + 500: + description: Error + content: {} + delete: + tags: + - Issuing + summary: Revoke credentials by id + operationId: revokeCredentialsById + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + 200: + description: OK + content: + 'appliation/json': + schema: + $ref: '#/components/schemas/revokedResponse' + 401: + description: Not Authorized + content: {} + 404: + description: Not Found + content: {} + 500: + description: Error + content: {} + /credentials/search: + post: + tags: + - Issuing + summary: Search credentials by issuer and subject + operationId: searchCredentials + requestBody: + description: Search payload + content: + 'appliation/json': + schema: + type: object + properties: + issuer: + type: object + properties: + id: + type: string + subject: + type: object + properties: + id: + type: string + type: + type: string + enum: ["claim", "sign"] + required: true + responses: + 200: + description: OK + content: + 'appliation/json': + schema: + type: array + items: + $ref: '#/components/schemas/verifiableCredential' + 401: + description: Not Authorized + content: {} + 404: + description: Not Found + content: {} + 500: + description: Error + content: {} + /credentials/{id}/verify: + get: + tags: + - Verifying + summary: Verifies a verifiableCredential and returns a verificationResult in + the response body. + description: Verifies a verifiableCredential and returns a verificationResult + in the response body. + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + 200: + description: OK + content: + 'appliation/json': + schema: + $ref: '#/components/schemas/verifyResponse' + /revocation-list: + get: + tags: + - Revocation + summary: Complete list of revoked credentials (Need to be discussed). + responses: + 200: + description: OK + content: + 'appliation/json': + schema: + type: array + items: + type: object +components: + schemas: + revokedResponse: + type: object + properties: + revocationListIndex: + type: string + verifyResponse: + type: object + properties: + status: + type: string + enum: ["Active", "Revoked", "Expired"] + checks: + type: array + items: + type: object + properties: + active: + type: string + enum: ["OK"] + revoked: + type: string + enum: ["OK"] + expired: + type: string + enum: ["OK"] + proof: + description: "Check if the issuer " + type: string + enum: ["OK"] + warnings: + type: array + items: + type: string + errors: + type: array + items: + type: string + verifyRequest: + type: object + properties: + verifiableCredential: + $ref: '#/components/schemas/verifiableCredentialType' + options: + type: object + properties: + challenge: + type: string + domain: + type: string + credentialResponse: + type: object + properties: + credential: + allOf: + - $ref: '#/components/schemas/verifiableCredentialType' + - type: object + properties: + proof: + type: object + properties: + type: + type: string + created: + type: string + challenge: + type: string + domain: + type: string + nonce: + type: string + verificationMethod: + type: string + proofPurpose: + type: string + jws: + type: string + proofValue: + type: string + credentialSchemaId: + type: string + tags: + type: array + items: + type: string + + createdAt: + type: string + updatedAt: + type: string + createdBy: + type: string + updatedBy: + type: string + credentialRequest: + type: object + properties: + credential: + $ref: '#/components/schemas/verifiableCredentialType' + credentialSchemaId: + type: string + tags: + type: array + items: + type: string + + credentialOptions: + type: object + properties: + created: + type: string + challenge: + type: string + domain: + type: string + credentialStatus: + type: object + properties: + type: + type: string + verifiableCredentialType: + type: object + properties: + context: + type: array + items: + type: string + id: + type: string + type: + type: string + issuer: + type: object + properties: {} + issuanceDate: + type: string + format: date-time + expirationDate: + type: string + format: date-time + credentialSubject: + type: object + properties: {} + + verifiableCredential: + type: object + properties: + context: + type: array + items: + type: string + id: + type: string + type: + type: string + issuer: + type: object + properties: {} + issuanceDate: + type: string + format: date-time + expirationDate: + type: string + format: date-time + credentialSubject: + type: object + properties: {} + proof: + type: object + properties: + type: + type: string + created: + type: string + format: 'date-time' + challenge: + type: string + domain: + type: string + nonce: + type: string + verificationMethod: + type: string + proofPurpose: + type: string + jws: + type: string + proofValue: + type: string + + + + + securitySchemes: + Authorization: + type: apiKey + name: Token + in: header diff --git a/api-documentation/schema-api.yaml b/api-documentation/schema-api.yaml new file mode 100644 index 000000000..d0df25f82 --- /dev/null +++ b/api-documentation/schema-api.yaml @@ -0,0 +1,375 @@ +openapi: 3.0.1 +info: + title: Sunbird RC - Schema APIs + description: Schema APIs + termsOfService: https://sunbirdrc.dev/ + contact: + email: sunbird@example.com + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0.html + version: 1.0.0 +servers: +- url: https://sunbirdrc.dev/api/v1 +security: +- Authorization: [] +tags: +- name: Credential Schemas +- name: Rendering Templates +paths: + /credential-schema: + post: + tags: + - Credential Schemas + summary: Create credential schema + operationId: createCredentialSchema + requestBody: + description: Payload + content: + 'appliation/json': + schema: + $ref: '#/components/schemas/credentialSchemaRequest' + required: true + responses: + 201: + description: Credential Schema successfully created! + content: + 'application/json': + schema: + $ref: '#/components/schemas/credentialSchemaResponse' + 400: + description: Invalid Input + content: {} + 500: + description: error + content: {} + get: + tags: + - Credential Schemas + summary: Get credential schema by tags + operationId: getCredentialSchemas + parameters: + - in: query + name: tags + schema: + type: string + example: tag1,tag2 + responses: + 201: + description: Credential Schemas + content: + 'application/json': + schema: + type: array + items: + $ref: '#/components/schemas/credentialSchemaResponse' + 400: + description: Invalid Input + content: {} + 500: + description: error + content: {} + /credential-schema/{id}: + get: + tags: + - Credential Schemas + summary: Get credential schema by id + operationId: getCredentialSchema + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + 200: + description: Response + content: + 'application/json': + schema: + $ref: '#/components/schemas/credentialSchemaResponse' + 400: + description: Invalid Input + content: {} + 500: + description: error + content: {} + put: + tags: + - Credential Schemas + summary: Update credential schema by id + description: If the schema is updated, a new semver will be generated and the old one will be deprecated. If only metadata (like tags/status) is updated, the semver will not be generated + operationId: updateCredentialSchema + parameters: + - name: id + in: path + required: true + schema: + type: string + requestBody: + description: Payload + content: + 'appliation/json': + schema: + $ref: '#/components/schemas/credentialSchemaRequest' + required: true + responses: + 200: + description: Credential Schema successfully updated! + content: + 'application/json': + schema: + allOf: + - $ref: '#/components/schemas/credentialSchemaResponse' + - type: object + properties: + deprecatedId: + description: returns back the id of the schema that was deprecated + type: string + 400: + description: Invalid Input + content: {} + 500: + description: error + content: {} + /template: + post: + tags: + - Rendering Templates + summary: Create redenring templates + operationId: createRenderingTemplates + requestBody: + description: Payload + content: + 'appliation/json': + schema: + $ref: '#/components/schemas/renderingTemplateRequest' + required: true + responses: + 201: + description: Rendering template successfully created! + content: + 'application/json': + schema: + $ref: '#/components/schemas/renderingTemplateResponse' + 400: + description: Invalid Input + content: {} + 500: + description: error + content: {} + get: + tags: + - Rendering Templates + summary: Get rendering templates by schema id + operationId: getRenderingTemplatesBySchemaId + parameters: + - name: schemaId + in: query + required: true + schema: + type: string + responses: + 200: + description: Response + content: + 'application/json': + schema: + $ref: '#/components/schemas/renderingTemplateResponse' + 400: + description: Invalid Input + content: {} + 500: + description: error + content: {} + /template/{id}: + get: + tags: + - Rendering Templates + summary: Get rendering templates by id + operationId: getRenderingTemplates + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + 200: + description: Response + content: + 'application/json': + schema: + $ref: '#/components/schemas/renderingTemplateResponse' + 400: + description: Invalid Input + content: {} + 500: + description: error + content: {} + put: + tags: + - Rendering Templates + summary: Update rendering templates by id + description: Create a new version of template + operationId: updateRenderingTemplates + parameters: + - name: id + in: path + required: true + schema: + type: string + requestBody: + description: Payload + content: + 'appliation/json': + schema: + $ref: '#/components/schemas/renderingTemplateRequest' + required: true + responses: + 200: + description: Rendering template successfully updated! + content: + 'application/json': + schema: + $ref: '#/components/schemas/renderingTemplateResponse' + 400: + description: Invalid Input + content: {} + 500: + description: error + content: {} + delete: + tags: + - Rendering Templates + summary: Delete rendering templates by id + operationId: deleteRenderingTemplates + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + 200: + description: Credential Schema successfully deleted! + 400: + description: Invalid Input + content: {} + 500: + description: error + content: {} + +components: + schemas: + renderingTemplateResponse: + allOf: + - $ref: '#/components/schemas/renderingTemplateRequest' + - type: object + properties: + templateId: + type: string + example: "did:cred-template:MDP8AsFhHzhwUvGNuYkX7T/06e126d1-fa44-4882-a243-1e326fbe21dc;version=1.1" + createdAt: + type: string + updatedAt: + type: string + createdBy: + type: string + updatedBy: + type: string + renderingTemplateRequest: + type: object + properties: + template: + description: It should be valid string in handlebar format + type: string + schemaId: + type: string + example: "did:cred-schema:MDP8AsFhHzhwUvGNuYkX7T/06e126d1-fa44-4882-a243-1e326fbe21db;version=1.1" + updateSchemaRequest: + type: object + properties: + tags: + type: array + items: + type: string + status: + type: string + enum: [DRAFT,PUBLISHED, REVOKED] + credentialSchemaRequest: + type: object + properties: + schema: + type: object + properties: + type: + type: string + id: + type: string + pattern: "^\\d+\\.\\d+$" + example: "did:cred-schema:MDP8AsFhHzhwUvGNuYkX7T/06e126d1-fa44-4882-a243-1e326fbe21db;version=1.1" + version: + type: string + name: + type: string + author: + type: string + authored: + type: string + schema: + type: object + example: '{"$schema":"https://json-schema.org/draft/2020-12/schema","description":"Email","type":"object","properties":{"emailAddress":{"type":"string","format":"email"}},"required":["emailAddress"],"additionalProperties":false}' + tags: + type: array + items: + type: string + status: + type: string + enum: [DRAFT,PUBLISHED, REVOKED] + + credentialSchemaResponse: + type: object + properties: + schema: + type: object + properties: + type: + type: string + id: + type: string + pattern: "^\\d+\\.\\d+$" + example: "did:cred-schema:MDP8AsFhHzhwUvGNuYkX7T/06e126d1-fa44-4882-a243-1e326fbe21db;version=1.1" + version: + type: string + name: + type: string + author: + type: string + authored: + type: string + schema: + type: object + example: '{"$schema":"https://json-schema.org/draft/2020-12/schema","description":"Email","type":"object","properties":{"emailAddress":{"type":"string","format":"email"}},"required":["emailAddress"],"additionalProperties":false}' + proof: + type: object + tags: + type: array + items: + type: string + status: + type: string + enum: [DRAFT,PUBLISHED, REVOKED] + createdAt: + type: string + updatedAt: + type: string + createdBy: + type: string + updatedBy: + type: string + securitySchemes: + Authorization: + type: apiKey + name: Token + in: header diff --git a/build/wait_for_port.sh b/build/wait_for_port.sh index 33be08e7e..ee3573115 100644 --- a/build/wait_for_port.sh +++ b/build/wait_for_port.sh @@ -4,7 +4,7 @@ echo "Waiting service to launch on $1..." i=0 while ! curl localhost:$1; do sleep 10 - let i=i+1 + ((i=i+1)) if [[ $i -gt 60 ]]; then echo "Failed to get the service in sane state!" exit 1; diff --git a/deps/keycloak-mobile-number-login-spi/src/main/java/in/divoc/api/authenticator/IValidation.java b/deps/keycloak-mobile-number-login-spi/src/main/java/in/divoc/api/authenticator/IValidation.java new file mode 100644 index 000000000..a5136fdbd --- /dev/null +++ b/deps/keycloak-mobile-number-login-spi/src/main/java/in/divoc/api/authenticator/IValidation.java @@ -0,0 +1,5 @@ +package in.divoc.api.authenticator; + +public interface IValidation { + boolean validate(String mobileNumber); +} diff --git a/deps/keycloak-mobile-number-login-spi/src/main/java/in/divoc/api/authenticator/MobileNumberAuthenticator.java b/deps/keycloak-mobile-number-login-spi/src/main/java/in/divoc/api/authenticator/MobileNumberAuthenticator.java index 1163abb3a..7bf6edf29 100644 --- a/deps/keycloak-mobile-number-login-spi/src/main/java/in/divoc/api/authenticator/MobileNumberAuthenticator.java +++ b/deps/keycloak-mobile-number-login-spi/src/main/java/in/divoc/api/authenticator/MobileNumberAuthenticator.java @@ -8,8 +8,11 @@ import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; +import org.keycloak.representations.idm.ErrorRepresentation; import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.MediaType; import java.io.IOException; import java.util.Collections; import java.util.List; @@ -22,6 +25,8 @@ public class MobileNumberAuthenticator extends AbstractUsernameFormAuthenticator private final OTPService mockOTPService; private final OTPService otpService; private final NotifyService notifyService; + private final IValidation iValidation; + private static final String mockOtp = System.getenv().getOrDefault("MOCK_OTP", "true"); @@ -29,6 +34,7 @@ public MobileNumberAuthenticator() { this.mockOTPService = new MockOTPServiceImpl(); this.otpService = new OTPServiceImpl(); this.notifyService = new NotifyService(); + this.iValidation = new ValidationService(); } @Override @@ -39,32 +45,51 @@ public void action(AuthenticationFlowContext context) { System.err.println("action request " + type); if (type.equals(LOGIN_FORM)) { //TODO: rename form id + String INVALID_REGISTRATION = "INVALID_REGISTRATION"; + String INVALID_USERNAME = "INVALID_USERNAME"; String mobileNumber = formData.getFirst(MOBILE_NUMBER); - List users = context.getSession().users() - .searchForUserByUserAttributeStream(context.getSession().getContext().getRealm(), MOBILE_NUMBER, mobileNumber).collect(Collectors.toList()); - if (users.size() > 0) { - generateOTPAndNotify(context, mobileNumber, users); - } else { - users = context.getSession().users() - .searchForUserByUserAttributeStream(context.getSession().getContext().getRealm(), EMAIL, mobileNumber).collect(Collectors.toList()); + if(iValidation.validate(mobileNumber)) { + List users = context.getSession().users() + .searchForUserByUserAttributeStream(context.getSession().getContext().getRealm(), MOBILE_NUMBER, mobileNumber).collect(Collectors.toList()); if (users.size() > 0) { + if (checkIfMaxResendOtpLimitReached(context)) return; generateOTPAndNotify(context, mobileNumber, users); } else { - context.failure(AuthenticationFlowError.INVALID_USER); + users = context.getSession().users() + .searchForUserByUserAttributeStream(context.getSession().getContext().getRealm(), EMAIL, mobileNumber).collect(Collectors.toList()); + if (users.size() > 0) { + generateOTPAndNotify(context, mobileNumber, users); + } else { + Response response = context.form().setError(System.getenv().getOrDefault(INVALID_REGISTRATION, "No user found with this username")).createForm(MOBILE_LOGIN_UI); + context.failure(AuthenticationFlowError.INVALID_USER, response); + } } } + else { + Response response = context.form().setError(System.getenv().getOrDefault(INVALID_USERNAME, "Please enter correct Username")).createForm(MOBILE_LOGIN_UI); + context.failure(AuthenticationFlowError.INVALID_USER, response); + } } else if (type.equals(VERIFY_OTP_FORM)) { String sessionKey = context.getAuthenticationSession().getAuthNote(OTP); if (sessionKey != null) { String secret = formData.getFirst(OTP); + String VALID_OTP = "VALID_OTP"; if (secret != null) { if (secret.equals(sessionKey)) { context.success(); } else { - context.failure(AuthenticationFlowError.INVALID_CREDENTIALS); + Response response = context.form().setError(System.getenv().getOrDefault(VALID_OTP, "Please enter correct OTP")).createForm(VERIFY_OTP_UI); + if(checkIfMaxOtpTriesReached(context)) { + return; + } + context.failure(AuthenticationFlowError.INVALID_CREDENTIALS, response); } } else { - context.failure(AuthenticationFlowError.INVALID_CREDENTIALS); + Response response = context.form().setError(System.getenv().getOrDefault(VALID_OTP, "Please enter correct OTP")).createForm(VERIFY_OTP_UI); + if(checkIfMaxOtpTriesReached(context)) { + return; + } + context.failure(AuthenticationFlowError.INVALID_CREDENTIALS, response); } } else { context.challenge(context.form().createForm(MOBILE_LOGIN_UI)); @@ -72,6 +97,41 @@ public void action(AuthenticationFlowContext context) { } } + private static boolean checkIfMaxResendOtpLimitReached(AuthenticationFlowContext context) { + String MAX_RESEND_TRIES = "MAX_RESEND_TRIES"; + String RESEND_OTP_TRY_COUNT = "RESEND_OTP_TRY_COUNT"; + String resendTries = context.getAuthenticationSession().getAuthNote(RESEND_OTP_TRY_COUNT); + System.out.println("RESEND RETRIES : " + resendTries); + int count = resendTries == null ? 0 : Integer.parseInt(resendTries); + count++; + if(count == Integer.parseInt(System.getenv().getOrDefault(MAX_RESEND_TRIES, "3")) + 1) { + context.getAuthenticationSession().setAuthNote(RESEND_OTP_TRY_COUNT, null); + context.failure(AuthenticationFlowError.INTERNAL_ERROR); + return true; + } + context.getAuthenticationSession().setAuthNote(RESEND_OTP_TRY_COUNT, count + "" ); + return false; + } + + private boolean checkIfMaxOtpTriesReached(AuthenticationFlowContext context) { + String OTP_TRIES = "OTP_TRIES"; + String OTP_MAX_RETRY_LIMIT = "OTP_MAX_RETRY_LIMIT"; + String otpTries = context.getAuthenticationSession().getAuthNote(OTP_TRIES); + System.out.println("OTP TRIES : " + otpTries); + int count = (otpTries == null ? 0 : Integer.parseInt(otpTries)); + count++; + int maxLimit = Integer.parseInt(System.getenv().getOrDefault(OTP_MAX_RETRY_LIMIT, "3")); + if(count == maxLimit) { + context.getAuthenticationSession().setAuthNote(OTP_TRIES, null); + String MAX_RETRIES_LIMIT_MESSAGE = "MAX_RETRIES_LIMIT_MESSAGE"; + Response response = context.form().setError(System.getenv().getOrDefault(MAX_RETRIES_LIMIT_MESSAGE, "Max failed login limit reached")).createForm(MOBILE_LOGIN_UI); + context.failure(AuthenticationFlowError.INVALID_CREDENTIALS, response); + return true; + } + context.getAuthenticationSession().setAuthNote(OTP_TRIES, "" + count); + return false; + } + private void generateOTPAndNotify(AuthenticationFlowContext context, String mobileNumber, List users) { UserModel user = users.get(0); String otp; diff --git a/deps/keycloak-mobile-number-login-spi/src/main/java/in/divoc/api/authenticator/NotifyService.java b/deps/keycloak-mobile-number-login-spi/src/main/java/in/divoc/api/authenticator/NotifyService.java index b5f74b8b7..901dd18f8 100644 --- a/deps/keycloak-mobile-number-login-spi/src/main/java/in/divoc/api/authenticator/NotifyService.java +++ b/deps/keycloak-mobile-number-login-spi/src/main/java/in/divoc/api/authenticator/NotifyService.java @@ -1,7 +1,9 @@ package in.divoc.api.authenticator; -import okhttp3.*; -import org.jboss.logging.Logger; + +import org.apache.commons.lang.exception.ExceptionUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.BufferedReader; import java.io.IOException; @@ -12,7 +14,7 @@ import java.nio.charset.StandardCharsets; public class NotifyService { - private static final Logger logger = Logger.getLogger(NotifyService.class); + private static final Logger logger = LoggerFactory.getLogger(NotifyService.class); private static final String notificationURL = System.getenv().getOrDefault("NOTIFICATION_SERVICE_URL", "http://localhost:8765/notification-service/v1/notification"); private static final String messageTemplate = System.getenv().getOrDefault("MESSAGE_TEMPLATE", "Your OTP to access SunbirdRC portal is %s. Please dont share this with anyone."); @@ -44,7 +46,8 @@ public void notify(String to, String otp) throws IOException { System.out.println(response.toString()); } } catch (IOException e) { - logger.error(e); + logger.error("Exception occurred while notifying: {}", ExceptionUtils.getStackTrace(e)); + throw e; } } diff --git a/deps/keycloak-mobile-number-login-spi/src/main/java/in/divoc/api/authenticator/ValidationService.java b/deps/keycloak-mobile-number-login-spi/src/main/java/in/divoc/api/authenticator/ValidationService.java new file mode 100644 index 000000000..582f17c46 --- /dev/null +++ b/deps/keycloak-mobile-number-login-spi/src/main/java/in/divoc/api/authenticator/ValidationService.java @@ -0,0 +1,9 @@ +package in.divoc.api.authenticator; + +public class ValidationService implements IValidation { + @Override + public boolean validate(String mobileNumber) { + final String MOBILE_NUMBER_LENGTH = "MOBILE_NUMBER_LENGTH"; + return mobileNumber.length() == Integer.parseInt(System.getenv().getOrDefault(MOBILE_NUMBER_LENGTH, "10")); + } +} diff --git a/deps/keycloak/Dockerfile b/deps/keycloak/Dockerfile index 8afca8c14..a0e91a3d4 100644 --- a/deps/keycloak/Dockerfile +++ b/deps/keycloak/Dockerfile @@ -1,6 +1,6 @@ -FROM jboss/keycloak:14.0.0 +FROM sunbirdrc/keycloak ADD themes /opt/jboss/keycloak/themes ADD providers /opt/jboss/keycloak/providers -ENTRYPOINT ["/opt/jboss/tools/docker-entrypoint.sh"] \ No newline at end of file +ENTRYPOINT ["/opt/jboss/tools/docker-entrypoint.sh"] diff --git a/deps/keycloak/Makefile b/deps/keycloak/Makefile index 467846712..defe22104 100644 --- a/deps/keycloak/Makefile +++ b/deps/keycloak/Makefile @@ -3,7 +3,7 @@ build: echo "Building docker image" @cd ../keycloak-mobile-number-login-spi/ && sh ./build.sh - @docker build -t dockerhub/sunbird-rc-keycloak . + @docker build -t ghcr.io/sunbird-rc/sunbird-rc-keycloak . push: - @docker push dockerhub/sunbird-rc-keycloak + @docker push ghcr.io/sunbird-rc/sunbird-rc-keycloak diff --git a/deps/keycloak/docker-compose.yml b/deps/keycloak/docker-compose.yml index c3664db9c..11f233bfd 100644 --- a/deps/keycloak/docker-compose.yml +++ b/deps/keycloak/docker-compose.yml @@ -8,7 +8,7 @@ services: - POSTGRES_USER=keycloak - POSTGRES_PASSWORD=keycloak keycloak: - image: dockerhub/keycloak + image: ghcr.io/sunbird-rc/keycloak # build: # . environment: diff --git a/deps/keycloak/themes/sunbird-rc/login/mobile-login.ftl b/deps/keycloak/themes/sunbird-rc/login/mobile-login.ftl index efc48a616..864d87c44 100644 --- a/deps/keycloak/themes/sunbird-rc/login/mobile-login.ftl +++ b/deps/keycloak/themes/sunbird-rc/login/mobile-login.ftl @@ -23,7 +23,7 @@