Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add login using PAT #246

Merged
merged 8 commits into from
Jan 21, 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
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