diff --git a/README.md b/README.md index 9b37f289f03c2a502da1f4aec9134a45871c7e12..b2791a175fa3e65b70117a259ad5ed9dcb45bdf2 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,15 @@ # libsectigo -Nim Library implementing the CNAME method for DCV, using Sectigo's Cert-Manager Rest API +Nim Library implementing the CNAME method for Domain Control Validation, using Sectigo's Cert-Manager Rest API -DCV: Domain Control Validation \ No newline at end of file + +# Sectigo Rest API reference + +[Sectigo Rest API refecence](https://sectigo.com/uploads/audio/Certificate-Manager-20.1-Rest-API.html) + + +## Disclamer, misc + +This is implementation is limited to the DCV method, feel free to submit PR, fork or whatever suits you to enhance it. + +<small><div>Icons from <a href="https://www.flaticon.com/fr/auteurs/srip" title="srip">srip</a> from <a href="https://www.flaticon.com/fr/" title="Flaticon">www.flaticon.com</a></div></small> \ No newline at end of file diff --git a/libsectigo.nimble b/libsectigo.nimble new file mode 100644 index 0000000000000000000000000000000000000000..0d39c218ca5b2122077c2810f3ae7cd4abf2c461 --- /dev/null +++ b/libsectigo.nimble @@ -0,0 +1,12 @@ +# Package + +version = "0.1.0" +author = "m33" +description = "A Library implementing the CNAME method for DCV, using Sectigo's Cert-Manager Rest API" +license = "MIT" +srcDir = "src" + + +# Dependencies + +requires "nim >= 1.4.0" diff --git a/src/libsectigo.nim b/src/libsectigo.nim new file mode 100644 index 0000000000000000000000000000000000000000..bf304aebd5615f6273d7d874a2c73ba755a7d930 --- /dev/null +++ b/src/libsectigo.nim @@ -0,0 +1,2 @@ +import libsectigo/sectigo +export sectigo diff --git a/src/libsectigo/sectigo.nim b/src/libsectigo/sectigo.nim new file mode 100644 index 0000000000000000000000000000000000000000..9efecccb5b9643be16277c4a420360835ce000a6 --- /dev/null +++ b/src/libsectigo/sectigo.nim @@ -0,0 +1,173 @@ +import json +import httpclient +import strutils + +# Sectigo WSS API entry point (complete URL) +type + CM_WSS* {.pure.} = enum + List = "https://cert-manager.com/api/dcv/v1/validation?&size=200&position=0&org=&department=&domain=&expiresIn=365&org=&department=", + Get_Validation_Status = "https://cert-manager.com/api/dcv/v2/validation/status", + Check_Domain_Validation_Status = "https://cert-manager.com/api/dcv/v1/validation?&size=200&position=0&org=&department=&domain=&expiresIn=365&org=&department=", + Start_New_Validation = "https://cert-manager.com/api/dcv/v1/validation/start/domain/cname" + Submit_Validation = "https://cert-manager.com/api/dcv/v1/validation/submit/domain/cname" + +# Sectigo Network API handler +type + Cert_Manager* = object + client: HttpClient + accept: string + content_type: string + login: string + password: string + customer: string + data: string + + +# +# unescape_safe(string): string +# +# In many cases we don't know for sure is a string from WSS is escaped or not +# Don't mess with exception in code, let's define a macro for that +# +proc unescape_safe(a_string: string): string = + var unescaped_string: string + try: + unescaped_string = unescape(a_string) + except: + unescaped_string = a_string + + return unescaped_string +#endproc + + +# +# initSectigoNetworkAPI(CM): HTTPClient +# +# Open a connection to the network API for one WSS provider +# Return true if initialized correctly, withtout connexion exceptions +# +proc initSectigoNetworkAPI*(CM: Cert_Manager): HttpClient = + var client: HttpClient + let client_headers = {"Accept": "application/json", "login": CM.login, "password": CM.password, + "customerUri": CM.customer, "Content-Type": "application/json;charset=utf-8"} + + try: + client = newHttpClient() + client.headers = newHttpHeaders(client_headers) + except: + echo "Error: can't initialize connexion to WSS. Detail: ", getCurrentExceptionMsg() + return client + + return client +#endproc + + +# +# endSectigoNetworkAPI(CM) +# +# Close network socket +# +proc endSectigoNetworkAPI*(CM: Cert_Manager) = + close(CM.client) +#endproc + + +# +# getSectigoDomains(CM): JsonNode +# +# get the domain list from Sectigo +# +proc getSectigoDomains*(CM: Cert_Manager): JsonNode = + var list: Response + var jlist: JsonNode + + try: + list = CM.client.request($CM_WSS.List) + except: + echo "Error: can't connect to WSS. Detail: ", getCurrentExceptionMsg() + return jlist + + try: + jlist = parseJson(list.body) + except: + echo "Error: can't parse WSS response to json. Detail: ", getCurrentExceptionMsg() + return jlist + + return jlist +#endproc + + +# +# getValidationStatus(CM, domain): JsonNode +# +# get the DCV current status for domain, return the WS response +# +proc getValidationStatus*(CM: Cert_Manager, domain: string): JsonNode = + var response: Response + var jresponse: JsonNode + + try: + response = post(CM.client, $CM_WSS.Get_Validation_Status, "{\"domain\":\"" & domain & "\"}", nil) + except: + echo "Error: can't connect to WSS. Detail: ", getCurrentExceptionMsg() + return jresponse + + jresponse = parseJson(response.body) + + return jresponse +#endproc + + +# +# startNewValidation(CM, domain): string[] +# +# Start a CNAME DCV for domain, returns the cname and value +# +proc startNewValidation*(CM: Cert_Manager, domain: string): seq[string] = + var response: Response + var jresult: JsonNode + var dcv_entries: seq[string] + + try: + response = post(CM.client, $CM_WSS.Start_New_Validation, "{\"domain\":\"" & domain & "\"}", nil) + except: + echo "Error: can't connect to WSS. Detail: ", getCurrentExceptionMsg() + return dcv_entries + + try: + jresult = parseJson(response.body) + except: + echo "Error: can't parse WSS response to json. Detail: ", getCurrentExceptionMsg() + return dcv_entries + + dcv_entries.add(unescape_safe($jresult["host"])) + dcv_entries.add(unescape_safe($jresult["point"])) + return dcv_entries +#endproc + + +# +# submitValidation(CM, domain): bool +# +# Submit a CNAME DCV for domain, hope for the best +# Return true for a success +# +proc submitValidation*(CM: Cert_Manager, domain: string): bool = + var response: Response + var jresult: JsonNode + var dcv_entries: seq[string] + + try: + response = post(CM.client, $CM_WSS.Submit_Validation, "{\"domain\":\"" & domain & "\"}", nil) + except: + echo "Error: can't connect to WSS. Detail: ", getCurrentExceptionMsg() + return false + + try: + jresult = parseJson(response.body) + except: + echo "Error: can't parse WSS response to json. Detail: ", getCurrentExceptionMsg() + return false + + return true +#endproc diff --git a/tests/config.nims b/tests/config.nims new file mode 100644 index 0000000000000000000000000000000000000000..3bb69f82af2b03d759a985634d69563496d956b6 --- /dev/null +++ b/tests/config.nims @@ -0,0 +1 @@ +switch("path", "$projectDir/../src") \ No newline at end of file