Skip to content

Commit

Permalink
enh: basic project structure and simple PoC
Browse files Browse the repository at this point in the history
  • Loading branch information
notdodo committed Nov 8, 2023
1 parent 34ecf6d commit b3c5e87
Show file tree
Hide file tree
Showing 10 changed files with 301 additions and 12 deletions.
11 changes: 7 additions & 4 deletions .env_example
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
NEO4J_URL="neo4j://localhost:7687"
PASSWORD=IAMmeIAMme!1!
NEO4J_AUTH="neo4j/${PASSWORD}"
NEO4J_URL=neo4j://localhost:7687
NEO4J_USER=neo4j
NEO4J_PASS=IAMmeIAMme!1!
NEO4J_AUTH="${NEO4J_USER}/${NEO4J_PASS}"
NEO4J_server_memory_heap_initial__size=12G
NEO4J_server_memory_heap_max__size=16G
NEO4J_server_memory_pagecache_size=12G
NEO4J_server_memory_pagecache_size=12G
OKTA_CLIENT_ORGURL=yourtenant.okta.com
OKTA_CLIENT_TOKEN="00SOMETHINGWRONG"
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
dist
iamme*
IAMme-IAMme*
*.sarif
20 changes: 20 additions & 0 deletions cmd/dump.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package cmd

import (
"IAMme-IAMme/pkg/app"

"github.com/spf13/cobra"
)

var usersCmd = &cobra.Command{
Use: "dump",
Short: "Fetch Okta info and store them in Neo4j",
Run: func(cmd *cobra.Command, args []string) {
oktaNeo4jApp := app.NewOktaNeo4jApp(clients.okta, clients.neo4j)
oktaNeo4jApp.Dump()
},
}

func init() {
rootCmd.AddCommand(usersCmd)
}
30 changes: 30 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package cmd

import (
"IAMme-IAMme/pkg/infra/neo4j"
"IAMme-IAMme/pkg/infra/okta"
"fmt"

"github.com/spf13/cobra"
)

type clients_type struct {
okta okta.OktaClient
neo4j neo4j.Neo4jClient
}

var clients *clients_type
var rootCmd = &cobra.Command{
Use: "iamme-iamme",
Short: "A CLI tool to interact with Okta and Neo4j",
}

func Execute(oktaClient okta.OktaClient, neo4jClient neo4j.Neo4jClient) {
clients = &clients_type{
oktaClient,
neo4jClient,
}
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
}
}
19 changes: 14 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,23 @@ module IAMme-IAMme

go 1.20

require github.com/okta/okta-sdk-golang/v2 v2.20.0
require (
github.com/joho/godotenv v1.5.1
github.com/neo4j/neo4j-go-driver/v5 v5.14.0
github.com/okta/okta-sdk-golang/v2 v2.20.0
github.com/spf13/cobra v1.8.0
)

