Skip to content

Commit

Permalink
feat: add login using PAT (#246)
Browse files Browse the repository at this point in the history
  • Loading branch information
sudohikumar authored Jan 21, 2021
1 parent db01ce5 commit 3f64352
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 3 deletions.
35 changes: 32 additions & 3 deletions api/authenticate.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,18 @@ package api
import (
"encoding/base64"
"fmt"
"net/http"

"github.com/go-vela/server/database"
"github.com/go-vela/server/router/middleware/token"
"github.com/go-vela/server/source"
"github.com/go-vela/server/util"
"github.com/go-vela/types"
"github.com/google/uuid"
"github.com/sirupsen/logrus"
"net/http"

"github.com/go-vela/types/library"

"github.com/gin-gonic/gin"
"github.com/google/uuid"
)

// swagger:operation GET /authenticate authenticate GetAuthenticate
Expand Down Expand Up @@ -211,3 +210,33 @@ func AuthenticateType(c *gin.Context) {

c.Redirect(http.StatusTemporaryRedirect, r)
}

// AuthenticateToken represents the API handler to
// process a user logging in using PAT to Vela from
// the API
func AuthenticateToken(c *gin.Context) {
newUser, err := source.FromContext(c).AuthenticateToken(c.Writer, c.Request)
if err != nil {
retErr := fmt.Errorf("unable to authenticate user: %w", err)

util.HandleError(c, http.StatusUnauthorized, retErr)

return
}

newUser.SetActive(true)

// We don't need refresh token for this scenario
// We only need access token and are configured based on the config defined
m := c.MustGet("metadata").(*types.Metadata)
at, err := token.CreateAccessToken(newUser, m.Vela.AccessTokenDuration)

if err != nil {
retErr := fmt.Errorf("unable to compose token for user %s: %w", newUser.GetName(), err)

util.HandleError(c, http.StatusServiceUnavailable, retErr)
}

// return the user with their jwt access token
c.JSON(http.StatusOK, library.Login{Token: &at})
}
1 change: 1 addition & 0 deletions router/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ func Load(options ...gin.HandlerFunc) *gin.Engine {
authenticate.GET("", api.Authenticate)
authenticate.GET("/:type", api.AuthenticateType)
authenticate.GET("/:type/:port", api.AuthenticateType)
authenticate.POST("/token", api.AuthenticateToken)
}

// API endpoints
Expand Down
18 changes: 18 additions & 0 deletions source/github/authentication.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,21 @@ func (c *client) Authenticate(w http.ResponseWriter, r *http.Request, oAuthState
Token: &token.AccessToken,
}, nil
}

// AuthenticateToken completes the authentication workflow
// for the session and returns the remote user details.
func (c *client) AuthenticateToken(w http.ResponseWriter, r *http.Request) (*library.User, error) {
logrus.Trace("Authenticating user via token")

token := r.Header.Get("Token")

u, err := c.Authorize(token)
if err != nil {
return nil, err
}

return &library.User{
Name: &u,
Token: &token,
}, nil
}
77 changes: 77 additions & 0 deletions source/github/authentication_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,83 @@ func TestGithub_Login(t *testing.T) {
}
}

func TestGithub_Authenticate_Token(t *testing.T) {
// setup context
gin.SetMode(gin.TestMode)

resp := httptest.NewRecorder()
context, engine := gin.CreateTestContext(resp)
context.Request, _ = http.NewRequest(http.MethodPost, "/authenticate/token", nil)
context.Request.Header.Set("Token", "foo")

engine.GET("/api/v3/user", func(c *gin.Context) {
c.Header("Content-Type", "application/json")
c.Status(http.StatusOK)
c.File("testdata/user.json")
})

s := httptest.NewServer(engine)
defer s.Close()

// setup types
want := new(library.User)
want.SetName("octocat")
want.SetToken("foo")

client, _ := NewTest(s.URL)

// run test
got, err := client.AuthenticateToken(context.Writer, context.Request)

if resp.Code != http.StatusOK {
t.Errorf("Authenticate returned %v, want %v", resp.Code, http.StatusOK)
}

if err != nil {
t.Errorf("Authenticate returned err: %v", err)
}

if !reflect.DeepEqual(got, want) {
t.Errorf("Authenticate is %v, want %v", got, want)
}
}

func TestGithub_Authenticate_Invalid_Token(t *testing.T) {
// setup context
gin.SetMode(gin.TestMode)

resp := httptest.NewRecorder()
context, engine := gin.CreateTestContext(resp)
context.Request, _ = http.NewRequest(http.MethodPost, "/authenticate/token", nil)
context.Request.Header.Set("Token", "foo")

// setup mock server
engine.GET("/api/v3/user", func(c *gin.Context) {
c.Status(http.StatusNotFound)
})

s := httptest.NewServer(engine)
defer s.Close()

// setup client
client, _ := NewTest(s.URL)

// run test
got, err := client.AuthenticateToken(context.Writer, context.Request)

if resp.Code != http.StatusOK {
t.Errorf("Authenticate returned %v, want %v", resp.Code, http.StatusOK)
}

if err == nil {
t.Errorf("Authenticate did not return err")
}

if got != nil {
t.Errorf("Authenticate is %v, want nil", got)
}
}

func TestGithub_LoginWCreds(t *testing.T) {
// setup context
gin.SetMode(gin.TestMode)
Expand Down
5 changes: 5 additions & 0 deletions source/source.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ type Service interface {
// Authenticate defines a function that completes
// the OAuth workflow for the session.
Authenticate(http.ResponseWriter, *http.Request, string) (*library.User, error)

// AuthenticateToken defines a function that completes
// the OAuth workflow for the session using PAT Token
AuthenticateToken(http.ResponseWriter, *http.Request) (*library.User, error)

// Login defines a function that begins
// the OAuth workflow for the session.
Login(http.ResponseWriter, *http.Request) (string, error)
Expand Down

0 comments on commit 3f64352

Please sign in to comment.