Skip to content
This repository has been archived by the owner on Mar 8, 2022. It is now read-only.

Add auth0 organizations #458

Merged
merged 12 commits into from
Nov 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
## Unreleased
## 0.22.0

ENHANCEMENTS:
* **New Resource:** `auth0_organization` ([#458](https://github.com/alexkappa/terraform-provider-auth0/pull/458))

## 0.21.1

* resource/auth0_client: Documentation removal of `custom_login_page_preview` field [#386](https://github.com/alexkappa/terraform-provider-auth0/pull/386)
* resource/auth0_client: Add `organization_usage` and `organization_require_behavior` parameters to `auth0_client` resource. ([#451](https://github.com/alexkappa/terraform-provider-auth0/pull/451))

NOTES:

* Bumped go-auth0 version to v5.17.0 [#398](https://github.com/alexkappa/terraform-provider-auth0/pull/398)
* Build darwin/arm64 binaries for Mac M1 silicon ([#421](https://github.com/alexkappa/terraform-provider-auth0/pull/421))

## 0.21.0

Expand Down
21 changes: 21 additions & 0 deletions auth0/internal/hash/hash.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package hash

import (
"github.com/hashicorp/terraform-plugin-sdk/helper/hashcode"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
)

// StringKey returns a schema.SchemaSetFunc able to hash a string value
// from map accessed by k.
func StringKey(k string) schema.SchemaSetFunc {
return func(v interface{}) int {
m, ok := v.(map[string]interface{})
if !ok {
return 0
}
if v, ok := m[k].(string); ok {
return hashcode.String(v)
}
return 0
}
}
23 changes: 23 additions & 0 deletions auth0/internal/hash/hash_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package hash

import "testing"

func TestStringKey(t *testing.T) {

v := map[string]interface{}{
"Foo": "Foo",
"Bar": "Bar",
}

for key, expected := range map[string]int{
"Foo": 3023971265,
"Bar": 1320340042,
} {
t.Run(key, func(t *testing.T) {
fn := StringKey(key)
if fn(v) != expected {
t.Errorf("expected %d to be %d", fn(v), expected)
}
})
}
}
1 change: 1 addition & 0 deletions auth0/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ func init() {
"auth0_log_stream": newLogStream(),
"auth0_branding": newBranding(),
"auth0_guardian": newGuardian(),
"auth0_organization": newOrganization(),
},
ConfigureFunc: Configure,
}
Expand Down
2 changes: 1 addition & 1 deletion auth0/resource_auth0_email_template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func TestAccEmailTemplate(t *testing.T) {
"auth0": Provider(),
},
Steps: []resource.TestStep{
resource.TestStep{
{
Config: testAccEmailTemplateConfig,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("auth0_email_template.my_email_template", "template", "welcome_email"),
Expand Down
239 changes: 239 additions & 0 deletions auth0/resource_auth0_organization.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
package auth0

import (
"fmt"
"log"
"net/http"

"github.com/alexkappa/terraform-provider-auth0/auth0/internal/hash"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"gopkg.in/auth0.v5/management"
)

func newOrganization() *schema.Resource {
return &schema.Resource{

Create: createOrganization,
Read: readOrganization,
Update: updateOrganization,
Delete: deleteOrganization,

Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},

Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
Description: "The name of this organization",
},
"display_name": {
Type: schema.TypeString,
Optional: true,
Description: "Friendly name of this organization",
},
"branding": {
Type: schema.TypeList,
Optional: true,
Computed: true,
MaxItems: 1,
MinItems: 1,
Description: "Defines how to style the login pages",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"logo_url": {
Type: schema.TypeString,
Optional: true,
},
"colors": {
Type: schema.TypeMap,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
},
},
},
"metadata": {
Type: schema.TypeMap,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
Description: "Metadata associated with the organization, Maximum of 10 metadata properties allowed",
},
"connections": {
Type: schema.TypeSet,
Optional: true,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"connection_id": {
Type: schema.TypeString,
Required: true,
},
"assign_membership_on_login": {
Type: schema.TypeBool,
Optional: true,
},
},
},
Set: hash.StringKey("connection_id"),
},
},
}
}

func createOrganization(d *schema.ResourceData, m interface{}) error {
o := expandOrganization(d)
api := m.(*management.Management)
if err := api.Organization.Create(o); err != nil {
return err
}
d.SetId(o.GetID())

d.Partial(true)
err := assignOrganizationConnections(d, m)
if err != nil {
return fmt.Errorf("failed assigning organization connections. %w", err)
}
d.Partial(false)

return readOrganization(d, m)
}

func assignOrganizationConnections(d *schema.ResourceData, m interface{}) (err error) {

api := m.(*management.Management)

add, rm := Diff(d, "connections")

add.Elem(func(dd ResourceData) {
c := &management.OrganizationConnection{
ConnectionID: String(dd, "connection_id"),
AssignMembershipOnLogin: Bool(dd, "assign_membership_on_login"),
}
log.Printf("[DEBUG] (+) auth0_organization.%s.connections.%s", d.Id(), c.GetConnectionID())
err = api.Organization.AddConnection(d.Id(), c)
if err != nil {
return
}
})

rm.Elem(func(dd ResourceData) {
// Take connectionID before it changed (i.e. removed). Therefore we use
// GetChange() instead of the typical Get().
connectionID, _ := dd.GetChange("connection_id")
log.Printf("[DEBUG] (-) auth0_organization.%s.connections.%s", d.Id(), connectionID.(string))
err = api.Organization.DeleteConnection(d.Id(), connectionID.(string))
if err != nil {
return
}
})

// Update existing connections if any mutable properties have changed.
Set(d, "connections", HasChange()).Elem(func(dd ResourceData) {
connectionID := dd.Get("connection_id").(string)
c := &management.OrganizationConnection{
AssignMembershipOnLogin: Bool(dd, "assign_membership_on_login"),
}
log.Printf("[DEBUG] (~) auth0_organization.%s.connections.%s", d.Id(), connectionID)
err = api.Organization.UpdateConnection(d.Id(), connectionID, c)
if err != nil {
return
}
})

return nil
}

func readOrganization(d *schema.ResourceData, m interface{}) error {
api := m.(*management.Management)
o, err := api.Organization.Read(d.Id())
if err != nil {
if mErr, ok := err.(management.Error); ok {
if mErr.Status() == http.StatusNotFound {
d.SetId("")
return nil
}
}
return err
}

d.SetId(o.GetID())
d.Set("name", o.Name)
d.Set("display_name", o.DisplayName)
d.Set("branding", flattenOrganizationBranding(o.Branding))
d.Set("metadata", o.Metadata)

l, err := api.Organization.Connections(d.Id())
if err != nil {
return err
}

d.Set("connections", func() (v []interface{}) {
for _, connection := range l.OrganizationConnections {
v = append(v, &map[string]interface{}{
"connection_id": connection.ConnectionID,
"assign_membership_on_login": connection.AssignMembershipOnLogin,
})
}
return
}())

return nil
}

func updateOrganization(d *schema.ResourceData, m interface{}) error {
o := expandOrganization(d)
api := m.(*management.Management)
err := api.Organization.Update(d.Id(), o)
if err != nil {
return err
}

d.Partial(true)
err = assignOrganizationConnections(d, m)
if err != nil {
return fmt.Errorf("failed updating organization connections. %w", err)
}
d.Partial(false)

return readOrganization(d, m)
}

func deleteOrganization(d *schema.ResourceData, m interface{}) error {
api := m.(*management.Management)
err := api.Organization.Delete(d.Id())
if err != nil {
if mErr, ok := err.(management.Error); ok {
if mErr.Status() == http.StatusNotFound {
d.SetId("")
return nil
}
}
}
return err
}

func expandOrganization(d *schema.ResourceData) *management.Organization {
o := &management.Organization{
Name: String(d, "name"),
DisplayName: String(d, "display_name"),
Metadata: Map(d, "metadata"),
}
List(d, "branding").Elem(func(d ResourceData) {
o.Branding = &management.OrganizationBranding{
LogoURL: String(d, "logo_url"),
Colors: Map(d, "colors"),
}
})
return o
}

func flattenOrganizationBranding(b *management.OrganizationBranding) []interface{} {
m := make(map[string]interface{})
if b != nil {
m["logo_url"] = b.LogoURL
m["colors"] = b.Colors
}
return []interface{}{m}
}
Loading