Skip to content

Commit

Permalink
Merge pull request #30 from daystram/dev
Browse files Browse the repository at this point in the history
  • Loading branch information
daystram authored Jan 23, 2021
2 parents 2b91842 + 3e2dada commit 2eab997
Show file tree
Hide file tree
Showing 40 changed files with 764 additions and 303 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ For `ratify-be`, a configuration file has to be bound to the container. Use the

To run `ratify-be`, run the following:
```console
$ docker run --name ratify-be -v /path_to_config/config.yaml:/config.yaml:ro -p 8080:8080 -d daystram/ratify:be
$ docker run --name ratify-be --env-file ./.env -p 8080:8080 -d daystram/ratify:be
```

And `ratify-fe` as follows:
Expand Down Expand Up @@ -90,8 +90,8 @@ services:
image: daystram/ratify:be
ports:
- 8080:8080
volumes:
- /path_to_config/config.yaml:/config.yaml
env_file:
- /path_to_env_file/.env
restart: unless-stopped
postgres:
image: postgres:13.1-alpine
Expand Down
1 change: 1 addition & 0 deletions ratify-be/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ ratify-be
config/config.yaml
.air.toml
docs/
.env

### Git ###
# Created by git for backups. To disable backups in Git:
Expand Down
23 changes: 23 additions & 0 deletions ratify-be/config/.example.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
HOSTNAME=localhost
PORT=8080
ENVIRONMENT=development
DEBUG=true

DB_HOSTNAME=localhost
DB_PORT=5432
DB_DATABASE=ratify
DB_USERNAME=ratify-be
DB_PASSWORD=ratify-be

REDIS_HOSTNAME=localhost
REDIS_PORT=6379
REDIS_PASSWORD=ratify-be
REDIS_DATABASE=0

SMTP_SERVER=localhost
SMTP_PORT=587
SMTP_USERNAME=no-reply@localhost
SMTP_PASSWORD=ratify-be

DOMAIN=ratify.daystram.com
SECRET=secret1234
59 changes: 29 additions & 30 deletions ratify-be/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,47 +35,46 @@ type Config struct {
}

