From 29e06d83c0aa183ea48dfb53bf38162d5d078ab4 Mon Sep 17 00:00:00 2001 From: Maciej Szlosarczyk Date: Thu, 12 Jul 2018 15:13:01 +0300 Subject: [PATCH 01/21] Add description for authentication endpoint --- doc/registrant-api.md | 24 ++++++ doc/registrant-api/authentication.md | 120 +++++++++++++++++++++++++++ 2 files changed, 144 insertions(+) create mode 100644 doc/registrant-api.md create mode 100644 doc/registrant-api/authentication.md diff --git a/doc/registrant-api.md b/doc/registrant-api.md new file mode 100644 index 000000000..c8dd852c2 --- /dev/null +++ b/doc/registrant-api.md @@ -0,0 +1,24 @@ +# Registrant API integration specification + +REPP uses HTTP/1.1 protocol (http://tools.ietf.org/html/rfc2616) and +Basic Authentication (http://tools.ietf.org/html/rfc2617#section-2) using +Secure Transport (https://tools.ietf.org/html/rfc5246) with certificate and key +(https://tools.ietf.org/html/rfc5280). + +Credentials and certificate are issued by EIS (in an exchange for desired API +username, CSR and IP). + +To quickly test the API, use curl: + + curl -q -k --cert user.crt.pem --key user.key.pem https://TBA/repp/v1/accounts/balance -u username:password + +Test API endpoint: https://testepp.internet.ee/repp/v1 +Production API endpoint: TBA + +Main communication specification through Restful EPP (REPP): + +[Contact related functions](repp/v1/contact.md) +[Domain related functions](repp/v1/domain.md) +[Domain transfers](repp/v1/domain_transfers.md) +[Account related functions](repp/v1/account.md) +[Nameservers](repp/v1/nameservers.md) diff --git a/doc/registrant-api/authentication.md b/doc/registrant-api/authentication.md new file mode 100644 index 000000000..5396d82b6 --- /dev/null +++ b/doc/registrant-api/authentication.md @@ -0,0 +1,120 @@ +# Authentication + +## Authenticating with mobileID or ID-card + +For specified partners the API allows for use of data from mobile ID for +authentication. API client should perform authentication with eID according to +the approriate documentation, and then pass on values from the webserver's +certificate to the API server. + +## POST /repp/v1/auth/eid/token + +Returns a bearer token to be used for further API requests. Tokens are valid for 2 hours since their creation. + +#### Paramaters + +Values in brackets represent values that come from the id card certificate. + +| Field name | Required | Type | Allowed values | Description | +| ----------------- | -------- | ---- | -------------- | ----------- | +| ident | true | String | | Identity code of the user (`serialNumber`) | +| first_name | true | String | | Name of the customer (`GN`) | +| last_name | true | String | | Name of the customer (`SN`) | +| country | true | String | 'ee' | Code of the country that issued the id card (`C`) | +| issuing authority | true | String | 'AS Sertifitseerimiskeskus' | | +| | | | | | + + +#### Request +``` +POST /repp/v1/auth/token HTTP/1.1 +Accept: application/json +Content-length: 0 +Content-type: application/json + +{ + "ident": "30110100103", + "first_name": "Jaan", + "last_name": "Tamm", + "country": "ee", + "issuing authority": "AS Sertifitseerimiskeskus" +} +``` + +#### Response +``` +HTTP/1.1 201 +Cache-Control: max-age=0, private, must-revalidate +Content-Length: 0 +Content-Type: application.json + + +{ + "access_token": "", + "expires_at": "2018-07-13 11:30:51 UTC", + "type": "Bearer" +} +``` + +## POST /repp/v1/auth/username/token -- NOT IMPLEMENTED + +#### Paramaters + +Values in brackets represent values that come from the id card certificate + +| Field name | Required | Type | Allowed values | Description | +| ----------------- | -------- | ---- | -------------- | ----------- | +| username | true | String | Username as provided by the user | | +| password | true | String | Password as provided by the user | | + + +#### Request +``` +POST /repp/v1/auth/token HTTP/1.1 +Accept: application/json +Content-length: 0 +Content-type: application/json +``` + +#### Response +``` +HTTP/1.1 201 +Cache-Control: max-age=0, private, must-revalidate +Content-Length: 0 +Content-Type: application.json + + +{ + "access_token": "", + "expires_at": "2018-07-13 11:30:51 UTC", + "type": "Bearer" +} +``` + +## Implementation notes: + +We do not need to store the session data at all, instead we can levarage AES encryption and use +Rails secret as the key. General approximation: + +```ruby +class AuthenticationToken + def initialize(secret = Rails.application.config.secret_key_base, values = {}) + end + + def create_token_hash + data = values.to_s + + cipher = OpenSSL::Cipher::AES.new(256, :CBC) + cipher.encrypt + + encrypted = cipher.update(data) + cipher.final + base64_encoded = Base64.encode64(encrypted) + + { + token: base64_encoded, + expires_in = values[:expires_in] + type: "Bearer" + } + end +end +``` From d49b4b5c0f622665577cef2100402cc40cc0fc9e Mon Sep 17 00:00:00 2001 From: Maciej Szlosarczyk Date: Fri, 13 Jul 2018 16:19:34 +0300 Subject: [PATCH 02/21] Add domain list documentation --- doc/registrant-api.md | 8 +- doc/registrant-api/{ => v1}/authentication.md | 2 +- doc/registrant-api/v1/domain.md | 102 ++++++++++++++++++ 3 files changed, 106 insertions(+), 6 deletions(-) rename doc/registrant-api/{ => v1}/authentication.md (98%) create mode 100644 doc/registrant-api/v1/domain.md diff --git a/doc/registrant-api.md b/doc/registrant-api.md index c8dd852c2..7ab295d38 100644 --- a/doc/registrant-api.md +++ b/doc/registrant-api.md @@ -17,8 +17,6 @@ Production API endpoint: TBA Main communication specification through Restful EPP (REPP): -[Contact related functions](repp/v1/contact.md) -[Domain related functions](repp/v1/domain.md) -[Domain transfers](repp/v1/domain_transfers.md) -[Account related functions](repp/v1/account.md) -[Nameservers](repp/v1/nameservers.md) +[Authentication](registrant-api/v1/authentication.md) +[Domain related functions](registrant-api/v1/domain.md) +[Contact related functions](registrant-api/v1/contact.md) diff --git a/doc/registrant-api/authentication.md b/doc/registrant-api/v1/authentication.md similarity index 98% rename from doc/registrant-api/authentication.md rename to doc/registrant-api/v1/authentication.md index 5396d82b6..f9486f57a 100644 --- a/doc/registrant-api/authentication.md +++ b/doc/registrant-api/v1/authentication.md @@ -7,7 +7,7 @@ authentication. API client should perform authentication with eID according to the approriate documentation, and then pass on values from the webserver's certificate to the API server. -## POST /repp/v1/auth/eid/token +## POST /repp/v1/registrant/auth/eid/token Returns a bearer token to be used for further API requests. Tokens are valid for 2 hours since their creation. diff --git a/doc/registrant-api/v1/domain.md b/doc/registrant-api/v1/domain.md new file mode 100644 index 000000000..b39f28cae --- /dev/null +++ b/doc/registrant-api/v1/domain.md @@ -0,0 +1,102 @@ +# Domain related actions + +## GET /repp/v1/registrant/domains +Returns domains of the current registrar. + + +#### Parameters + +| Field name | Required | Type | Allowed values | Description | +| ---------- | -------- | ---- | -------------- | ----------- | +| limit | false | Integer | [1..200] | How many domains to show | +| offset | false | Integer | | Domain number to start at | +| details | false | String | ["true", "false"] | Whether to include details | + +#### Request +``` +GET repp/v1/registrant/domains?limit=1&details=true HTTP/1.1 +Accept: application/json +Authorization: Bearer Z2l0bGFiOmdoeXQ5ZTRmdQ== +Content-Length: 0 +Content-Type: application/json +``` + +#### Response +``` +HTTP/1.1 200 +Cache-Control: max-age=0, private, must-revalidate +Content-Length: 808 +Content-Type: application/json + +{ + "domains": [ + { + "id": 1, + "name": "domain0.ee", + "registrar_id": 2, + "registered_at": "2015-09-09T09:11:14.861Z", + "status": null, + "valid_from": "2015-09-09T09:11:14.861Z", + "valid_to": "2016-09-09T09:11:14.861Z", + "registrant_id": 1, + "transfer_code": "98oiewslkfkd", + "created_at": "2015-09-09T09:11:14.861Z", + "updated_at": "2015-09-09T09:11:14.860Z", + "name_dirty": "domain0.ee", + "name_puny": "domain0.ee", + "period": 1, + "period_unit": "y", + "creator_str": null, + "updator_str": null, + "legacy_id": null, + "legacy_registrar_id": null, + "legacy_registrant_id": null, + "outzone_at": "2016-09-24T09:11:14.861Z", + "delete_at": "2016-10-24T09:11:14.861Z", + "registrant_verification_asked_at": null, + "registrant_verification_token": null, + "pending_json": { + }, + "force_delete_at": null, + "statuses": [ + "ok" + ], + "reserved": false, + "status_notes": { + }, + "statuses_backup": [ + + ] + } + ], + "total_number_of_records": 2 +} +``` + +## GET repp/v1/registrant/domains +Returns domain names with offset. + + +#### Request +``` +GET repp/v1/registrant/domains?offset=1 HTTP/1.1 +Accept: application/json +Authorization: Bearer Z2l0bGFiOmdoeXQ5ZTRmdQ== +Content-Length: 0 +Content-Type: application/json +``` + +#### Response +``` +HTTP/1.1 200 +Cache-Control: max-age=0, private, must-revalidate +Content-Length: 54 +Content-Type: application/json + +{ + "domains": [ + "domain1.ee" + ], + "total_number_of_records": 2 +} +``` From f965878b42a327904d1e76143419abd9c85c34a4 Mon Sep 17 00:00:00 2001 From: Maciej Szlosarczyk Date: Mon, 16 Jul 2018 10:28:17 +0300 Subject: [PATCH 03/21] Add contact endpoint documentation --- doc/registrant-api/v1/authentication.md | 6 +- doc/registrant-api/v1/contact.md | 148 ++++++++++++++++++++++++ doc/registrant-api/v1/domain.md | 8 +- 3 files changed, 157 insertions(+), 5 deletions(-) create mode 100644 doc/registrant-api/v1/contact.md diff --git a/doc/registrant-api/v1/authentication.md b/doc/registrant-api/v1/authentication.md index f9486f57a..f914a7a23 100644 --- a/doc/registrant-api/v1/authentication.md +++ b/doc/registrant-api/v1/authentication.md @@ -7,7 +7,7 @@ authentication. API client should perform authentication with eID according to the approriate documentation, and then pass on values from the webserver's certificate to the API server. -## POST /repp/v1/registrant/auth/eid/token +## POST /repp/v1/registrant/auth/eid Returns a bearer token to be used for further API requests. Tokens are valid for 2 hours since their creation. @@ -34,7 +34,7 @@ Content-type: application/json { "ident": "30110100103", - "first_name": "Jaan", + "first_name": "Jan", "last_name": "Tamm", "country": "ee", "issuing authority": "AS Sertifitseerimiskeskus" @@ -56,7 +56,7 @@ Content-Type: application.json } ``` -## POST /repp/v1/auth/username/token -- NOT IMPLEMENTED +## POST /repp/v1/auth/username -- NOT IMPLEMENTED #### Paramaters diff --git a/doc/registrant-api/v1/contact.md b/doc/registrant-api/v1/contact.md new file mode 100644 index 000000000..126ae27c6 --- /dev/null +++ b/doc/registrant-api/v1/contact.md @@ -0,0 +1,148 @@ +## GET /repp/v1/registrant/contacts +Returns contacts of the current registrar. + + +#### Parameters + +| Field name | Required | Type | Allowed values | Description | +| ---------- | -------- | ---- | -------------- | ----------- | +| limit | false | Integer | [1..200] | How many contacts to show | +| offset | false | Integer | | Contact number to start at | + +#### Request +``` +GET /repp/v1/registrant/contacts?limit=1 HTTP/1.1 +Accept: application/json +Authorization: Bearer Z2l0bGFiOmdoeXQ5ZTRmdQ== +Content-Type: application/json +``` + +#### Response +``` +HTTP/1.1 200 +Cache-Control: max-age=0, private, must-revalidate +Content-Length: 564 +Content-Type: application/json + +{ + "contacts": [ + { + "uuid": "84c62f3d-e56f-40fa-9ca4-dc0137778949", + "domain_name": "example.com" + "code": "REGISTRAR2:SH022086480", + "phone": "+372.12345678", + "email": "hoyt@deckowbechtelar.net", + "fax": null, + "created_at": "2015-09-09T09:11:14.130Z", + "updated_at": "2015-09-09T09:11:14.130Z", + "ident": "37605030299", + "ident_type": "priv", + "auth_info": "password", + "name": "Karson Kessler0", + "org_name": null, + "registrar_id": 2, + "creator_str": null, + "updator_str": null, + "ident_country_code": "EE", + "city": "Tallinn", + "street": "Short street 11", + "zip": "11111", + "country_code": "EE", + "state": null, + "legacy_id": null, + "statuses": [ + "ok" + ], + "status_notes": { + } + } + ], + "total_number_of_records": 2 +} +``` + +## PUT/PATCH /rep/v1/registrant/contacts/$UUID + +Update contact details for a contact. + +#### Parameters + +| Field name | Required | Type | Allowed values | Description | +| ---- | --- | --- | --- | --- | +| email | false | String | | New email address | +| phone | false | String | | New phone number | +| fax | false | String | | New fax number | +| city | false | String | | New city name | +| street | false | String | | New street name | +| zip | false | String | | New zip code | +| country_code | false | String | | New country code in 2 letter format ('EE', 'LV') | +| state | false | String | | New state name | + + +#### Request +``` +PUT /repp/v1/registrant/contacts/84c62f3d-e56f-40fa-9ca4-dc0137778949 HTTP/1.1 +Authorization: Bearer Z2l0bGFiOmdoeXQ5ZTRmdQ== +Accept: application/json +Content-type: application/json + +{ + "email": "foo@bar.baz", + "phone": "+372.12345671", + "fax": "+372.12345672", + "city": "New City", + "street": "Main Street 123", + "zip": "22222", + "country_code": "LV", + "state": "New state" +} + +``` +#### Response on success + +``` +HTTP/1.1 200 +Content-Type: application.json + +{ + "uuid": "84c62f3d-e56f-40fa-9ca4-dc0137778949", + "domain_name": "example.com" + "code": "REGISTRAR2:SH022086480", + "phone": "+372.12345671", + "email": "foo@bar.baz", + "fax": "+372.12345672", + "created_at": "2015-09-09T09:11:14.130Z", + "updated_at": "2018-09-09T09:11:14.130Z", + "ident": "37605030299", + "ident_type": "priv", + "auth_info": "password", + "name": "Karson Kessler0", + "org_name": null, + "registrar_id": 2, + "creator_str": null, + "updator_str": null, + "ident_country_code": "EE", + "city": "New City", + "street": "Main Street 123", + "zip": "22222", + "country_code": "LV", + "state": "New state" + "legacy_id": null, + "statuses": [ + "ok" + ], + "status_notes": {} +} +``` + +### Response on failure +``` +HTTP/1.1 400 +Content-Type: application.json + +{ + "errors": [ + { "phone": "Phone nr is invalid" } + ] +} +``` diff --git a/doc/registrant-api/v1/domain.md b/doc/registrant-api/v1/domain.md index b39f28cae..5ea82a376 100644 --- a/doc/registrant-api/v1/domain.md +++ b/doc/registrant-api/v1/domain.md @@ -1,7 +1,7 @@ # Domain related actions ## GET /repp/v1/registrant/domains -Returns domains of the current registrar. +Returns domains of the current registrant. #### Parameters @@ -65,7 +65,6 @@ Content-Type: application/json "status_notes": { }, "statuses_backup": [ - ] } ], @@ -100,3 +99,8 @@ Content-Type: application/json "total_number_of_records": 2 } ``` + +#### Implementation details + +This endpoint is practically a copy-paste from similar endpoint used by +registrars. From 2e2e5cd08deb698b9c0cc130d74c531969bcc0d0 Mon Sep 17 00:00:00 2001 From: Maciej Szlosarczyk Date: Mon, 16 Jul 2018 13:32:40 +0300 Subject: [PATCH 04/21] Add GET contact info endpoint --- doc/registrant-api/v1/contact.md | 52 +++++++++++++++++++++++++++++++- doc/registrant-api/v1/domain.md | 7 +---- 2 files changed, 52 insertions(+), 7 deletions(-) diff --git a/doc/registrant-api/v1/contact.md b/doc/registrant-api/v1/contact.md index 126ae27c6..127c9fdb4 100644 --- a/doc/registrant-api/v1/contact.md +++ b/doc/registrant-api/v1/contact.md @@ -61,7 +61,57 @@ Content-Type: application/json } ``` -## PUT/PATCH /rep/v1/registrant/contacts/$UUID +## GET /repp/v1/registrant/contacts/$UUID +Returns contacts of the current registrar. + + +#### Request +``` +GET /repp/v1/registrant/contacts/84c62f3d-e56f-40fa-9ca4-dc0137778949 HTTP/1.1 +Accept: application/json +Authorization: Bearer Z2l0bGFiOmdoeXQ5ZTRmdQ== +Content-Type: application/json +``` + +#### Response +``` +HTTP/1.1 200 +Cache-Control: max-age=0, private, must-revalidate +Content-Length: 564 +Content-Type: application/json + +{ + "uuid": "84c62f3d-e56f-40fa-9ca4-dc0137778949", + "domain_name": "example.com" + "code": "REGISTRAR2:SH022086480", + "phone": "+372.12345678", + "email": "hoyt@deckowbechtelar.net", + "fax": null, + "created_at": "2015-09-09T09:11:14.130Z", + "updated_at": "2015-09-09T09:11:14.130Z", + "ident": "37605030299", + "ident_type": "priv", + "auth_info": "password", + "name": "Karson Kessler0", + "org_name": null, + "registrar_id": 2, + "creator_str": null, + "updator_str": null, + "ident_country_code": "EE", + "city": "Tallinn", + "street": "Short street 11", + "zip": "11111", + "country_code": "EE", + "state": null, + "legacy_id": null, + "statuses": [ + "ok" + ], + "status_notes": {} +} +``` + +## PUT/PATCH /repp/v1/registrant/contacts/$UUID Update contact details for a contact. diff --git a/doc/registrant-api/v1/domain.md b/doc/registrant-api/v1/domain.md index 5ea82a376..db9b17978 100644 --- a/doc/registrant-api/v1/domain.md +++ b/doc/registrant-api/v1/domain.md @@ -31,7 +31,7 @@ Content-Type: application/json { "domains": [ { - "id": 1, + "uuid": "98d1083a-8863-4153-93e4-caee4a013535", "name": "domain0.ee", "registrar_id": 2, "registered_at": "2015-09-09T09:11:14.861Z", @@ -99,8 +99,3 @@ Content-Type: application/json "total_number_of_records": 2 } ``` - -#### Implementation details - -This endpoint is practically a copy-paste from similar endpoint used by -registrars. From 6aeda6444e9efcaecfc14a62347ae557f570bc23 Mon Sep 17 00:00:00 2001 From: Maciej Szlosarczyk Date: Mon, 16 Jul 2018 13:47:55 +0300 Subject: [PATCH 05/21] Change `domain_names` to be an array, not a singular object --- doc/registrant-api/v1/contact.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/registrant-api/v1/contact.md b/doc/registrant-api/v1/contact.md index 127c9fdb4..32d194a75 100644 --- a/doc/registrant-api/v1/contact.md +++ b/doc/registrant-api/v1/contact.md @@ -28,7 +28,7 @@ Content-Type: application/json "contacts": [ { "uuid": "84c62f3d-e56f-40fa-9ca4-dc0137778949", - "domain_name": "example.com" + "domain_names": ["example.com"], "code": "REGISTRAR2:SH022086480", "phone": "+372.12345678", "email": "hoyt@deckowbechtelar.net", @@ -82,7 +82,7 @@ Content-Type: application/json { "uuid": "84c62f3d-e56f-40fa-9ca4-dc0137778949", - "domain_name": "example.com" + "domain_names": ["example.com"], "code": "REGISTRAR2:SH022086480", "phone": "+372.12345678", "email": "hoyt@deckowbechtelar.net", @@ -156,7 +156,7 @@ Content-Type: application.json { "uuid": "84c62f3d-e56f-40fa-9ca4-dc0137778949", - "domain_name": "example.com" + "domain_names": ["example.com"], "code": "REGISTRAR2:SH022086480", "phone": "+372.12345671", "email": "foo@bar.baz", From e2d2768b6e35de19ed96e45f61eb5f8c693888ee Mon Sep 17 00:00:00 2001 From: Maciej Szlosarczyk Date: Mon, 16 Jul 2018 14:26:59 +0300 Subject: [PATCH 06/21] Update domain endpoint --- doc/registrant-api/v1/domain.md | 68 +++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 4 deletions(-) diff --git a/doc/registrant-api/v1/domain.md b/doc/registrant-api/v1/domain.md index db9b17978..bfc4349eb 100644 --- a/doc/registrant-api/v1/domain.md +++ b/doc/registrant-api/v1/domain.md @@ -17,7 +17,6 @@ Returns domains of the current registrant. GET repp/v1/registrant/domains?limit=1&details=true HTTP/1.1 Accept: application/json Authorization: Bearer Z2l0bGFiOmdoeXQ5ZTRmdQ== -Content-Length: 0 Content-Type: application/json ``` @@ -81,15 +80,12 @@ Returns domain names with offset. GET repp/v1/registrant/domains?offset=1 HTTP/1.1 Accept: application/json Authorization: Bearer Z2l0bGFiOmdoeXQ5ZTRmdQ== -Content-Length: 0 Content-Type: application/json ``` #### Response ``` HTTP/1.1 200 -Cache-Control: max-age=0, private, must-revalidate -Content-Length: 54 Content-Type: application/json { @@ -99,3 +95,67 @@ Content-Type: application/json "total_number_of_records": 2 } ``` + +## GET repp/v1/registrant/domains/$UUID +Returns domain names with offset. + + +#### Request +``` +GET repp/v1/registrant/domains/98d1083a-8863-4153-93e4-caee4a013535 HTTP/1.1 +Accept: application/json +Authorization: Bearer Z2l0bGFiOmdoeXQ5ZTRmdQ== +Content-Length: 0 +Content-Type: application/json +``` + +#### Response for success + +``` +HTTP/1.1 200 +Content-Type: application/json + +{ + "uuid": "98d1083a-8863-4153-93e4-caee4a013535", + "name": "domain0.ee", + "registrar_id": 2, + "registered_at": "2015-09-09T09:11:14.861Z", + "status": null, + "valid_from": "2015-09-09T09:11:14.861Z", + "valid_to": "2016-09-09T09:11:14.861Z", + "registrant_id": 1, + "transfer_code": "98oiewslkfkd", + "created_at": "2015-09-09T09:11:14.861Z", + "updated_at": "2015-09-09T09:11:14.860Z", + "name_dirty": "domain0.ee", + "name_puny": "domain0.ee", + "period": 1, + "period_unit": "y", + "creator_str": null, + "updator_str": null, + "legacy_id": null, + "legacy_registrar_id": null, + "legacy_registrant_id": null, + "outzone_at": "2016-09-24T09:11:14.861Z", + "delete_at": "2016-10-24T09:11:14.861Z", + "registrant_verification_asked_at": null, + "registrant_verification_token": null, + "pending_json": {}, + "force_delete_at": null, + "statuses": [ + "ok" + ], + "reserved": false, + "status_notes": {}, + "statuses_backup": [] +} +``` + +#### Response for failure + +``` +HTTP/1.1 404 +Content-Type: application/json + +{ "error": "Domain not found" } +``` From 270444b2e8be80dd81b2781890c1715f8d2c75f8 Mon Sep 17 00:00:00 2001 From: Maciej Szlosarczyk Date: Mon, 16 Jul 2018 16:03:51 +0300 Subject: [PATCH 07/21] Add registry lock documentation --- doc/registrant-api.md | 21 +--- doc/registrant-api/v1/domain.md | 5 +- doc/registrant-api/v1/domain_lock.md | 164 +++++++++++++++++++++++++++ 3 files changed, 173 insertions(+), 17 deletions(-) create mode 100644 doc/registrant-api/v1/domain_lock.md diff --git a/doc/registrant-api.md b/doc/registrant-api.md index 7ab295d38..c6f063a91 100644 --- a/doc/registrant-api.md +++ b/doc/registrant-api.md @@ -1,22 +1,11 @@ # Registrant API integration specification -REPP uses HTTP/1.1 protocol (http://tools.ietf.org/html/rfc2616) and -Basic Authentication (http://tools.ietf.org/html/rfc2617#section-2) using -Secure Transport (https://tools.ietf.org/html/rfc5246) with certificate and key -(https://tools.ietf.org/html/rfc5280). - -Credentials and certificate are issued by EIS (in an exchange for desired API -username, CSR and IP). - -To quickly test the API, use curl: - - curl -q -k --cert user.crt.pem --key user.key.pem https://TBA/repp/v1/accounts/balance -u username:password - -Test API endpoint: https://testepp.internet.ee/repp/v1 +Test API endpoint: TBA Production API endpoint: TBA -Main communication specification through Restful EPP (REPP): +Main communication specification through Registrant API: [Authentication](registrant-api/v1/authentication.md) -[Domain related functions](registrant-api/v1/domain.md) -[Contact related functions](registrant-api/v1/contact.md) +[Domains](registrant-api/v1/domain.md) +[Domain Lock](registrant-api/v1/domain_lock.md) +[Contacts](registrant-api/v1/contact.md) diff --git a/doc/registrant-api/v1/domain.md b/doc/registrant-api/v1/domain.md index bfc4349eb..f4b5b2895 100644 --- a/doc/registrant-api/v1/domain.md +++ b/doc/registrant-api/v1/domain.md @@ -1,6 +1,7 @@ # Domain related actions ## GET /repp/v1/registrant/domains + Returns domains of the current registrant. @@ -72,6 +73,7 @@ Content-Type: application/json ``` ## GET repp/v1/registrant/domains + Returns domain names with offset. @@ -97,7 +99,8 @@ Content-Type: application/json ``` ## GET repp/v1/registrant/domains/$UUID -Returns domain names with offset. + +Returns a single domain object. #### Request diff --git a/doc/registrant-api/v1/domain_lock.md b/doc/registrant-api/v1/domain_lock.md new file mode 100644 index 000000000..7dcd6efa4 --- /dev/null +++ b/doc/registrant-api/v1/domain_lock.md @@ -0,0 +1,164 @@ +# Domain locks + +## POST repp/v1/registrant/domains/$UUID/registry_lock + +Set a registry lock on a domain. + +#### Request +``` +POST repp/v1/registrant/domains/98d1083a-8863-4153-93e4-caee4a013535/registry_lock HTTP/1.1 +Accept: application/json +Authorization: Bearer Z2l0bGFiOmdoeXQ5ZTRmdQ== +Content-Type: application/json +``` + +#### Response for success + +``` +HTTP/1.1 200 +Content-Type: application/json + +{ + "uuid": "98d1083a-8863-4153-93e4-caee4a013535", + "name": "domain0.ee", + "registrar_id": 2, + "registered_at": "2015-09-09T09:11:14.861Z", + "status": null, + "valid_from": "2015-09-09T09:11:14.861Z", + "valid_to": "2016-09-09T09:11:14.861Z", + "registrant_id": 1, + "transfer_code": "98oiewslkfkd", + "created_at": "2015-09-09T09:11:14.861Z", + "updated_at": "2015-09-09T09:11:14.860Z", + "name_dirty": "domain0.ee", + "name_puny": "domain0.ee", + "period": 1, + "period_unit": "y", + "creator_str": null, + "updator_str": null, + "legacy_id": null, + "legacy_registrar_id": null, + "legacy_registrant_id": null, + "outzone_at": "2016-09-24T09:11:14.861Z", + "delete_at": "2016-10-24T09:11:14.861Z", + "registrant_verification_asked_at": null, + "registrant_verification_token": null, + "pending_json": {}, + "force_delete_at": null, + "statuses": [ + "clientUpdateProhibited", + "serverUpdateProhibited", + "serverTransferProhibited", + "serverDeleteProhibited", + ], + "reserved": false, + "status_notes": {}, + "statuses_backup": [] +} +``` + +#### Response for failure + +``` +HTTP/1.1 400 +Content-Type: application/json + +{ + "errors": [ + { "base": "domain cannot be locked" } + ] +} + +``` + +``` +HTTP/1.1 404 +Content-Type: application/json + +{ + "errors": [ + { "base": "domain does not exist" } + ] +} + +``` + +## DELETE repp/v1/registrant/domains/$UUID/registry_lock + +Remove a registry lock. + +#### Request +``` +DELETE repp/v1/registrant/domains/98d1083a-8863-4153-93e4-caee4a013535/registry_lock HTTP/1.1 +Accept: application/json +Authorization: Bearer Z2l0bGFiOmdoeXQ5ZTRmdQ== +Content-Type: application/json +``` + +#### Response for success + +``` +HTTP/1.1 200 +Content-Type: application/json + +{ + "uuid": "98d1083a-8863-4153-93e4-caee4a013535", + "name": "domain0.ee", + "registrar_id": 2, + "registered_at": "2015-09-09T09:11:14.861Z", + "status": null, + "valid_from": "2015-09-09T09:11:14.861Z", + "valid_to": "2016-09-09T09:11:14.861Z", + "registrant_id": 1, + "transfer_code": "98oiewslkfkd", + "created_at": "2015-09-09T09:11:14.861Z", + "updated_at": "2015-09-09T09:11:14.860Z", + "name_dirty": "domain0.ee", + "name_puny": "domain0.ee", + "period": 1, + "period_unit": "y", + "creator_str": null, + "updator_str": null, + "legacy_id": null, + "legacy_registrar_id": null, + "legacy_registrant_id": null, + "outzone_at": "2016-09-24T09:11:14.861Z", + "delete_at": "2016-10-24T09:11:14.861Z", + "registrant_verification_asked_at": null, + "registrant_verification_token": null, + "pending_json": {}, + "force_delete_at": null, + "statuses": [ + "ok" + ], + "reserved": false, + "status_notes": {}, + "statuses_backup": [] +} +``` + +#### Response for failure + +``` +HTTP/1.1 400 +Content-Type: application/json + +{ + "errors": [ + { "base": "domain cannot be unlocked" } + ] +} + +``` + +``` +HTTP/1.1 404 +Content-Type: application/json + +{ + "errors": [ + { "base": "domain does not exist" } + ] +} + +``` From e42951bec881fb2a5bb47c0347e6fcc9bc0020fc Mon Sep 17 00:00:00 2001 From: Maciej Szlosarczyk Date: Tue, 17 Jul 2018 12:46:21 +0300 Subject: [PATCH 08/21] Update statuses for registry lock --- doc/registrant-api/v1/domain_lock.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/doc/registrant-api/v1/domain_lock.md b/doc/registrant-api/v1/domain_lock.md index 7dcd6efa4..6702d4233 100644 --- a/doc/registrant-api/v1/domain_lock.md +++ b/doc/registrant-api/v1/domain_lock.md @@ -47,9 +47,8 @@ Content-Type: application/json "force_delete_at": null, "statuses": [ "clientUpdateProhibited", - "serverUpdateProhibited", - "serverTransferProhibited", - "serverDeleteProhibited", + "clientDeleteProhibited", + "clientTransferProhibited" ], "reserved": false, "status_notes": {}, From d71726c55d1ee23741272de63a273704ea63a04a Mon Sep 17 00:00:00 2001 From: Maciej Szlosarczyk Date: Tue, 17 Jul 2018 15:04:55 +0300 Subject: [PATCH 09/21] Remove Content-Length header from examples --- doc/registrant-api/v1/authentication.md | 4 ---- doc/registrant-api/v1/contact.md | 2 -- doc/registrant-api/v1/domain.md | 2 -- 3 files changed, 8 deletions(-) diff --git a/doc/registrant-api/v1/authentication.md b/doc/registrant-api/v1/authentication.md index f914a7a23..b3b926a67 100644 --- a/doc/registrant-api/v1/authentication.md +++ b/doc/registrant-api/v1/authentication.md @@ -29,7 +29,6 @@ Values in brackets represent values that come from the id card certificate. ``` POST /repp/v1/auth/token HTTP/1.1 Accept: application/json -Content-length: 0 Content-type: application/json { @@ -45,7 +44,6 @@ Content-type: application/json ``` HTTP/1.1 201 Cache-Control: max-age=0, private, must-revalidate -Content-Length: 0 Content-Type: application.json @@ -72,7 +70,6 @@ Values in brackets represent values that come from the id card certificate ``` POST /repp/v1/auth/token HTTP/1.1 Accept: application/json -Content-length: 0 Content-type: application/json ``` @@ -80,7 +77,6 @@ Content-type: application/json ``` HTTP/1.1 201 Cache-Control: max-age=0, private, must-revalidate -Content-Length: 0 Content-Type: application.json diff --git a/doc/registrant-api/v1/contact.md b/doc/registrant-api/v1/contact.md index 32d194a75..8ce129cdb 100644 --- a/doc/registrant-api/v1/contact.md +++ b/doc/registrant-api/v1/contact.md @@ -21,7 +21,6 @@ Content-Type: application/json ``` HTTP/1.1 200 Cache-Control: max-age=0, private, must-revalidate -Content-Length: 564 Content-Type: application/json { @@ -77,7 +76,6 @@ Content-Type: application/json ``` HTTP/1.1 200 Cache-Control: max-age=0, private, must-revalidate -Content-Length: 564 Content-Type: application/json { diff --git a/doc/registrant-api/v1/domain.md b/doc/registrant-api/v1/domain.md index f4b5b2895..49953f4a1 100644 --- a/doc/registrant-api/v1/domain.md +++ b/doc/registrant-api/v1/domain.md @@ -25,7 +25,6 @@ Content-Type: application/json ``` HTTP/1.1 200 Cache-Control: max-age=0, private, must-revalidate -Content-Length: 808 Content-Type: application/json { @@ -108,7 +107,6 @@ Returns a single domain object. GET repp/v1/registrant/domains/98d1083a-8863-4153-93e4-caee4a013535 HTTP/1.1 Accept: application/json Authorization: Bearer Z2l0bGFiOmdoeXQ5ZTRmdQ== -Content-Length: 0 Content-Type: application/json ``` From aa87d001d562ebf84ca0943653ac533a84ee69f5 Mon Sep 17 00:00:00 2001 From: Maciej Szlosarczyk Date: Tue, 17 Jul 2018 15:07:32 +0300 Subject: [PATCH 10/21] Remove Cache-Control headers from examples --- doc/registrant-api/v1/authentication.md | 2 -- doc/registrant-api/v1/contact.md | 2 -- doc/registrant-api/v1/domain.md | 1 - 3 files changed, 5 deletions(-) diff --git a/doc/registrant-api/v1/authentication.md b/doc/registrant-api/v1/authentication.md index b3b926a67..1c65691d2 100644 --- a/doc/registrant-api/v1/authentication.md +++ b/doc/registrant-api/v1/authentication.md @@ -43,7 +43,6 @@ Content-type: application/json #### Response ``` HTTP/1.1 201 -Cache-Control: max-age=0, private, must-revalidate Content-Type: application.json @@ -76,7 +75,6 @@ Content-type: application/json #### Response ``` HTTP/1.1 201 -Cache-Control: max-age=0, private, must-revalidate Content-Type: application.json diff --git a/doc/registrant-api/v1/contact.md b/doc/registrant-api/v1/contact.md index 8ce129cdb..ea28294d0 100644 --- a/doc/registrant-api/v1/contact.md +++ b/doc/registrant-api/v1/contact.md @@ -20,7 +20,6 @@ Content-Type: application/json #### Response ``` HTTP/1.1 200 -Cache-Control: max-age=0, private, must-revalidate Content-Type: application/json { @@ -75,7 +74,6 @@ Content-Type: application/json #### Response ``` HTTP/1.1 200 -Cache-Control: max-age=0, private, must-revalidate Content-Type: application/json { diff --git a/doc/registrant-api/v1/domain.md b/doc/registrant-api/v1/domain.md index 49953f4a1..80290fbac 100644 --- a/doc/registrant-api/v1/domain.md +++ b/doc/registrant-api/v1/domain.md @@ -24,7 +24,6 @@ Content-Type: application/json #### Response ``` HTTP/1.1 200 -Cache-Control: max-age=0, private, must-revalidate Content-Type: application/json { From e1605f81eb75278a91ffe16df9bbf8003cd872ab Mon Sep 17 00:00:00 2001 From: Maciej Szlosarczyk Date: Wed, 18 Jul 2018 14:34:07 +0300 Subject: [PATCH 11/21] Fix error in authentication field name --- doc/registrant-api/v1/authentication.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/registrant-api/v1/authentication.md b/doc/registrant-api/v1/authentication.md index 1c65691d2..2b25a62c2 100644 --- a/doc/registrant-api/v1/authentication.md +++ b/doc/registrant-api/v1/authentication.md @@ -21,7 +21,7 @@ Values in brackets represent values that come from the id card certificate. | first_name | true | String | | Name of the customer (`GN`) | | last_name | true | String | | Name of the customer (`SN`) | | country | true | String | 'ee' | Code of the country that issued the id card (`C`) | -| issuing authority | true | String | 'AS Sertifitseerimiskeskus' | | +| issuing_authority | true | String | 'AS Sertifitseerimiskeskus' | | | | | | | | @@ -36,7 +36,7 @@ Content-type: application/json "first_name": "Jan", "last_name": "Tamm", "country": "ee", - "issuing authority": "AS Sertifitseerimiskeskus" + "issuing_authority": "AS Sertifitseerimiskeskus" } ``` From 74c97eb9238c7c187495f39ea68d12e4049d37ee Mon Sep 17 00:00:00 2001 From: Maciej Szlosarczyk Date: Wed, 18 Jul 2018 15:21:53 +0300 Subject: [PATCH 12/21] Remove issuing authority as a parameter from authentication --- doc/registrant-api/v1/authentication.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/doc/registrant-api/v1/authentication.md b/doc/registrant-api/v1/authentication.md index 2b25a62c2..13638fca7 100644 --- a/doc/registrant-api/v1/authentication.md +++ b/doc/registrant-api/v1/authentication.md @@ -21,8 +21,6 @@ Values in brackets represent values that come from the id card certificate. | first_name | true | String | | Name of the customer (`GN`) | | last_name | true | String | | Name of the customer (`SN`) | | country | true | String | 'ee' | Code of the country that issued the id card (`C`) | -| issuing_authority | true | String | 'AS Sertifitseerimiskeskus' | | -| | | | | | #### Request @@ -36,7 +34,6 @@ Content-type: application/json "first_name": "Jan", "last_name": "Tamm", "country": "ee", - "issuing_authority": "AS Sertifitseerimiskeskus" } ``` From 1b9a504fb5c4ece69bd4a181d9c99229987d0ead Mon Sep 17 00:00:00 2001 From: Maciej Szlosarczyk Date: Wed, 18 Jul 2018 21:22:13 +0300 Subject: [PATCH 13/21] Remove country as identification parameter, restore server statuses --- doc/registrant-api/v1/authentication.md | 12 +++++------- doc/registrant-api/v1/domain_lock.md | 6 +++--- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/doc/registrant-api/v1/authentication.md b/doc/registrant-api/v1/authentication.md index 13638fca7..558bb1880 100644 --- a/doc/registrant-api/v1/authentication.md +++ b/doc/registrant-api/v1/authentication.md @@ -15,12 +15,11 @@ Returns a bearer token to be used for further API requests. Tokens are valid for Values in brackets represent values that come from the id card certificate. -| Field name | Required | Type | Allowed values | Description | -| ----------------- | -------- | ---- | -------------- | ----------- | -| ident | true | String | | Identity code of the user (`serialNumber`) | -| first_name | true | String | | Name of the customer (`GN`) | -| last_name | true | String | | Name of the customer (`SN`) | -| country | true | String | 'ee' | Code of the country that issued the id card (`C`) | +| Field name | Required | Type | Allowed values | Description | +| ----------------- | -------- | ---- | -------------- | ----------- | +| ident | true | String | | Identity code of the user (`serialNumber`) | +| first_name | true | String | | Name of the customer (`GN`) | +| last_name | true | String | | Name of the customer (`SN`) | #### Request @@ -33,7 +32,6 @@ Content-type: application/json "ident": "30110100103", "first_name": "Jan", "last_name": "Tamm", - "country": "ee", } ``` diff --git a/doc/registrant-api/v1/domain_lock.md b/doc/registrant-api/v1/domain_lock.md index 6702d4233..0237a11cb 100644 --- a/doc/registrant-api/v1/domain_lock.md +++ b/doc/registrant-api/v1/domain_lock.md @@ -46,9 +46,9 @@ Content-Type: application/json "pending_json": {}, "force_delete_at": null, "statuses": [ - "clientUpdateProhibited", - "clientDeleteProhibited", - "clientTransferProhibited" + "serverUpdateProhibited", + "serverDeleteProhibited", + "serverTransferProhibited" ], "reserved": false, "status_notes": {}, From e75d962d39d6b5fc7a615168033405556d59b00d Mon Sep 17 00:00:00 2001 From: Maciej Szlosarczyk Date: Fri, 27 Jul 2018 11:19:18 +0300 Subject: [PATCH 14/21] Remove inconsistent usage of error/errors --- doc/registrant-api/v1/domain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/registrant-api/v1/domain.md b/doc/registrant-api/v1/domain.md index 80290fbac..2e249423c 100644 --- a/doc/registrant-api/v1/domain.md +++ b/doc/registrant-api/v1/domain.md @@ -157,5 +157,5 @@ Content-Type: application/json HTTP/1.1 404 Content-Type: application/json -{ "error": "Domain not found" } +{ "errors": ["Domain not found"] } ``` From 3673c69319b87c82bc8b279583f3d463f347c50b Mon Sep 17 00:00:00 2001 From: Maciej Szlosarczyk Date: Wed, 1 Aug 2018 14:57:41 +0300 Subject: [PATCH 15/21] Add Registrant/Contacts endpoint --- .../api/v1/registrant/contacts_controller.rb | 47 +++++++++++ config/routes.rb | 1 + .../registrant_api_contacts_test.rb | 79 +++++++++++++++++++ 3 files changed, 127 insertions(+) create mode 100644 app/controllers/api/v1/registrant/contacts_controller.rb create mode 100644 test/integration/api/registrant/registrant_api_contacts_test.rb diff --git a/app/controllers/api/v1/registrant/contacts_controller.rb b/app/controllers/api/v1/registrant/contacts_controller.rb new file mode 100644 index 000000000..31d56885b --- /dev/null +++ b/app/controllers/api/v1/registrant/contacts_controller.rb @@ -0,0 +1,47 @@ +require 'rails5_api_controller_backport' +require 'auth_token/auth_token_decryptor' + +module Api + module V1 + module Registrant + class ContactsController < BaseController + before_action :set_contacts_pool + + def index + limit = params[:limit] || 200 + offset = params[:offset] || 0 + + if limit.to_i > 200 || limit.to_i < 1 + render(json: { errors: [{ limit: ['parameter is out of range'] }] }, + status: :bad_request) && return + end + + if offset.to_i.negative? + render(json: { errors: [{ offset: ['parameter is out of range'] }] }, + status: :bad_request) && return + end + + @contacts = @contacts_pool.limit(limit).offset(offset) + render json: @contacts + end + + def show + @contact = @contacts_pool.find_by(uuid: params[:uuid]) + + if @contact + render json: @contact + else + render json: { errors: ['Contact not found'] }, status: :not_found + end + end + + private + + def set_contacts_pool + country_code, ident = current_user.registrant_ident.to_s.split '-' + @contacts_pool = Contact.where(country_code: country_code, ident: ident) + end + end + end + end +end diff --git a/config/routes.rb b/config/routes.rb index 3ae18a7cd..9229eb1b2 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -24,6 +24,7 @@ Rails.application.routes.draw do post 'auth/eid', to: 'auth#eid' resources :domains, only: [:index] + resources :contacts, only: %i[index show] end end end diff --git a/test/integration/api/registrant/registrant_api_contacts_test.rb b/test/integration/api/registrant/registrant_api_contacts_test.rb new file mode 100644 index 000000000..a3367dd58 --- /dev/null +++ b/test/integration/api/registrant/registrant_api_contacts_test.rb @@ -0,0 +1,79 @@ +require 'test_helper' +require 'auth_token/auth_token_creator' + +class RegistrantApiContactsTest < ActionDispatch::IntegrationTest + def setup + super + + @user = users(:registrant) + @auth_headers = { 'HTTP_AUTHORIZATION' => auth_token } + end + + def test_root_returns_domain_list + get '/api/v1/registrant/contacts', {}, @auth_headers + assert_equal(200, response.status) + + json_body = JSON.parse(response.body, symbolize_names: true) + assert_equal(2, json_body.count) + array_of_contact_codes = json_body.map { |x| x[:code] } + assert(array_of_contact_codes.include?('william-001')) + assert(array_of_contact_codes.include?('william-002')) + end + + def test_root_accepts_limit_and_offset_parameters + get '/api/v1/registrant/contacts', { 'limit' => 1, 'offset' => 0 }, @auth_headers + response_json = JSON.parse(response.body, symbolize_names: true) + assert_equal(200, response.status) + assert_equal(1, response_json.count) + + get '/api/v1/registrant/contacts', {}, @auth_headers + response_json = JSON.parse(response.body, symbolize_names: true) + assert_equal(2, response_json.count) + end + + def test_root_does_not_accept_limit_higher_than_200 + get '/api/v1/registrant/contacts', { 'limit' => 400, 'offset' => 0 }, @auth_headers + assert_equal(400, response.status) + response_json = JSON.parse(response.body, symbolize_names: true) + assert_equal({ errors: [{ limit: ['parameter is out of range'] }] }, response_json) + end + + def test_root_does_not_accept_offset_lower_than_0 + get '/api/v1/registrant/contacts', { 'limit' => 200, 'offset' => "-10" }, @auth_headers + assert_equal(400, response.status) + response_json = JSON.parse(response.body, symbolize_names: true) + assert_equal({ errors: [{ offset: ['parameter is out of range'] }] }, response_json) + end + + def test_root_returns_401_without_authorization + get '/api/v1/registrant/contacts', {}, {} + assert_equal(401, response.status) + json_body = JSON.parse(response.body, symbolize_names: true) + + assert_equal({ errors: ['Not authorized'] }, json_body) + end + + def test_details_returns_401_without_authorization + get '/api/v1/registrant/contacts/c0a191d5-3793-4f0b-8f85-491612d0293e', {}, {} + assert_equal(401, response.status) + json_body = JSON.parse(response.body, symbolize_names: true) + + assert_equal({ errors: ['Not authorized'] }, json_body) + end + + def test_details_returns_404_for_non_existent_contact + get '/api/v1/registrant/contacts/some-random-uuid', {}, @auth_headers + assert_equal(404, response.status) + json_body = JSON.parse(response.body, symbolize_names: true) + + assert_equal({ errors: ['Contact not found'] }, json_body) + end + + private + + def auth_token + token_creator = AuthTokenCreator.create_with_defaults(@user) + hash = token_creator.token_in_hash + "Bearer #{hash[:access_token]}" + end +end From 079800172552ab94ff5bcc360288d76c5c2828ff Mon Sep 17 00:00:00 2001 From: Maciej Szlosarczyk Date: Tue, 7 Aug 2018 15:04:37 +0300 Subject: [PATCH 16/21] Make returned API errors more consistent --- app/controllers/api/v1/registrant/contacts_controller.rb | 2 +- .../api/registrant/registrant_api_contacts_test.rb | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/controllers/api/v1/registrant/contacts_controller.rb b/app/controllers/api/v1/registrant/contacts_controller.rb index 31d56885b..f252b3acd 100644 --- a/app/controllers/api/v1/registrant/contacts_controller.rb +++ b/app/controllers/api/v1/registrant/contacts_controller.rb @@ -31,7 +31,7 @@ module Api if @contact render json: @contact else - render json: { errors: ['Contact not found'] }, status: :not_found + render json: { errors: [{ base: ['Contact not found'] }] }, status: :not_found end end diff --git a/test/integration/api/registrant/registrant_api_contacts_test.rb b/test/integration/api/registrant/registrant_api_contacts_test.rb index a3367dd58..ff51494af 100644 --- a/test/integration/api/registrant/registrant_api_contacts_test.rb +++ b/test/integration/api/registrant/registrant_api_contacts_test.rb @@ -50,7 +50,7 @@ class RegistrantApiContactsTest < ActionDispatch::IntegrationTest assert_equal(401, response.status) json_body = JSON.parse(response.body, symbolize_names: true) - assert_equal({ errors: ['Not authorized'] }, json_body) + assert_equal({ errors: [base: ['Not authorized']] }, json_body) end def test_details_returns_401_without_authorization @@ -58,7 +58,7 @@ class RegistrantApiContactsTest < ActionDispatch::IntegrationTest assert_equal(401, response.status) json_body = JSON.parse(response.body, symbolize_names: true) - assert_equal({ errors: ['Not authorized'] }, json_body) + assert_equal({ errors: [base: ['Not authorized']] }, json_body) end def test_details_returns_404_for_non_existent_contact @@ -66,7 +66,7 @@ class RegistrantApiContactsTest < ActionDispatch::IntegrationTest assert_equal(404, response.status) json_body = JSON.parse(response.body, symbolize_names: true) - assert_equal({ errors: ['Contact not found'] }, json_body) + assert_equal({ errors: [base: ['Contact not found']] }, json_body) end private From 06dc954167b48f1854611282d02537cf6ced3a56 Mon Sep 17 00:00:00 2001 From: Maciej Szlosarczyk Date: Wed, 8 Aug 2018 09:15:18 +0300 Subject: [PATCH 17/21] Make tests inherit from ApplicationIntegrationTest class --- test/integration/api/registrant/registrant_api_contacts_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/api/registrant/registrant_api_contacts_test.rb b/test/integration/api/registrant/registrant_api_contacts_test.rb index ff51494af..c73c4a503 100644 --- a/test/integration/api/registrant/registrant_api_contacts_test.rb +++ b/test/integration/api/registrant/registrant_api_contacts_test.rb @@ -1,7 +1,7 @@ require 'test_helper' require 'auth_token/auth_token_creator' -class RegistrantApiContactsTest < ActionDispatch::IntegrationTest +class RegistrantApiContactsTest < ApplicationIntegrationTest def setup super From b18db190355fb38f5b7891ef32dd284a8025e39b Mon Sep 17 00:00:00 2001 From: Maciej Szlosarczyk Date: Thu, 9 Aug 2018 14:07:06 +0300 Subject: [PATCH 18/21] Fix error introduced by merge conflict --- config/routes.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/routes.rb b/config/routes.rb index 7a802a7a1..6af6623f8 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -23,7 +23,7 @@ Rails.application.routes.draw do namespace :registrant do post 'auth/eid', to: 'auth#eid' - resources :domains, only: %i[index, show], param: :uuid + resources :domains, only: %i[index show], param: :uuid resources :contacts, only: %i[index show], param: :uuid end end From 4cc06692868e15a910b41beb17658d309396cda9 Mon Sep 17 00:00:00 2001 From: Maciej Szlosarczyk Date: Thu, 9 Aug 2018 15:04:22 +0300 Subject: [PATCH 19/21] Add business registry support for contacts controller --- .../api/v1/registrant/contacts_controller.rb | 18 +++++++--- .../api/v1/registrant/domains_controller.rb | 2 -- .../registrant/contacts_controller.rb | 2 +- .../registrant_api_contacts_test.rb | 33 +++++++++++++++++-- 4 files changed, 45 insertions(+), 10 deletions(-) diff --git a/app/controllers/api/v1/registrant/contacts_controller.rb b/app/controllers/api/v1/registrant/contacts_controller.rb index f252b3acd..2c8a1f70e 100644 --- a/app/controllers/api/v1/registrant/contacts_controller.rb +++ b/app/controllers/api/v1/registrant/contacts_controller.rb @@ -1,6 +1,3 @@ -require 'rails5_api_controller_backport' -require 'auth_token/auth_token_decryptor' - module Api module V1 module Registrant @@ -39,7 +36,20 @@ module Api def set_contacts_pool country_code, ident = current_user.registrant_ident.to_s.split '-' - @contacts_pool = Contact.where(country_code: country_code, ident: ident) + associated_domain_ids = begin + BusinessRegistryCache.fetch_by_ident_and_cc(ident, country_code).associated_domain_ids + end + + available_contacts_ids = begin + DomainContact.where(domain_id: associated_domain_ids).pluck(:contact_id) | + Domain.where(id: associated_domain_ids).pluck(:registrant_id) + end + + @contacts_pool = Contact.where(id: available_contacts_ids) + rescue Soap::Arireg::NotAvailableError => error + Rails.logger.fatal("[EXCEPTION] #{error}") + render json: { errors: [{ base: ["Business Registry Not Available"] }] }, + status: :service_unavailable and return end end end diff --git a/app/controllers/api/v1/registrant/domains_controller.rb b/app/controllers/api/v1/registrant/domains_controller.rb index 27b7b6125..7209f8a10 100644 --- a/app/controllers/api/v1/registrant/domains_controller.rb +++ b/app/controllers/api/v1/registrant/domains_controller.rb @@ -1,5 +1,3 @@ -require 'rails5_api_controller_backport' - module Api module V1 module Registrant diff --git a/app/controllers/registrant/contacts_controller.rb b/app/controllers/registrant/contacts_controller.rb index f73650de2..267b4d68d 100644 --- a/app/controllers/registrant/contacts_controller.rb +++ b/app/controllers/registrant/contacts_controller.rb @@ -26,4 +26,4 @@ class Registrant::ContactsController < RegistrantController BusinessRegistryCache.fetch_by_ident_and_cc(ident, ident_cc).associated_domain_ids end end -end \ No newline at end of file +end diff --git a/test/integration/api/registrant/registrant_api_contacts_test.rb b/test/integration/api/registrant/registrant_api_contacts_test.rb index c73c4a503..28dd50d76 100644 --- a/test/integration/api/registrant/registrant_api_contacts_test.rb +++ b/test/integration/api/registrant/registrant_api_contacts_test.rb @@ -5,19 +5,30 @@ class RegistrantApiContactsTest < ApplicationIntegrationTest def setup super + @original_registry_time = Setting.days_to_keep_business_registry_cache + Setting.days_to_keep_business_registry_cache = 1 + travel_to Time.zone.parse('2010-07-05') + @user = users(:registrant) @auth_headers = { 'HTTP_AUTHORIZATION' => auth_token } end + def teardown + super + + Setting.days_to_keep_business_registry_cache = @original_registry_time + travel_back + end + def test_root_returns_domain_list get '/api/v1/registrant/contacts', {}, @auth_headers assert_equal(200, response.status) json_body = JSON.parse(response.body, symbolize_names: true) - assert_equal(2, json_body.count) + assert_equal(5, json_body.count) array_of_contact_codes = json_body.map { |x| x[:code] } assert(array_of_contact_codes.include?('william-001')) - assert(array_of_contact_codes.include?('william-002')) + assert(array_of_contact_codes.include?('jane-001')) end def test_root_accepts_limit_and_offset_parameters @@ -28,7 +39,23 @@ class RegistrantApiContactsTest < ApplicationIntegrationTest get '/api/v1/registrant/contacts', {}, @auth_headers response_json = JSON.parse(response.body, symbolize_names: true) - assert_equal(2, response_json.count) + assert_equal(5, response_json.count) + end + + def test_get_contact_details_by_uuid + get '/api/v1/registrant/contacts/0aa54704-d6f7-4ca9-b8ca-2827d9a4e4eb', {}, @auth_headers + assert_equal(200, response.status) + + contact = JSON.parse(response.body, symbolize_names: true) + assert_equal('william@inbox.test', contact[:email]) + end + + def test_get_contact_details_by_uuid_returns_404_for_non_existent_contact + get '/api/v1/registrant/contacts/nonexistent-uuid', {}, @auth_headers + assert_equal(404, response.status) + + response_json = JSON.parse(response.body, symbolize_names: true) + assert_equal({ errors: [{ base: ['Contact not found'] }] }, response_json) end def test_root_does_not_accept_limit_higher_than_200 From 256d2b7de424b914c7cd36628d73f58de3420086 Mon Sep 17 00:00:00 2001 From: Maciej Szlosarczyk Date: Thu, 9 Aug 2018 15:24:21 +0300 Subject: [PATCH 20/21] Add test for error in business registry --- .../api/v1/registrant/contacts_controller.rb | 2 +- .../api/registrant/registrant_api_contacts_test.rb | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/app/controllers/api/v1/registrant/contacts_controller.rb b/app/controllers/api/v1/registrant/contacts_controller.rb index 2c8a1f70e..5db175265 100644 --- a/app/controllers/api/v1/registrant/contacts_controller.rb +++ b/app/controllers/api/v1/registrant/contacts_controller.rb @@ -48,7 +48,7 @@ module Api @contacts_pool = Contact.where(id: available_contacts_ids) rescue Soap::Arireg::NotAvailableError => error Rails.logger.fatal("[EXCEPTION] #{error}") - render json: { errors: [{ base: ["Business Registry Not Available"] }] }, + render json: { errors: [{ base: ["Business Registry not available"] }] }, status: :service_unavailable and return end end diff --git a/test/integration/api/registrant/registrant_api_contacts_test.rb b/test/integration/api/registrant/registrant_api_contacts_test.rb index 28dd50d76..ddeaee9f3 100644 --- a/test/integration/api/registrant/registrant_api_contacts_test.rb +++ b/test/integration/api/registrant/registrant_api_contacts_test.rb @@ -50,6 +50,17 @@ class RegistrantApiContactsTest < ApplicationIntegrationTest assert_equal('william@inbox.test', contact[:email]) end + def test_root_returns_503_when_business_registry_is_not_available + raise_not_available = -> (a, b) { raise Soap::Arireg::NotAvailableError.new({}) } + BusinessRegistryCache.stub :fetch_by_ident_and_cc, raise_not_available do + get '/api/v1/registrant/contacts', {}, @auth_headers + + assert_equal(503, response.status) + response_json = JSON.parse(response.body, symbolize_names: true) + assert_equal({ errors: [base: ['Business Registry not available']] }, response_json) + end + end + def test_get_contact_details_by_uuid_returns_404_for_non_existent_contact get '/api/v1/registrant/contacts/nonexistent-uuid', {}, @auth_headers assert_equal(404, response.status) From c186929be4704ba2f1ea26a3b4e86286dce1ac26 Mon Sep 17 00:00:00 2001 From: Maciej Szlosarczyk Date: Thu, 9 Aug 2018 15:28:43 +0300 Subject: [PATCH 21/21] Use single quote instead of double quotes --- app/controllers/api/v1/registrant/contacts_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/api/v1/registrant/contacts_controller.rb b/app/controllers/api/v1/registrant/contacts_controller.rb index 5db175265..de5ef9dcf 100644 --- a/app/controllers/api/v1/registrant/contacts_controller.rb +++ b/app/controllers/api/v1/registrant/contacts_controller.rb @@ -48,7 +48,7 @@ module Api @contacts_pool = Contact.where(id: available_contacts_ids) rescue Soap::Arireg::NotAvailableError => error Rails.logger.fatal("[EXCEPTION] #{error}") - render json: { errors: [{ base: ["Business Registry not available"] }] }, + render json: { errors: [{ base: ['Business Registry not available'] }] }, status: :service_unavailable and return end end