diff --git a/server/constants/db_types.go b/server/constants/db_types.go index 66510d3..30ae23e 100644 --- a/server/constants/db_types.go +++ b/server/constants/db_types.go @@ -18,5 +18,5 @@ const ( // DbTypeMariaDB is the mariadb database type DbTypeMariaDB = "mariadb" // DbTypeCassandra is the cassandra database type - DbTypeCassandra = "cassandra" + DbTypeCassandraDB = "cassandradb" ) diff --git a/server/db/db.go b/server/db/db.go index 17fdbec..70b1033 100644 --- a/server/db/db.go +++ b/server/db/db.go @@ -4,6 +4,7 @@ import ( "github.com/authorizerdev/authorizer/server/constants" "github.com/authorizerdev/authorizer/server/db/providers" "github.com/authorizerdev/authorizer/server/db/providers/arangodb" + "github.com/authorizerdev/authorizer/server/db/providers/cassandradb" "github.com/authorizerdev/authorizer/server/db/providers/mongodb" "github.com/authorizerdev/authorizer/server/db/providers/sql" "github.com/authorizerdev/authorizer/server/envstore" @@ -15,9 +16,10 @@ var Provider providers.Provider func InitDB() error { var err error - isSQL := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseType) != constants.DbTypeArangodb && envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseType) != constants.DbTypeMongodb + isSQL := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseType) != constants.DbTypeArangodb && envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseType) != constants.DbTypeMongodb && envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseType) != constants.DbTypeCassandraDB isArangoDB := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseType) == constants.DbTypeArangodb isMongoDB := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseType) == constants.DbTypeMongodb + isCassandra := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseType) == constants.DbTypeCassandraDB if isSQL { Provider, err = sql.NewProvider() @@ -40,5 +42,12 @@ func InitDB() error { } } + if isCassandra { + Provider, err = cassandradb.NewProvider() + if err != nil { + return err + } + } + return nil } diff --git a/server/db/models/env.go b/server/db/models/env.go index e506fda..959284a 100644 --- a/server/db/models/env.go +++ b/server/db/models/env.go @@ -1,5 +1,7 @@ package models +// Note: any change here should be reflected in providers/casandra/provider.go as it does not have model support in collection creation + // Env model for db type Env struct { Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty"` // for arangodb diff --git a/server/db/models/session.go b/server/db/models/session.go index 59169f1..a495135 100644 --- a/server/db/models/session.go +++ b/server/db/models/session.go @@ -1,5 +1,7 @@ package models +// Note: any change here should be reflected in providers/casandra/provider.go as it does not have model support in collection creation + // Session model for db type Session struct { Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty"` // for arangodb diff --git a/server/db/models/user.go b/server/db/models/user.go index 38b1fb1..e072650 100644 --- a/server/db/models/user.go +++ b/server/db/models/user.go @@ -6,6 +6,8 @@ import ( "github.com/authorizerdev/authorizer/server/graph/model" ) +// Note: any change here should be reflected in providers/casandra/provider.go as it does not have model support in collection creation + // User model for db type User struct { Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty"` // for arangodb @@ -25,9 +27,9 @@ type User struct { PhoneNumberVerifiedAt *int64 `json:"phone_number_verified_at" bson:"phone_number_verified_at" cql:"phone_number_verified_at"` Picture *string `gorm:"type:text" json:"picture" bson:"picture" cql:"picture"` Roles string `json:"roles" bson:"roles" cql:"roles"` + RevokedTimestamp *int64 `json:"revoked_timestamp" bson:"revoked_timestamp" cql:"revoked_timestamp"` UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"` CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"` - RevokedTimestamp *int64 `json:"revoked_timestamp" bson:"revoked_timestamp" cql:"revoked_timestamp"` } func (user *User) AsAPIUser() *model.User { @@ -53,8 +55,8 @@ func (user *User) AsAPIUser() *model.User { PhoneNumberVerified: &isPhoneVerified, Picture: user.Picture, Roles: strings.Split(user.Roles, ","), + RevokedTimestamp: revokedTimestamp, CreatedAt: &createdAt, UpdatedAt: &updatedAt, - RevokedTimestamp: revokedTimestamp, } } diff --git a/server/db/models/verification_requests.go b/server/db/models/verification_requests.go index 5addba3..afd9ad7 100644 --- a/server/db/models/verification_requests.go +++ b/server/db/models/verification_requests.go @@ -2,18 +2,20 @@ package models import "github.com/authorizerdev/authorizer/server/graph/model" +// Note: any change here should be reflected in providers/casandra/provider.go as it does not have model support in collection creation + // VerificationRequest model for db type VerificationRequest struct { Key string `json:"_key,omitempty" bson:"_key" cql:"_key,omitempty"` // for arangodb ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id"` - Token string `gorm:"type:text" json:"token" bson:"token" cql:"token"` + Token string `gorm:"type:text" json:"token" bson:"token" cql:"jwt_token"` // token is reserved keyword in cassandra Identifier string `gorm:"uniqueIndex:idx_email_identifier;type:varchar(64)" json:"identifier" bson:"identifier" cql:"identifier"` ExpiresAt int64 `json:"expires_at" bson:"expires_at" cql:"expires_at"` - CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"` - UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"` Email string `gorm:"uniqueIndex:idx_email_identifier;type:varchar(256)" json:"email" bson:"email" cql:"email"` Nonce string `gorm:"type:text" json:"nonce" bson:"nonce" cql:"nonce"` RedirectURI string `gorm:"type:text" json:"redirect_uri" bson:"redirect_uri" cql:"redirect_uri"` + CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"` + UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"` } func (v *VerificationRequest) AsAPIVerificationRequest() *model.VerificationRequest { @@ -30,10 +32,10 @@ func (v *VerificationRequest) AsAPIVerificationRequest() *model.VerificationRequ Token: &token, Identifier: &identifier, Expires: &expires, - CreatedAt: &createdAt, - UpdatedAt: &updatedAt, Email: &email, Nonce: &nonce, RedirectURI: &redirectURI, + CreatedAt: &createdAt, + UpdatedAt: &updatedAt, } } diff --git a/server/db/providers/cassandra/provider.go b/server/db/providers/cassandra/provider.go deleted file mode 100644 index fed9339..0000000 --- a/server/db/providers/cassandra/provider.go +++ /dev/null @@ -1,52 +0,0 @@ -package cassandra - -import ( - "fmt" - "log" - "strings" - - "github.com/authorizerdev/authorizer/server/constants" - "github.com/authorizerdev/authorizer/server/envstore" - cansandraDriver "github.com/gocql/gocql" -) - -type provider struct { - db *cansandraDriver.Session -} - -func (s provider) createTableIfNotExists(tableName string, fields []string) error { - keySpace := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseName) - return s.db.Query(fmt.Sprintf(`CREATE TABLE IF NOT EXISTS %s (%s)`, keySpace+"."+tableName, strings.Join(fields, ", "))).Exec() -} - -// NewProvider to initialize arangodb connection -func NewProvider() (*provider, error) { - dbURL := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseURL) - keySpace := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseName) - cassandraClient := cansandraDriver.NewCluster(dbURL) - cassandraClient.Keyspace = keySpace - cassandraClient.RetryPolicy = &cansandraDriver.SimpleRetryPolicy{ - NumRetries: 3, - } - cassandraClient.Consistency = cansandraDriver.Quorum - - session, err := cassandraClient.CreateSession() - if err != nil { - log.Println("Error while creating connection to cassandra db", err) - return nil, err - } - - q := fmt.Sprintf("CREATE KEYSPACE IF NOT EXISTS %s WITH REPLICATION = {'class': 'SimpleStrategy', 'replication_factor':1}", - keySpace) - err = session.Query(q).Exec() - if err != nil { - log.Println("Unable to create keyspace:", err) - return nil, err - } - - // make sure collections are present - - return &provider{ - db: session, - }, err -} diff --git a/server/db/providers/cassandra/env.go b/server/db/providers/cassandradb/env.go similarity index 97% rename from server/db/providers/cassandra/env.go rename to server/db/providers/cassandradb/env.go index a471da0..9e19f3e 100644 --- a/server/db/providers/cassandra/env.go +++ b/server/db/providers/cassandradb/env.go @@ -1,4 +1,4 @@ -package cassandra +package cassandradb import ( "time" diff --git a/server/db/providers/cassandradb/provider.go b/server/db/providers/cassandradb/provider.go new file mode 100644 index 0000000..0c8ea4e --- /dev/null +++ b/server/db/providers/cassandradb/provider.go @@ -0,0 +1,76 @@ +package cassandradb + +import ( + "fmt" + "log" + + "github.com/authorizerdev/authorizer/server/constants" + "github.com/authorizerdev/authorizer/server/db/models" + "github.com/authorizerdev/authorizer/server/envstore" + cansandraDriver "github.com/gocql/gocql" +) + +type provider struct { + db *cansandraDriver.Session +} + +// NewProvider to initialize arangodb connection +func NewProvider() (*provider, error) { + dbURL := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseURL) + keySpace := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseName) + cassandraClient := cansandraDriver.NewCluster(dbURL) + // cassandraClient.Keyspace = keySpace + cassandraClient.RetryPolicy = &cansandraDriver.SimpleRetryPolicy{ + NumRetries: 3, + } + cassandraClient.Consistency = cansandraDriver.Quorum + + session, err := cassandraClient.CreateSession() + if err != nil { + log.Println("Error while creating connection to cassandra db", err) + return nil, err + } + + keyspaceQuery := fmt.Sprintf("CREATE KEYSPACE IF NOT EXISTS %s WITH REPLICATION = {'class': 'SimpleStrategy', 'replication_factor':1}", + keySpace) + err = session.Query(keyspaceQuery).Exec() + if err != nil { + log.Println("Unable to create keyspace:", err) + return nil, err + } + + // make sure collections are present + envCollectionQuery := fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s.%s (id text, env text, hash text, updated_at bigint, created_at bigint, PRIMARY KEY (id))", + keySpace, models.Collections.Env) + err = session.Query(envCollectionQuery).Exec() + if err != nil { + log.Println("Unable to create env collection:", err) + return nil, err + } + + sessionCollectionQuery := fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s.%s (id text, user_id text, user_agent text, ip text, updated_at bigint, created_at bigint, PRIMARY KEY (id))", keySpace, models.Collections.Session) + err = session.Query(sessionCollectionQuery).Exec() + if err != nil { + log.Println("Unable to create session collection:", err) + return nil, err + } + + userCollectionQuery := fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s.%s (id text, email text, email_verified_at bigint, password text, signup_methods text, given_name text, family_name text, middle_name text, nick_name text, gender text, birthdate text, phone_number text, phone_number_verified_at bigint, picture text, roles text, updated_at bigint, created_at bigint, revoked_timestamp bigint, PRIMARY KEY (id, email))", keySpace, models.Collections.User) + err = session.Query(userCollectionQuery).Exec() + if err != nil { + log.Println("Unable to create user collection:", err) + return nil, err + } + + // token is reserved keyword in cassandra, hence we need to use jwt_token + verificationRequestCollectionQuery := fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s.%s (id text, jwt_token text, identifier text, expires_at bigint, email text, nonce text, redirect_uri text, created_at bigint, updated_at bigint, PRIMARY KEY (id, identifier, email))", keySpace, models.Collections.VerificationRequest) + err = session.Query(verificationRequestCollectionQuery).Exec() + if err != nil { + log.Println("Unable to create verification request collection:", err) + return nil, err + } + + return &provider{ + db: session, + }, err +} diff --git a/server/db/providers/cassandra/session.go b/server/db/providers/cassandradb/session.go similarity index 96% rename from server/db/providers/cassandra/session.go rename to server/db/providers/cassandradb/session.go index 54f66dc..e60c45f 100644 --- a/server/db/providers/cassandra/session.go +++ b/server/db/providers/cassandradb/session.go @@ -1,4 +1,4 @@ -package cassandra +package cassandradb import ( "time" diff --git a/server/db/providers/cassandra/user.go b/server/db/providers/cassandradb/user.go similarity index 98% rename from server/db/providers/cassandra/user.go rename to server/db/providers/cassandradb/user.go index dc57118..2512a32 100644 --- a/server/db/providers/cassandra/user.go +++ b/server/db/providers/cassandradb/user.go @@ -1,4 +1,4 @@ -package cassandra +package cassandradb import ( "strings" diff --git a/server/db/providers/cassandra/verification_requests.go b/server/db/providers/cassandradb/verification_requests.go similarity index 98% rename from server/db/providers/cassandra/verification_requests.go rename to server/db/providers/cassandradb/verification_requests.go index 398c977..087ef4a 100644 --- a/server/db/providers/cassandra/verification_requests.go +++ b/server/db/providers/cassandradb/verification_requests.go @@ -1,4 +1,4 @@ -package cassandra +package cassandradb import ( "time"