func InitializeAppConfig() {
viper.SetConfigName("config.yaml")
viper.SetConfigType("yaml")
viper.SetConfigName(".env") // for development
viper.SetConfigType("env")
viper.AddConfigPath(".")
viper.AddConfigPath("./config")
viper.AddConfigPath("/")
err := viper.ReadInConfig()
if err != nil {
log.Fatalf("[INIT] Unable to load configuration. %+v\n", err)
}
viper.AllowEmptyEnv(true)
viper.AutomaticEnv()
_ = viper.ReadInConfig()

if AppConfig.Hostname = viper.GetString("hostname"); AppConfig.Hostname == "" {
log.Fatalln("[INIT] hostname is missing in config.yaml")
if AppConfig.Hostname = viper.GetString("HOSTNAME"); AppConfig.Hostname == "" {
log.Fatalln("[INIT] HOSTNAME is not set")
}
AppConfig.Port = viper.GetInt("port")
if AppConfig.Environment = viper.GetString("environment"); AppConfig.Environment == "" {
log.Fatalln("[INIT] environment is missing in config.yaml")
AppConfig.Port = viper.GetInt("PORT")
if AppConfig.Environment = viper.GetString("ENVIRONMENT"); AppConfig.Environment == "" {
log.Fatalln("[INIT] ENVIRONMENT is not set")
}
AppConfig.Debug = viper.GetBool("debug")
AppConfig.Debug = viper.GetBool("DEBUG")

AppConfig.DBHostname = viper.GetString("db_hostname")
AppConfig.DBPort = viper.GetInt("db_port")
AppConfig.DBDatabase = viper.GetString("db_database")
AppConfig.DBUsername = viper.GetString("db_username")
AppConfig.DBPassword = viper.GetString("db_password")
AppConfig.DBHostname = viper.GetString("DB_HOSTNAME")
AppConfig.DBPort = viper.GetInt("DB_PORT")
AppConfig.DBDatabase = viper.GetString("DB_DATABASE")
AppConfig.DBUsername = viper.GetString("DB_USERNAME")
AppConfig.DBPassword = viper.GetString("DB_PASSWORD")

AppConfig.RedisHostname = viper.GetString("redis_hostname")
AppConfig.RedisPort = viper.GetInt("redis_port")
AppConfig.RedisPassword = viper.GetString("redis_password")
AppConfig.RedisDatabase = viper.GetInt("redis_database")
AppConfig.RedisHostname = viper.GetString("REDIS_HOSTNAME")
AppConfig.RedisPort = viper.GetInt("REDIS_PORT")
AppConfig.RedisPassword = viper.GetString("REDIS_PASSWORD")
AppConfig.RedisDatabase = viper.GetInt("REDIS_DATABASE")

AppConfig.SMTPServer = viper.GetString("smtp_server")
AppConfig.SMTPPort = viper.GetInt("smtp_port")
AppConfig.SMTPUsername = viper.GetString("smtp_username")
AppConfig.SMTPPassword = viper.GetString("smtp_password")
AppConfig.SMTPServer = viper.GetString("SMTP_SERVER")
AppConfig.SMTPPort = viper.GetInt("SMTP_PORT")
AppConfig.SMTPUsername = viper.GetString("SMTP_USERNAME")
AppConfig.SMTPPassword = viper.GetString("SMTP_PASSWORD")

if AppConfig.Domain = viper.GetString("domain"); AppConfig.Domain == "" {
log.Fatalln("[INIT] domain is missing in config.yaml")
if AppConfig.Domain = viper.GetString("DOMAIN"); AppConfig.Domain == "" {
log.Fatalln("[INIT] DOMAIN is not set")
}
if AppConfig.JWTSecret = viper.GetString("secret"); AppConfig.JWTSecret == "" {
log.Fatalln("[INIT] secret is missing in config.yaml")
if AppConfig.JWTSecret = viper.GetString("SECRET"); AppConfig.JWTSecret == "" {
log.Fatalln("[INIT] SECRET is not set")
}

log.Printf("[INIT] Configuration loaded from %s\n", viper.ConfigFileUsed())
log.Printf("[INIT] Configuration loaded!")
}
23 changes: 0 additions & 23 deletions ratify-be/config/example.config.yaml

This file was deleted.

8 changes: 6 additions & 2 deletions ratify-be/constants/redis.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,18 @@ const (

RDTemAuthorizationCode = GrantTypeAuthorizationCode + defaultDelimiter
RDTemCodeChallenge = RDKeyCodeChallenge + defaultDelimiter
RDTemSessionToken = RDKeySessionToken + defaultDelimiter
RDTemSessionID = RDKeySessionID + defaultDelimiter
RDTemSessionList = RDKeySessionList + defaultDelimiter
RDTemSessionChild = RDKeySessionChild + defaultDelimiter
RDTemAccessToken = RDKeyAccessToken + defaultDelimiter
RDTemRefreshToken = RDKeyRefreshToken + defaultDelimiter
RDTemVerificationToken = RDKeyVerificationToken + defaultDelimiter
RDTemTOTPToken = RDKeyTOTPToken + defaultDelimiter

RDKeyCodeChallenge = "code_challenge"
RDKeySessionToken = "session_token"
RDKeySessionID = "session_id"
RDKeySessionList = "session_list"
RDKeySessionChild = "session_child"
RDKeyAccessToken = "access_token"
RDKeyRefreshToken = "refresh_token"
RDKeyVerificationToken = "refresh_token"
Expand Down
1 change: 1 addition & 0 deletions ratify-be/constants/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ package constants
const (
IsAuthenticatedKey = "is_authenticated"
UserSubjectKey = "user_subject"
SessionIDKey = "session_id"
IsSuperuserKey = "is_superuser"
)
5 changes: 3 additions & 2 deletions ratify-be/controllers/middleware/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,18 @@ func AuthMiddleware(c *gin.Context) {
}
var err error
var tokenInfo datatransfers.TokenIntrospection
if tokenInfo, err = handlers.Handler.IntrospectAccessToken(accessToken); err != nil || !tokenInfo.Active {
if tokenInfo, err = handlers.Handler.OAuthIntrospectAccessToken(accessToken); err != nil || !tokenInfo.Active {
c.AbortWithStatusJSON(http.StatusUnauthorized, datatransfers.APIResponse{Code: "invalid_token", Error: "invalid access_token"})
return
}
var user models.User
if user, err = handlers.Handler.RetrieveUserBySubject(tokenInfo.Subject); err != nil {
if user, err = handlers.Handler.UserGetOneBySubject(tokenInfo.Subject); err != nil {
c.AbortWithStatusJSON(http.StatusUnauthorized, datatransfers.APIResponse{Code: "invalid_token", Error: err.Error()})
return
}
c.Set(constants.IsAuthenticatedKey, true)
c.Set(constants.UserSubjectKey, user.Subject)
c.Set(constants.SessionIDKey, tokenInfo.SessionID)
c.Set(constants.IsSuperuserKey, user.Superuser)
c.Next()
}
67 changes: 37 additions & 30 deletions ratify-be/controllers/oauth/authorize.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,14 @@ func POSTAuthorize(c *gin.Context) {
}
// retrieve application
var application models.Application
if application, err = handlers.Handler.RetrieveApplication(authRequest.ClientID); err != nil {
c.JSON(http.StatusNotFound, datatransfers.APIResponse{Error: "application not found"})
if application, err = handlers.Handler.ApplicationGetOneByClientID(authRequest.ClientID); err != nil {
c.JSON(http.StatusBadRequest, datatransfers.APIResponse{Error: "application not found"})
return
}
// verify request credentials
// TODO: support comma-separated callback URLs
if authRequest.RedirectURI != "" && authRequest.RedirectURI != application.CallbackURL {
c.JSON(http.StatusBadRequest, datatransfers.APIResponse{Error: "not allowed callback_uri"})
return
}
flow := authRequest.Flow()
Expand All @@ -48,21 +54,25 @@ func POSTAuthorize(c *gin.Context) {
c.SetCookie(constants.SessionIDCookieKey, "", -1, "/oauth", "", true, true)
c.JSON(http.StatusUnauthorized, datatransfers.APIResponse{Code: errors.ErrAuthIncorrectCredentials.Error(), Error: "invalid cookie"})
return
} else {
// verify user session
if user, sessionID, err = handlers.Handler.CheckSession(sessionID); err != nil {
c.SetCookie(constants.SessionIDCookieKey, "", -1, "/oauth", "", true, true)
c.JSON(http.StatusUnauthorized, datatransfers.APIResponse{Code: errors.ErrAuthIncorrectCredentials.Error(), Error: "invalid session_id"})
return
}
}
// verify user session
var session datatransfers.SessionInfo
if session, err = handlers.Handler.SessionInfo(sessionID); err != nil {
c.SetCookie(constants.SessionIDCookieKey, "", -1, "/oauth", "", true, true)
c.JSON(http.StatusUnauthorized, datatransfers.APIResponse{Code: errors.ErrAuthIncorrectCredentials.Error(), Error: "invalid session_id"})
return
}
if user, err = handlers.Handler.UserGetOneBySubject(session.Subject); err != nil {
c.JSON(http.StatusUnauthorized, datatransfers.APIResponse{Code: errors.ErrAuthIncorrectCredentials.Error(), Error: "failed retrieveing user"})
return
}
} else {
// verify user login
if user, sessionID, err = handlers.Handler.AuthenticateUser(authRequest.UserLogin); err != nil {
if user, err = handlers.Handler.AuthAuthenticate(authRequest.UserLogin); err != nil {
if err == errors.ErrAuthIncorrectIdentifier {
c.JSON(http.StatusUnauthorized, datatransfers.APIResponse{Code: errors.ErrAuthIncorrectCredentials.Error(), Error: "incorrect credentials"})
} else if err == errors.ErrAuthIncorrectCredentials {
handlers.Handler.LogLogin(user, application, false, datatransfers.LogDetail{
handlers.Handler.LogInsertLogin(user, application, false, datatransfers.LogDetail{
Scope: constants.LogScopeOAuthAuthorize,
Detail: utils.ParseUserAgent(c),
})
Expand All @@ -76,27 +86,26 @@ func POSTAuthorize(c *gin.Context) {
}
return
}
}
// verify request credentials
// TODO: support comma-separated callback URLs
if authRequest.RedirectURI != "" && authRequest.RedirectURI != application.CallbackURL {
c.JSON(http.StatusUnauthorized, datatransfers.APIResponse{Error: "not allowed callback_uri"})
return
// initialize new session
if sessionID, err = handlers.Handler.SessionInitialize(user.Subject, utils.ParseUserAgent(c)); err != nil {
c.JSON(http.StatusUnauthorized, datatransfers.APIResponse{Error: "failed initializing new session"})
return
}
}
// generate authorization code
var authorizationCode string
if authorizationCode, err = handlers.Handler.GenerateAuthorizationCode(authRequest, user.Subject); err != nil {
if authorizationCode, err = handlers.Handler.OAuthGenerateAuthorizationCode(authRequest, user.Subject, sessionID); err != nil {
c.JSON(http.StatusUnauthorized, datatransfers.APIResponse{Error: "failed generating authorization_code"})
return
}
// note code challenge
if flow == constants.FlowAuthorizationCodeWithPKCE {
if err = handlers.Handler.StoreCodeChallenge(authorizationCode, authRequest.PKCEAuthFields); err != nil {
if err = handlers.Handler.OAuthStoreCodeChallenge(authorizationCode, authRequest.PKCEAuthFields); err != nil {
c.JSON(http.StatusUnauthorized, datatransfers.APIResponse{Error: "failed storing code_challenge"})
return
}
}
handlers.Handler.LogLogin(user, application, true, datatransfers.LogDetail{
handlers.Handler.LogInsertLogin(user, application, true, datatransfers.LogDetail{
Scope: constants.LogScopeOAuthAuthorize,
Detail: utils.ParseUserAgent(c),
})
Expand Down Expand Up @@ -132,27 +141,25 @@ func POSTLogout(c *gin.Context) {
}
// introspect access_token
var tokenInfo datatransfers.TokenIntrospection
if tokenInfo, err = handlers.Handler.IntrospectAccessToken(logoutRequest.AccessToken); err != nil || !tokenInfo.Active {
if tokenInfo, err = handlers.Handler.OAuthIntrospectAccessToken(logoutRequest.AccessToken); err != nil || !tokenInfo.Active {
c.AbortWithStatusJSON(http.StatusUnauthorized, datatransfers.APIResponse{Code: "invalid_token", Error: "invalid access_token"})
return
}
// retrieve application
if _, err = handlers.Handler.RetrieveApplication(logoutRequest.ClientID); err != nil {
if _, err = handlers.Handler.ApplicationGetOneByClientID(logoutRequest.ClientID); err != nil {
c.JSON(http.StatusNotFound, datatransfers.APIResponse{Error: "application not found"})
return
}
// revoke tokens
if err = handlers.Handler.RevokeTokens(tokenInfo.Subject, logoutRequest.ClientID, logoutRequest.Global); err != nil {
c.JSON(http.StatusInternalServerError, datatransfers.APIResponse{Error: "failed revoking tokens"})
// revoke token
if err = handlers.Handler.OAuthRevokeAccessToken(logoutRequest.AccessToken); err != nil {
c.JSON(http.StatusInternalServerError, datatransfers.APIResponse{Error: "failed revoking access_token"})
return
}
// revoke current session if global (global: all access_token spawned from current session is revoked)
if logoutRequest.Global {
var sessionID string
if sessionID, err = c.Cookie(constants.SessionIDCookieKey); err == nil {
if err = handlers.Handler.ClearSession(sessionID); err != nil {
log.Printf("failed clearing session. %v", err)
if err = handlers.Handler.SessionRevoke(c.GetString(constants.SessionIDKey)); err != nil {
log.Printf("failed revoking session. %v", err)
}
}
c.SetCookie(constants.SessionIDCookieKey, "", -1, "/oauth", "", true, true)
}
c.JSON(http.StatusOK, datatransfers.APIResponse{})
Expand Down
Loading

0 comments on commit 2eab997

Please sign in to comment.