Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Derick Diaz committed Apr 3, 2023
0 parents commit 653ca26
Show file tree
Hide file tree
Showing 6 changed files with 563 additions and 0 deletions.
98 changes: 98 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# go_adp
ADP Developer Library for Golang


## Worker API Service

### Authentication
Before initializing a service, the application must authentication using an ADPAuthenticationSystem. Current only OAuth is supported.
To obtain the required files, a CSR must be submitted to ADP: https://developers.adp.com/articles/general/generate-a-certificate-signing-request.
Once the CSR is approved, ADP will provide the following:

1. Signed PFX certificate
2. Key file

Use OpenSSL To convert the signed pfx certificate to a pem file.

```bash
openssl pkcs12 -in certificate.pfx -out certificate.pem -clcerts
```

Lastly, an ADP Representative will help create a service account. The base64 credentials follow the "username:password" format.

```golang
auth, err := adp.NewOAuthAuthenticationSystem("certificate.pem", "certificate_key.key", "base64_credentials")
if err != nil {
log.Fatal(err)
}
// Generates OAuth Client Credentials
err = auth.Authenticate()
```

## Using Worker API Service

Pulling all workers from the ADP Directory synchronously
```golang

import (
"fmt"
adp "github.com/derickdiaz/go_adp"
"log"
)


func main() {
auth, err := adp.NewOAuthAuthenticationSystem("certificate.pem", "certificate_key.key", "base64_credentials")
if err != nil {
log.Fatal(err)
}
err = auth.Authenticate()
if err != nil {
log.Fatal(err)
}

svc := adp.NewADPWorkerService(auth)
workers, err := svc.ListWorkers()
if err != nil {
log.Fatal(err)
}

for _, worker := range workers.Workers {
fmt.Printf(
"AssociateOID: %s\n"+
"First Name: %s\n"+
"Middle Name: %s\n"+
"Last Name: %s\n"+
"Active: %v",
worker.GetAssociateOID(),
worker.GetFirstName(),
worker.GetMiddleName(),
worker.GetLastName(),
worker.IsActive(),
)

assignment := worker.GetPrimaryWorkAssignment()
if assignment != nil {
fmt.Println("")
}
fmt.Printf(
"Job Title: %s\n\n",
assignment.GetJobTitle(),
)
}
}
```

Pulling all workers from the ADP Directory using channels
``` golang
...
svc := adp.NewADPWorkerService(auth)
reciever := make(chan *adp.Worker)

go svc.ListWorkersAsync(reciever, 50)

for worker := range reciever {
fmt.Println(worker.GetAssociateOID())
}
...
```
109 changes: 109 additions & 0 deletions auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package adp

import (
"crypto/tls"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
)

type ADPAuthenticationSystem interface {
Authenticate() error
NewHttpClient() (*http.Client, error)
SetRequestAuthorizationHeader(*http.Request)
}

type ADPOAuthAuthentication struct {
KeyFilePath string
CertificateFilePath string
Credential string
Token *ADPOAuthOutput
}

type ADPOAuthOutput struct {
AccessToken string `json:"access_token"`
TokenType string `json:"token_type"`
ExpiresIn int `json:"expires_in"`
Scope string `json:"scope"`
}

func NewOAuthAuthenticationSystem(certificateFilePath, keyFilePath, credential string) (*ADPOAuthAuthentication, error) {
err := []error{}

if !fileExists(keyFilePath) {
err = append(err, fmt.Errorf("key file path does not exists: %s", keyFilePath))
}

if !fileExists(certificateFilePath) {
err = append(err, fmt.Errorf("certficatefile path does not exsits: %s", certificateFilePath))
}

if len(err) > 0 {
return nil, errors.Join(err...)
}

return &ADPOAuthAuthentication{
KeyFilePath: keyFilePath,
CertificateFilePath: certificateFilePath,
Credential: credential,
}, nil
}

func (a *ADPOAuthAuthentication) Authenticate() error {
client, err := a.NewHttpClient()
if err != nil {
return err
}
request, err := http.NewRequest(
http.MethodPost,
"https://accounts.adp.com/auth/oauth/v2/token?grant_type=client_credentials",
nil,
)
if err != nil {
return err
}

request.Header.Set("Content-Type", "application/json")
request.Header.Set("Authorization", fmt.Sprintf("Basic %s", a.Credential))

resp, err := client.Do(request)
if err != nil {
return err
}

if !isValidResponseStatusCode(resp) {
body, err := io.ReadAll(resp.Body)
if err != nil {
return err
}
return fmt.Errorf("unable to retrieve access token: %s", body)
}

token := ADPOAuthOutput{}
if err = json.NewDecoder(resp.Body).Decode(&token); err != nil {
return err
}
a.Token = &token
return nil
}

func (a *ADPOAuthAuthentication) SetRequestAuthorizationHeader(request *http.Request) {
request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", a.Token.AccessToken))
}

func (a *ADPOAuthAuthentication) NewHttpClient() (*http.Client, error) {
certificates, err := tls.LoadX509KeyPair(a.CertificateFilePath, a.KeyFilePath)
if err != nil {
return nil, err
}
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
Certificates: []tls.Certificate{certificates},
},
},
}
return client, nil
}
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/derickdiaz/go_adp

go 1.20
17 changes: 17 additions & 0 deletions shared.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package adp

import (
"net/http"
"os"
)

func fileExists(filePath string) bool {
if _, err := os.Stat(filePath); err != nil {
return false
}
return true
}

func isValidResponseStatusCode(resp *http.Response) bool {
return resp.StatusCode >= 200 && resp.StatusCode < 300
}
Loading

0 comments on commit 653ca26

Please sign in to comment.