Skip to content

Commit

Permalink
Add Federations, Federation Certificates (#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
Icerzack authored Jul 29, 2024
1 parent cd9d6a0 commit 7380a41
Show file tree
Hide file tree
Showing 17 changed files with 1,568 additions and 45 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ You can use this library to work with the following objects of the Selectel IAM
* [serviceusers](https://pkg.go.dev/github.com/selectel/iam-go/service/serviceusers)
* [groups](https://pkg.go.dev/github.com/selectel/iam-go/service/groups)
* [s3credentials](https://pkg.go.dev/github.com/selectel/iam-go/service/s3credentials)
* [federations (saml)](https://pkg.go.dev/github.com/selectel/iam-go/service/federations/saml)

### Installation

Expand Down
24 changes: 24 additions & 0 deletions examples/federation-with-user/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Create Federation with Certificate & add User

This example program demonstrates how to manage creating and deleting Federation with Certificate and assigning Users.

The part of deleting is disabled by `deleteAfterRun` variable.

## Running this example

Running this file will execute the following operations:

1. **Create Federation:** Create is used to create a new Federation.
2. **Create Certificate for Federation:** Create is used to create a new Certificate for Federation.
3. **Create federated User:** Create is used to create a new federated User.
4. **Update Federation:** Updates the Federation Name and Description.
5. **(Delete Federation):** _(disabled by default)_ Delete a just-created Federation on a previous step.

You should see an output like the following:
```
Step 1: Created Federation Name: federation_name ID: 1a2b3c...
Step 2: Created Certificate for Federation ID: 12345_3... Federation ID: 1a2b3c...
Step 3: Created federated User ID: 54321_2... Keystone ID: 1c2b3a...
Step 4: Updated Federation Name and Description
Step 5: Deleting Federation with ID: 1a2b3c...
```
3 changes: 3 additions & 0 deletions examples/federation-with-user/cert.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-----BEGIN CERTIFICATE-----
MIICmzCCAYMCBgGI6ANFczANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZtYXN0ZXIwHhcNMjMwNjIzMTEyNjQ4WhcNMzMwNjIzMTEyODI4WjARMQ8wDQYDVQQDDAZtYXN0ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC04rOaDpre/MucE3HXVCnAnpqIqQOeMn696AW2FATnI26x1BsxVAGjcrheAOIu+CxC28m48Ah4+SiTEk/u2X/WbGTd/1GZooz37cge0AWMQGyh8ysZRd6q06kg4QGD1iUtdQyHioMbSr9pPne2QQgSX5/gM9XDuA6dpG9Yv0PIPLFlk3BIUL1qEfUiYbDlrunkN/y4XromJaJPpgXKWraH194bqcgXGQLrCqicKwsRBoQJHg3ODWHjHFOwYODJ1XBsRcAue4J88PKiPV1tZNPVczMptrkqGBYTgOYGjKXGe5EH50RJE4/3Ynurz2s34DSDVJhJOYtGwpfeSuU3i3mVAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAGAweCuWJmJXMUdRtgoFIiu6BGotDX5sA/VOm4CRsEXV7/qnBagrAPkRz86KGm4lOPL0X+I13JQh4/OB1gxnPN+BXhNtCWCoj1wA3/BWjs1ow/gaVXzwdy+1mbc/sUBudsLq2Yqs54GgeYsTBKMVpSLKiRg1NebEFlqFmG2hjPzYg1QHL4VBusMQgqt7TTnOfGtdT3Ss9TKGRQ+iwfNL0BtSAKaTRdhNVU4lDYUs788Kw5od/uJj0wTICKO5/PrkX7Uy42+fyU+4SvJynPOy+M+z+s08JC9+eYXixfeeFG1nNWR+DIKXcXaSwNQW+8RweGbOJxQ2BoUKtl0NCHrvxJw=
-----END CERTIFICATE-----
117 changes: 117 additions & 0 deletions examples/federation-with-user/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package main

import (
"context"
"fmt"
"os"

"github.com/selectel/iam-go"
"github.com/selectel/iam-go/service/federations/saml"
"github.com/selectel/iam-go/service/federations/saml/certificates"
"github.com/selectel/iam-go/service/roles"
"github.com/selectel/iam-go/service/users"
)

var (
// KeystoneToken
token = "gAAAAA..."
deleteAfterRun = false

// Prefix to be added to User-Agent.
prefix = "iam-go"

federationName = "federation_name"
federationDescription = "federation_description"
updatedFederationName = "new_federation_name"
updatedFederationDescription = "new_federation_description"

certificateName = "certificate name"
certificateDescription = "certificate description"
certificateFileName = "cert.crt"

userEmail = "testmail@example.com"
userExternalID = "some_id"
)

func main() {
// Create a new IAM client.
iamClient, err := iam.New(
iam.WithAuthOpts(&iam.AuthOpts{KeystoneToken: token}),
iam.WithUserAgentPrefix(prefix),
)
if err != nil {
fmt.Println(err)
return
}

federationsAPI := iamClient.SAMLFederations
federationsCertificatesAPI := federationsAPI.Certificates
usersAPI := iamClient.Users

ctx := context.Background()

federation, err := federationsAPI.Create(ctx, saml.CreateRequest{
Name: federationName,
Description: federationDescription,
Issuer: "http://localhost:8080/realms/master",
SSOUrl: "http://localhost:8080/realms/master/protocol/saml",
SessionMaxAgeHours: 24,
SignAuthnRequests: true,
})
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("Step 1: Created Federation Name: %s ID: %s\n", federation.Name, federation.ID)

cert, err := os.ReadFile(certificateFileName)
if err != nil {
fmt.Println(err)
return
}

certificate, err := federationsCertificatesAPI.Create(ctx, federation.ID, certificates.CreateRequest{
Name: certificateName,
Description: certificateDescription,
Data: string(cert),
})
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("Step 2: Created Certificate for Federation ID: %s Federation ID: %s\n", certificate.ID, federation.ID)

user, err := usersAPI.Create(ctx, users.CreateRequest{
AuthType: users.Federated,
Email: userEmail,
Federation: &users.Federation{
ExternalID: userExternalID,
ID: federation.ID,
},
Roles: []roles.Role{{Scope: roles.Account, RoleName: roles.Reader}},
})
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("Step 3: Created federated User ID: %s Keystone ID: %s\n", user.ID, user.KeystoneID)

err = federationsAPI.Update(ctx, federation.ID, saml.UpdateRequest{
Name: updatedFederationName,
Description: &updatedFederationDescription,
})
if err != nil {
fmt.Println(err)
return
}
fmt.Println("Step 4: Updated Federation Name and Description")

if deleteAfterRun {
// Removing User and Federation Certificate is unnecessary because removal of Federation
// also deletes its Certificate and all attached Users
fmt.Printf("Step 5: Deleting Federation with ID: %s\n", federation.ID)
if err = federationsAPI.Delete(ctx, federation.ID); err != nil {
fmt.Println(err)
}
}
}
6 changes: 6 additions & 0 deletions iam.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/selectel/iam-go/iamerrors"
baseclient "github.com/selectel/iam-go/internal/client"
"github.com/selectel/iam-go/service/federations/saml"
"github.com/selectel/iam-go/service/groups"
"github.com/selectel/iam-go/service/s3credentials"
"github.com/selectel/iam-go/service/serviceusers"
Expand Down Expand Up @@ -57,6 +58,10 @@ type Client struct {

// S3Credentials instance is used to make requests against Selectel IAM API and manage S3 Credentials.
S3Credentials *s3credentials.Service

// SAMLFederations instance is used to make requests against Selectel IAM API and manage SAML Federations.
// It also contains Certificates service, which is used to manage certificates.
SAMLFederations *saml.Service
}

type AuthOpts struct {
Expand Down Expand Up @@ -128,6 +133,7 @@ func New(opts ...Option) (*Client, error) {
c.ServiceUsers = serviceusers.New(c.baseClient)
c.Groups = groups.New(c.baseClient)
c.S3Credentials = s3credentials.New(c.baseClient)
c.SAMLFederations = saml.New(c.baseClient)

return c, nil
}
Expand Down
34 changes: 19 additions & 15 deletions iam_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

"github.com/selectel/iam-go/iamerrors"
baseclient "github.com/selectel/iam-go/internal/client"
"github.com/selectel/iam-go/service/federations/saml"
"github.com/selectel/iam-go/service/groups"
"github.com/selectel/iam-go/service/s3credentials"
"github.com/selectel/iam-go/service/serviceusers"
Expand Down Expand Up @@ -57,11 +58,12 @@ func TestNew(t *testing.T) {
authOpts: &AuthOpts{
KeystoneToken: testToken,
},
baseClient: baseClient,
Users: users.New(baseClient),
ServiceUsers: serviceusers.New(baseClient),
Groups: groups.New(baseClient),
S3Credentials: s3credentials.New(baseClient),
baseClient: baseClient,
Users: users.New(baseClient),
ServiceUsers: serviceusers.New(baseClient),
Groups: groups.New(baseClient),
S3Credentials: s3credentials.New(baseClient),
SAMLFederations: saml.New(baseClient),
}
},
expectedError: nil,
Expand Down Expand Up @@ -99,11 +101,12 @@ func TestNew(t *testing.T) {
authOpts: &AuthOpts{
KeystoneToken: testToken,
},
baseClient: baseClient,
Users: users.New(baseClient),
ServiceUsers: serviceusers.New(baseClient),
Groups: groups.New(baseClient),
S3Credentials: s3credentials.New(baseClient),
baseClient: baseClient,
Users: users.New(baseClient),
ServiceUsers: serviceusers.New(baseClient),
Groups: groups.New(baseClient),
S3Credentials: s3credentials.New(baseClient),
SAMLFederations: saml.New(baseClient),
}
},
expectedError: nil,
Expand Down Expand Up @@ -134,11 +137,12 @@ func TestNew(t *testing.T) {
authOpts: &AuthOpts{
KeystoneToken: testToken,
},
baseClient: baseClient,
Users: users.New(baseClient),
ServiceUsers: serviceusers.New(baseClient),
Groups: groups.New(baseClient),
S3Credentials: s3credentials.New(baseClient),
baseClient: baseClient,
Users: users.New(baseClient),
ServiceUsers: serviceusers.New(baseClient),
Groups: groups.New(baseClient),
S3Credentials: s3credentials.New(baseClient),
SAMLFederations: saml.New(baseClient),
}
},
expectedError: nil,
Expand Down
75 changes: 45 additions & 30 deletions iamerrors/iamerrors.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ var (
ErrGroupNotFound = errors.New("GROUP_NOT_FOUND")
ErrUserOrGroupNotFound = errors.New("USER_OR_GROUP_NOT_FOUND")

ErrFederationNameRequired = errors.New("FEDERATION_NAME_REQUIRED")
ErrFederationIDRequired = errors.New("FEDERATION_ID_REQUIRED")
ErrFederationIssuerRequired = errors.New("FEDERATION_ISSUER_REQUIRED")
ErrFederationSSOURLRequired = errors.New("FEDERATION_SSO_URL_REQUIRED")
ErrFederationCertificateIDRequired = errors.New("FEDERATION_CERTIFICATE_ID_REQUIRED")
ErrFederationMaxAgeHoursRequired = errors.New("FEDERATION_MAX_AGE_HOURS_REQUIRED")
ErrFederationNotFound = errors.New("FEDERATION_NOT_FOUND")

ErrCredentialNameRequired = errors.New("CREDENTIAL_NAME_REQUIRED")
ErrCredentialAccessKeyRequired = errors.New("CREDENTIAL_ACCESS_KEY_REQUIRED")

Expand All @@ -48,36 +56,43 @@ var (

//nolint:gochecknoglobals // stringToError is not global.
stringToError = map[string]error{
ErrUserNotFound.Error(): ErrUserNotFound,
ErrClientNoAuthOpts.Error(): ErrClientNoAuthOpts,
ErrAuthTokenUnathorized.Error(): ErrAuthTokenUnathorized,
ErrDomainNotFound.Error(): ErrDomainNotFound,
ErrCredentialNotFound.Error(): ErrCredentialNotFound,
ErrProjectNotFound.Error(): ErrProjectNotFound,
ErrUserAlreadyExists.Error(): ErrUserAlreadyExists,
ErrRequestValidationError.Error(): ErrRequestValidationError,
ErrForbidden.Error(): ErrForbidden,
ErrUnauthorized.Error(): ErrUnauthorized,
ErrInternalServerError.Error(): ErrInternalServerError,
ErrCredentialNameRequired.Error(): ErrCredentialNameRequired,
ErrCredentialAccessKeyRequired.Error(): ErrCredentialAccessKeyRequired,
ErrUserIDRequired.Error(): ErrUserIDRequired,
ErrProjectIDRequired.Error(): ErrProjectIDRequired,
ErrGroupIDRequired.Error(): ErrGroupIDRequired,
ErrGroupUserIDsRequired.Error(): ErrGroupUserIDsRequired,
ErrGroupNameRequired.Error(): ErrGroupNameRequired,
ErrGroupRolesRequired.Error(): ErrGroupRolesRequired,
ErrGroupAlreadyExists.Error(): ErrGroupAlreadyExists,
ErrGroupNotFound.Error(): ErrGroupNotFound,
ErrUserOrGroupNotFound.Error(): ErrUserOrGroupNotFound,
ErrServiceUserNameRequired.Error(): ErrServiceUserNameRequired,
ErrServiceUserPasswordRequired.Error(): ErrServiceUserPasswordRequired,
ErrServiceUserRolesRequired.Error(): ErrServiceUserRolesRequired,
ErrUserRolesRequired.Error(): ErrUserRolesRequired,
ErrUserEmailRequired.Error(): ErrUserEmailRequired,
ErrInputDataRequired.Error(): ErrInputDataRequired,
ErrInternalAppError.Error(): ErrInternalAppError,
ErrUnknown.Error(): ErrUnknown,
ErrUserNotFound.Error(): ErrUserNotFound,
ErrClientNoAuthOpts.Error(): ErrClientNoAuthOpts,
ErrAuthTokenUnathorized.Error(): ErrAuthTokenUnathorized,
ErrDomainNotFound.Error(): ErrDomainNotFound,
ErrCredentialNotFound.Error(): ErrCredentialNotFound,
ErrProjectNotFound.Error(): ErrProjectNotFound,
ErrUserAlreadyExists.Error(): ErrUserAlreadyExists,
ErrRequestValidationError.Error(): ErrRequestValidationError,
ErrForbidden.Error(): ErrForbidden,
ErrUnauthorized.Error(): ErrUnauthorized,
ErrInternalServerError.Error(): ErrInternalServerError,
ErrCredentialNameRequired.Error(): ErrCredentialNameRequired,
ErrCredentialAccessKeyRequired.Error(): ErrCredentialAccessKeyRequired,
ErrUserIDRequired.Error(): ErrUserIDRequired,
ErrProjectIDRequired.Error(): ErrProjectIDRequired,
ErrGroupIDRequired.Error(): ErrGroupIDRequired,
ErrGroupUserIDsRequired.Error(): ErrGroupUserIDsRequired,
ErrGroupNameRequired.Error(): ErrGroupNameRequired,
ErrGroupRolesRequired.Error(): ErrGroupRolesRequired,
ErrGroupAlreadyExists.Error(): ErrGroupAlreadyExists,
ErrGroupNotFound.Error(): ErrGroupNotFound,
ErrFederationNameRequired.Error(): ErrFederationNameRequired,
ErrFederationIDRequired.Error(): ErrFederationIDRequired,
ErrFederationIssuerRequired.Error(): ErrFederationIssuerRequired,
ErrFederationSSOURLRequired.Error(): ErrFederationSSOURLRequired,
ErrFederationCertificateIDRequired.Error(): ErrFederationCertificateIDRequired,
ErrFederationNotFound.Error(): ErrFederationNotFound,
ErrFederationMaxAgeHoursRequired.Error(): ErrFederationMaxAgeHoursRequired,
ErrUserOrGroupNotFound.Error(): ErrUserOrGroupNotFound,
ErrServiceUserNameRequired.Error(): ErrServiceUserNameRequired,
ErrServiceUserPasswordRequired.Error(): ErrServiceUserPasswordRequired,
ErrServiceUserRolesRequired.Error(): ErrServiceUserRolesRequired,
ErrUserRolesRequired.Error(): ErrUserRolesRequired,
ErrUserEmailRequired.Error(): ErrUserEmailRequired,
ErrInputDataRequired.Error(): ErrInputDataRequired,
ErrInternalAppError.Error(): ErrInternalAppError,
ErrUnknown.Error(): ErrUnknown,
}
)

Expand Down
2 changes: 2 additions & 0 deletions service/federations/saml/certificates/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Package certificates provides a set of functions for interacting with the Selectel Federations Certificates API.
package certificates
Loading

0 comments on commit 7380a41

Please sign in to comment.