require (
github.com/BurntSushi/toml v1.1.0 // indirect
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
github.com/go-jose/go-jose/v3 v3.0.0 // indirect
github.com/BurntSushi/toml v1.3.2 // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/go-jose/go-jose/v3 v3.0.1 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/kelseyhightower/envconfig v1.4.0 // indirect
github.com/kr/pretty v0.1.0 // indirect
github.com/patrickmn/go-cache v0.0.0-20180815053127-5633e0862627 // indirect
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect
github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/crypto v0.15.0 // indirect
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
28 changes: 27 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
@@ -1,35 +1,61 @@
github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I=
github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4=
github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo=
github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
github.com/go-jose/go-jose/v3 v3.0.1 h1:pWmKFVtt+Jl0vBZTIpz/eAKwsm6LkIxDVVbFHKkchhA=
github.com/go-jose/go-jose/v3 v3.0.1/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/neo4j/neo4j-go-driver/v5 v5.14.0 h1:5x3vD4HkXQIktlG63jSG8v9iweGjmObIPU7Y9U0ThUI=
github.com/neo4j/neo4j-go-driver/v5 v5.14.0/go.mod h1:Vff8OwT7QpLm7L2yYr85XNWe9Rbqlbeb9asNXJTHO4k=
github.com/okta/okta-sdk-golang/v2 v2.20.0 h1:EDKM+uOPfihOMNwgHMdno+NAsIfyXkVnoFAYVPay0YU=
github.com/okta/okta-sdk-golang/v2 v2.20.0/go.mod h1:FMy5hN5G8Rd/VoS0XrfyPPhIfOVo78ZK7lvwiQRS2+U=
github.com/patrickmn/go-cache v0.0.0-20180815053127-5633e0862627 h1:pSCLCl6joCFRnjpeojzOpEYs4q7Vditq8fySFG5ap3Y=
github.com/patrickmn/go-cache v0.0.0-20180815053127-5633e0862627/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM=
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA=
golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
13 changes: 11 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
package main

import (
"fmt"
"IAMme-IAMme/cmd"
"IAMme-IAMme/pkg/infra/neo4j"
"IAMme-IAMme/pkg/infra/okta"
"log"

"github.com/joho/godotenv"
)

func main() {
fmt.Println("IAMme IAMme")
envFile, err := godotenv.Read(".env")
if err != nil {
log.Fatalln(err.Error())
}
cmd.Execute(okta.NewOktaClient(envFile["OKTA_CLIENT_ORGURL"], envFile["OKTA_CLIENT_TOKEN"]), neo4j.NewNeo4jClient(envFile["NEO4J_URL"], envFile["NEO4J_USER"], envFile["NEO4J_PASS"]))
}
95 changes: 95 additions & 0 deletions pkg/app/okta_neo4j.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package app

import (
"IAMme-IAMme/pkg/infra/neo4j"
"IAMme-IAMme/pkg/infra/okta"
"context"
"log"
"strings"

neo4jSdk "github.com/neo4j/neo4j-go-driver/v5/neo4j"
)

type OktaNeo4jApp interface {
Dump()
}

func NewOktaNeo4jApp(oktaClient okta.OktaClient, neo4jClient neo4j.Neo4jClient) OktaNeo4jApp {
return &oktaNeo4jApp{
oktaClient: oktaClient,
neo4jClient: neo4jClient,
}
}

type oktaNeo4jApp struct {
oktaClient okta.OktaClient
neo4jClient neo4j.Neo4jClient
}

func (a *oktaNeo4jApp) Dump() {
users, err := a.oktaClient.GetUsers()
if err != nil {
log.Println(err.Error())
}

userParams := make([]map[string]interface{}, 0)
for _, user := range users {
userParams = append(userParams, map[string]interface{}{
"userId": user.Id,
"status": user.Status,
"firstName": (*user.Profile)["firstName"],
"lastName": (*user.Profile)["lastName"],
})
}

session := a.neo4jClient.Connect()
ctx := context.TODO()
query := buildDynamicQuery(userParams)
_, err = session.ExecuteWrite(ctx, func(tx neo4jSdk.ManagedTransaction) (interface{}, error) {
_, err := tx.Run(ctx, query, map[string]interface{}{
"userParams": userParams,
})

if err != nil {
panic(err)
}
return nil, err
})
if err != nil {
log.Fatalln(err.Error())
}
}

// TODO: this is ugly AF
func buildDynamicQuery(userParams []map[string]interface{}) string {
var queryBuilder strings.Builder

queryBuilder.WriteString("UNWIND $userParams as user\n")
queryBuilder.WriteString("CREATE (u:User {\n")

fields := userParams[0]

i := 0
for field := range fields {
queryBuilder.WriteString(fieldKeyToCypherProperty(field) + ": user." + fieldKeyToCypherProperty(field))
i++
if i < len(fields) {
queryBuilder.WriteString(",\n")
}
}

queryBuilder.WriteString("\n})\n")
queryBuilder.WriteString("RETURN u\n")

return queryBuilder.String()
}

// TODO: this is ugly AF
func fieldKeyToCypherProperty(key interface{}) string {
keyStr, ok := key.(string)
if !ok {
panic("Invalid field key")
}

return keyStr
}
49 changes: 49 additions & 0 deletions pkg/infra/neo4j/neo4j_client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package neo4j

import (
"context"
"log"

"github.com/neo4j/neo4j-go-driver/v5/neo4j"
)

// Neo4jClient is an interface for interacting with the Neo4j database.
type Neo4jClient interface {
Connect() neo4j.SessionWithContext
Close() error
}

// Session is an interface for a Neo4j database session.
type Session interface {
Run(cypher string, params map[string]interface{}) Result
Close() error
}

// Result is an interface for a Neo4j query result.
type Result interface {
Consume() (int, error)
}

type neo4jClient struct {
driver neo4j.DriverWithContext
}

func NewNeo4jClient(dbUri, username, password string) Neo4jClient {
driver, err := neo4j.NewDriverWithContext(dbUri, neo4j.BasicAuth(username, password, ""))
if err != nil {
log.Fatalln("Invalid Neo4j login", err.Error())
}
return &neo4jClient{
driver: driver,
}
}

func (c *neo4jClient) Connect() neo4j.SessionWithContext {
return c.driver.NewSession(context.TODO(), neo4j.SessionConfig{
AccessMode: neo4j.AccessModeWrite,
})
}

func (c *neo4jClient) Close() error {
return c.driver.Close(context.TODO())
}
47 changes: 47 additions & 0 deletions pkg/infra/okta/okta_client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package okta

import (
"context"
"fmt"
"log"

"github.com/okta/okta-sdk-golang/v2/okta"
)

// OktaClient is an interface for interacting with Okta resources.
type OktaClient interface {
GetUsers() ([]*okta.User, error)
GetGroups() ([]*okta.Group, error)
}

type oktaClient struct {
oktaClient *okta.Client
context context.Context
}

func NewOktaClient(orgUrl, apiKey string) OktaClient {
ctx, client, err := okta.NewClient(context.Background(), okta.WithOrgUrl(fmt.Sprintf("https://%s", orgUrl)), okta.WithToken(apiKey))
if err != nil {
log.Fatalln("Invalid Okta login", err.Error())
}
return &oktaClient{
oktaClient: client,
context: ctx,
}
}

func (c *oktaClient) GetUsers() ([]*okta.User, error) {
users, _, err := c.oktaClient.User.ListUsers(context.TODO(), nil)
if err != nil {
return nil, err
}
return users, nil
}

func (c *oktaClient) GetGroups() ([]*okta.Group, error) {
groups, _, err := c.oktaClient.Group.ListGroups(context.TODO(), nil)
if err != nil {
return nil, err
}
return groups, nil
}

0 comments on commit b3c5e87

Please sign in to comment.