Browse Source

oauth token views and key-exchange with gitea, gitea: push release builds to gitea, using oauth, separate build-script for releases

master
Stefan Naumann 5 months ago
parent
commit
e0d015cd3b
17 changed files with 629 additions and 75 deletions
  1. +32
    -4
      build/build.go
  2. +10
    -6
      build/worker.go
  3. +6
    -1
      core/config.go
  4. +38
    -2
      core/models.go
  5. +24
    -42
      hook/gitea.go
  6. +94
    -0
      hook/hook.go
  7. +30
    -0
      static/css/style.css
  8. +25
    -0
      views/admin_info.html
  9. +7
    -1
      views/integration.html
  10. +4
    -4
      views/layouts/nav.html
  11. +44
    -0
      views/oauth_edit.html
  12. +62
    -0
      views/oauth_overview.html
  13. +15
    -5
      views/repo_edit.html
  14. +157
    -0
      web/oauth.go
  15. +70
    -9
      web/repo.go
  16. +9
    -0
      web/routing.go
  17. +2
    -1
      web/webhook.go

+ 32
- 4
build/build.go View File

@@ -4,6 +4,7 @@
package build

import (
"fmt"
"time"
"mvoCI/core"

@@ -21,28 +22,55 @@ func BuildNow ( r core.Repository, db *gorm.DB ) uint {
}

func BuildRelease ( db *gorm.DB, r core.Repository, branch string, sha string, author string, url string, message string, event string, api string, apiUrl string ) uint {
var bs core.BuildScript
db.Where("repository_id=? AND event_type = ?", r.ID, event ).First (&bs );

fmt.Println("BSID: ", bs.ID);
if bs.ID <= 0 {
// fallback: push event
if event != "push" {
db.Where("repository_id=? AND event_type = ?", r.ID, "push" ).First (&bs );
if bs.ID <= 0 {
return 0;
}
}
}

b := core.Build { Log: "", CommitSha: sha, FinishedAt: time.Unix(0,0),
Branch: branch, Repository: r, Status : "enqueued",
CommitAuthor: author, CommitUrl: url, CommitMessage: message,
Event: event, Api: api, ApiUrl: apiUrl }
Event: event, Api: api, ApiUrl: apiUrl, BuildScript: bs }
db.Create ( &b )
EnqueueBuild ( b )
return b.ID;
}

func BuildSpecific2 ( db *gorm.DB, r core.Repository, branch string, sha string, author string, url string, message string, event string ) uint {
var bs core.BuildScript
db.Where("repository_id=? AND event_type = ?", r.ID, event ).First (&bs );

if bs.ID <= 0 {
return 0;
}
b := core.Build { Log: "", CommitSha: sha, FinishedAt: time.Unix(0,0),
Branch: branch, Repository: r, Status : "enqueued",
CommitAuthor: author, CommitUrl: url, CommitMessage: message,
Event: event }
Event: event, BuildScript: bs }
db.Create ( &b )
EnqueueBuild ( b )
return b.ID;
}

func BuildSpecific ( db *gorm.DB, r core.Repository, branch string, sha string ) uint {
var bs core.BuildScript
db.Where("repository_id=? AND event_type = ?", r.ID, "push" ).First (&bs );

if bs.ID <= 0 {
return 0;
}

b := core.Build { Log: "", CommitSha: sha, FinishedAt: time.Unix(0,0),
Branch: branch, Repository: r, Status : "enqueued" }
Branch: branch, Repository: r, Status : "enqueued", BuildScript: bs }
db.Create ( &b )
EnqueueBuild ( b )
return b.ID;
@@ -66,7 +94,7 @@ func ReBuild ( b core.Build, db *gorm.DB ) uint {
FinishedAt: time.Unix(0,0), Branch: b.Branch,
Repository: r, Status : "enqueued", Event: "User Rebuild",
CommitAuthor: b.CommitAuthor, CommitMessage: b.CommitMessage,
CommitUrl: b.CommitUrl }
CommitUrl: b.CommitUrl, BuildScript: b.BuildScript }
db.Create ( &bn )
EnqueueBuild ( bn );
return bn.ID


+ 10
- 6
build/worker.go View File

@@ -197,7 +197,9 @@ func WorkerGoGet ( b *core.Build ) (*mockerBuilder, bool) {
func WorkerGoBuild ( b *core.Build, log *mockerBuilder ) (*mockerBuilder, bool) {
log.append ( "> Starting Build ... \n")

buildScript := []byte( "set -e\n" + strings.ReplaceAll ( b.Repository.BuildScript, "\r", "\n") )
var ShellScript string
ShellScript = b.BuildScript.ShellScript
buildScript := []byte( "set -e\n" + strings.ReplaceAll ( ShellScript, "\r", "\n") )
dir := c.cfg.Directory.Repo + dirNameFromRepo ( b.Repository )
buildScriptName := dir + "/mvoBuild.sh"
err := ioutil.WriteFile ( buildScriptName, buildScript, 0777 )
@@ -206,7 +208,7 @@ func WorkerGoBuild ( b *core.Build, log *mockerBuilder ) (*mockerBuilder, bool)
return log, false
}

l, e := execBuildScript ( "mvoBuild.sh", dir )
l, e := execBuildScript ( "mvoBuild.sh", dir, b.CommitSha )
log.append ( l )
if e != true {
return log, false
@@ -253,11 +255,11 @@ func WorkerGoZip ( b *core.Build, log *mockerBuilder ) (*mockerBuilder, bool) {
func WorkerPushRelease ( b *core.Build, log *mockerBuilder ) (*mockerBuilder, bool ) {
switch b.Api {
case "gitea":
err := hook.GiteaPushRelease ( b.ApiUrl, c.cfg.Directory.Build + "/" + b.Zip, c.cfg.GiteaApi );
err := hook.GiteaPushRelease ( b, b.ApiUrl, c.cfg.Directory.Build + "/" + b.Zip, c.cfg, c.db );
if err != nil {
core.Console.Log("Error pushing the artifact: ", err );
log.append ("Error pushing the artifact: ", err.Error() );
} else {
core.Console.Log("Pushing artifact should have worked. Maybe.");
log.append ("Pushing artifact should have worked.");
}
}
return WorkerGoCleanup ( b, log );
@@ -273,10 +275,12 @@ func WorkerGoCleanup ( b *core.Build, log *mockerBuilder ) (*mockerBuilder, bool
}

// execute the shell build script
func execBuildScript ( file string, dir string ) (string, bool) {
func execBuildScript ( file string, dir string, buildVersion string ) (string, bool) {
var l mockerBuilder;
l.append ( "> Executing build script '", file, "'\n" )
cmd := exec.Command ( "bash", file )
cmd.Env = os.Environ();
cmd.Env = append ( cmd.Env, "VERSION="+buildVersion );
cmd.Dir = dir;
out, err := cmd.CombinedOutput();
l.append ( string(out) )


+ 6
- 1
core/config.go View File

@@ -53,6 +53,11 @@ type ConfigApi struct {
ListingEnable bool
}

type ConfigGiteaHook struct {
OauthClientId string
OauthClientSecret string
}

// global config structure
type Config struct {
AppTitle string
@@ -75,7 +80,7 @@ type Config struct {
PublicEnable bool
Api ConfigApi
Build ConfigBuild
GiteaApi string
GiteaApi ConfigGiteaHook
}

func ConfigDefault () Config {


+ 38
- 2
core/models.go View File

@@ -52,6 +52,21 @@ type LoginToken struct {
Secret string // a secret string
}

type OauthToken struct {
ID uint `gorm:"primary_key"`
CreatedAt time.Time
UpdatedAt time.Time
State string
Name string
Api string
Url string
UserID uint
ClientId string
ClientSecret string
ClientToken string
RedirectUri string
}

// structure representing a Repository
type Repository struct {
ID uint `gorm:"primary_key"`
@@ -62,7 +77,8 @@ type Repository struct {
CloneUrl string
BuildCount uint
DefaultBranch string
BuildScript string
BuildScript string // OBSOLETE! WILL BE REMOVED SOON
BuildScripts []BuildScript
WebHookEnable bool
LocalBuildEnable bool
KeepBuilds bool
@@ -71,6 +87,16 @@ type Repository struct {
Public bool
}

type BuildScript struct {
ID uint `gorm:"primary_key"`
CreatedAt time.Time
UpdatedAt time.Time
EventType string
ShellScript string
RepositoryID uint
Repository Repository
}

// structure representing a Build of Repositories
type Build struct {
ID uint `gorm:"primary_key"` // database ID
@@ -91,6 +117,7 @@ type Build struct {
RepositoryID uint
Api string
ApiUrl string
BuildScript BuildScript
}

type WebHookLog struct {
@@ -153,7 +180,16 @@ func connection_settings ( cfg *Config ) string {
func DBConnect ( cfg *Config ) (*gorm.DB, error) {
db,err := gorm.Open( cfg.Database.Provider, connection_settings ( cfg ) );
if err == nil {
db.AutoMigrate ( &User{}, &Group{}, &LoginToken{}, &Repository{}, &Build{}, &WebHookLog{} );
db.AutoMigrate (
&User{},
&Group{},
&LoginToken{},
&Repository{},
&Build{},
&WebHookLog{},
&OauthToken{},
&BuildScript{},
);
if cfg.Debug {
db.Debug();
db.LogMode ( true );


+ 24
- 42
hook/gitea.go View File

@@ -1,59 +1,41 @@
package hook

import (
"fmt"
"io"
"os"
"bytes"
"time"
"errors"
"strings"
"golang.org/x/oauth2"
"strconv"
"net/http"
"io/ioutil"
"path/filepath"
"mime/multipart"
"mvoCI/core"
"encoding/json"
"github.com/jinzhu/gorm"
)

func GiteaPushRelease ( url string, zip string, GiteaApiKey string ) error {
url2 := url + "/assets?access_token=" + GiteaApiKey
fmt.Println("\n", url2, "\n");
formData := map[string]string{
"authorization": "token " + GiteaApiKey,
};
file, err := os.Open( zip )
if err != nil {
return err
}
defer file.Close()
func GiteaPushRelease ( b *core.Build, url string, zip string, cfg *core.Config, db *gorm.DB ) error {
// (0) Find a fitting Token in our OAuthToken Table
var ex []string = strings.Split ( url, "/api/v1/repos" );
var baseurl = ex[0];

body := &bytes.Buffer{}
writer := multipart.NewWriter( body )
part, err := writer.CreateFormFile( "attachment", filepath.Base( zip ) )
if err != nil {
return err
}
_, err = io.Copy( part, file )
if err != nil {
return err
}
for key, val := range formData {
_ = writer.WriteField(key, val)
}
var cred core.OauthToken
var tok oauth2.Token;
db.Where("api = ? AND url LIKE ? AND state = ?", "gitea", baseurl, "finished" ).First ( &cred );

err = writer.Close()
err := json.Unmarshal ( []byte(cred.ClientToken), &tok );
if err != nil {
return err
return err;
}

client := &http.Client{};
req, _ := http.NewRequest( "POST", url2, body )
req.Header.Set("Content-Type", writer.FormDataContentType())
resp, err := client.Do ( req )
if resp.StatusCode != 200 {
body, _ := ioutil.ReadAll ( resp.Body );
fmt.Println ("Message: ", string(body) );
return errors.New("Statuscode ("+strconv.Itoa(resp.StatusCode)+") != 200");
// (1) connect to gitea with oauth token
client := ConnectWithToken ( cred.Url, cred.ClientId, cred.ClientSecret, []string{}, &tok, cred.RedirectUri );

// (2) send request
resp, err := PostFile ( client, url + "/assets", map[string]string{}, "attachment", zip )
defer resp.Body.Close() ;
if resp.StatusCode < 200 || resp.StatusCode > 299 {
return errors.New ("Statuscode does not indicate success: " + strconv.Itoa(resp.StatusCode))
}

// (3) close everything
return nil;
}



+ 94
- 0
hook/hook.go View File

@@ -0,0 +1,94 @@
package hook

import (
"os"
"io"
"fmt"
"bytes"
"context"
"net/http"
"path/filepath"
"mime/multipart"
"golang.org/x/oauth2"
//"mvoCI/core"
)

var AppName = "mvoCI"
var AppWebsite = "http://localhost:4042"

func PostFile ( client *http.Client, url string, formData map[string]string, formFilename string, filename string ) ( *http.Response, error) {
file, err := os.Open( filename )
if err != nil {
return nil, err
}
defer file.Close()

body := &bytes.Buffer{}
writer := multipart.NewWriter( body )
part, err := writer.CreateFormFile( formFilename, filepath.Base( filename ) )
if err != nil {
return nil, err
}
_, err = io.Copy( part, file )
if err != nil {
return nil, err
}
for key, val := range formData {
_ = writer.WriteField(key, val)
}

err = writer.Close()
if err != nil {
return nil, err
}

req, _ := http.NewRequest( "POST", url, body )
req.Header.Set("Content-Type", writer.FormDataContentType())
return client.Do ( req )
}

// internal function to create the configuration for the OAuth2 library
func CreateConf ( server string, clientID string, clientSecret string, scopes []string, redirectUri string ) *oauth2.Config {
return &oauth2.Config{
ClientID: clientID,
ClientSecret: clientSecret,
Scopes: scopes,
RedirectURL: redirectUri,
Endpoint: oauth2.Endpoint{
AuthURL: server+"/login/oauth/authorize",
TokenURL: server+"/login/oauth/access_token",
},
}
}

// returns the OAuth2 authentification url, the user has to visit
func AuthUrl ( server string, clientID string, clientSecret string, scopes []string, redirectUri string ) string {
conf := CreateConf ( server, clientID, clientSecret, scopes, redirectUri )
url := conf.AuthCodeURL("", oauth2.AccessTypeOnline)
return url
}

// create a client-connection with a (hopefully) valid OAuth2 Auth Token
func ConnectWithToken ( server string, clientID string, clientSecret string, scopes []string, tok *oauth2.Token, redirectUri string ) (*http.Client) {
ctx := context.Background()
conf := CreateConf ( server, clientID, clientSecret, scopes, redirectUri )

client := conf.Client(ctx, tok)
return client
}

// create a client-connection with an auth-code from the user, to get a token
func Connect ( server string, clientID string, clientSecret string, scopes []string, redirectUri string, authCode string ) (*http.Client, *oauth2.Token, error) {
ctx := context.Background()
conf := CreateConf ( server, clientID, clientSecret, scopes, redirectUri )

tok, err := conf.Exchange(ctx, authCode)
if err != nil {
//die()
fmt.Printf("Error: %+v\n", err)
return nil, nil, err
} else {
client := conf.Client(ctx, tok)
return client, tok, nil
}
}

+ 30
- 0
static/css/style.css View File

@@ -812,6 +812,36 @@ a .block:hover, .block-link .block:hover {
animation: spin 1s linear infinite;
}

.oauth {
width: 48px; height: 38px;
background-repeat: no-repeat!important;
background-position-y: -5px!important;
}

.oauth-gitea {
background: url('/static/img/gitea.png');
}

.oauth-gitlab {
background: url('/static/img/gitlab.png');
}

.oauth-gogs {
background: url('/static/img/gogs.png');
}

.oauth-github {
background: url('/static/img/github.png');
}

.oauth-gitbucket {
background: url('/static/img/gitbucket.png');
}

.oauth-bitbucket {
background: url('/static/img/bitbucket.png');
}

.dashboard-icons {
width: 110px;
text-align: right


+ 25
- 0
views/admin_info.html View File

@@ -0,0 +1,25 @@
{{ define "title" }}Info{{ end }}

{{ define "toolbar" }}
<li>
<a href="/admin/webhook">
<i class="fa fa-chevron-left"></i>Webhook-Logs
</a>
</li>
<li>
<a href="/admin/info">
<i class="fa fa-clock"></i>Info
</a>
</li>
{{ end }}

{{ define "content" }}
<h2>Information</h2>
<ul>
<li>Build-Time: {{ .BuildTime }}
<li>Build-Compiler: {{ .Compiler }}
<li>Version: {{ .Version }}
<li>Git-Hash: {{ .GitHash }}
</ul>

{{ end }}

+ 7
- 1
views/integration.html View File

@@ -1,4 +1,10 @@
{{ define "toolbar" }}{{ end }}
{{ define "toolbar" }}
<li>
<a href="/oauth">
<i class="fa fa-share-alt"></i>OAuth Token
</a>
</li>
{{ end }}

{{ define "content" }}
<h2>Webhook</h2>


+ 4
- 4
views/layouts/nav.html View File

@@ -17,15 +17,15 @@
<li class="{{ if eq .navPage "repo"}}active{{end}}">
<a href="/repo">Repositories</a>
</li>
<li class="{{ if eq .navPage "integration"}}active{{end}}">
<a href="/integration">Integration</a>
</li>
{{ if .user.Superuser }}
<li class="{{ if eq .navPage "user"}}active{{end}}">
<a href="/user">Users</a>
</li>
<li class="{{ if eq .navPage "integration"}}active{{end}}">
<a href="/integration">Integration</a>
</li>
<li class="{{ if eq .navPage "admin"}}active{{end}}">
<a href="/admin/webhooklog">Admin</a>
<a href="/admin/info">Admin</a>
</li>
{{ end }}
<li class="{{ if eq .navPage "profile"}}active{{end}}">


+ 44
- 0
views/oauth_edit.html View File

@@ -0,0 +1,44 @@
{{ define "title" }}{{end}}
{{ define "toolbar" }}
<li>
<a href="/oauth">
<i class="fa fa-chevron-left"></i>OAuth Token
</a>
</li>
{{ end }}

{{ define "content" }}
<h2>Add OAuth-Token</h2>
{{ render_error_list .errors }}
<div class="infobox">
Please use the following URL in your VCS as Redirect Url:
<pre>{{ .redirectUrl }}</pre>
</div>

<form method="post" action="/oauth/add">
<div class="field required">
<label for="oauth_name">Name</label>
<input type="text" id="oauth_name" name="oauth_name" value="" placeholder="Name"/>
</div>
<div class="field required">
<label for="oauth_api">API</label>
<select name="oauth_api" id="oauth_api" size="1">
<option value="gitea">Gitea</option>
</select>
</div>
<div class="field required">
<label for="oauth_url">Url</label>
<input type="text" id="oauth_url" name="oauth_url" value="" placeholder="https://git.example.org">
</div>
<div class="field required">
<label for="oauth_clientId">Client-ID</label>
<input type="text" id="oauth_clientId" name="oauth_clientId" value="">
</div>
<div class="field required">
<label for="oauth_clientSecret">Client-Secret</label>
<input type="text" id="oauth_clientSecret" name="oauth_clientSecret" value="">
</div>
<input type="hidden" name="oauth_id" value="{{ .oauthid }}">
<button class="btn-primary">Authenticate with Provider</button>
</form>
{{ end }}

+ 62
- 0
views/oauth_overview.html View File

@@ -0,0 +1,62 @@
{{ define "title" }}{{end}}

{{ define "toolbar" }}
{{ if .user.Superuser }}
<li>
<a href="/integration">
<i class="fa fa-chevron-left"></i>Integration
</a>
</li>
<li>
<a href="/oauth/add">
<i class="fa fa-plus"></i>Add OAuth Token
</a>
</li>
{{ end }}
{{ end }}

{{ define "content" }}
<h2>OAuth Token</h2>
{{ render_error_list .errors}}

<div class="infobox">
With OAuth-Token you can enable mvoCI to use the APIs of your VCS. For example mvoCI can automatically upload a Release-Build to Gitea, when it receives a corresponding Webhook-Event. To authenticate with the VCS, a Client-ID and a Client-Secret is needed. With that, mvoCI makes a request for a Token, you have to acknowledge. Then mvoCI can use that Token to authenticate itself as you(!) in the VCS.<br /><br />
The VCS will have additional checks, that the Redirect-URI is correct -- you'll have to create a new Application in your VCS, which then gives you a Client-ID and Client-Secret. It is important to use the Redirect-URI there, which mvoCI gives you.<br /><br />
</div>

{{ range .oauth }}
<div class="block-link">
<span class="block">
<span class="left-side">
<span class="block-item block-icon oauth oauth-{{ .Api }}">
</span>
<span class="block-item">
<span class="block-top-item">
{{ .Name }}
</span>
<span class="block-bottom-item block-fa">
{{ .ClientId }}
</span>
</span>
</span>
<span class="right-side">
<span class="block-item">
<span class="block-top-item block-fa">
<i class="fa fa-calendar-o"></i>{{ render_time .CreatedAt }}
</span>
</span>
<span class="block-item dashboard-icons">
<a href="/oauth/{{.ID}}/invalidate" title="Invalidate" class="fa fa-trash"></a>
</span>
</span>
</span>
</div>
{{ else }}
<div class="messages info">
<span class="icon">
<i class="fa fa-info-circle"></i>
</span>
<div>You have no OAuth-Token</div>
</div>
{{ end }}
{{ end }}

+ 15
- 5
views/repo_edit.html View File

@@ -56,13 +56,23 @@
</div>
{{ end }}
<div class="field">
<label for="repo_build_script">Build script</label>
<label for="repo_push_build_script">Build script for Push-Events</label>
<div class="infobox">
<b>Bash</b> script to be executed, when a build is requested. Use normal Bash-syntax.
There will be no parameters available for the script. After the script ran the complete
directory will be zipped (without <i>.git</i>-directory) so you may want to clean it from code.
<b>Bash</b> script to be executed, when a build is requested either by user or by a Push-Event through a Webhook.
Use normal Bash-syntax.
There will be no parameters available for the script.
After the script ran the complete directory will be zipped (without <i>.git</i>-directory) so you may want to clean it from code.
</div>
<textarea id="repo_build_script" name="repo_build_script">{{ if eq .mode "add" }}{{ else }}{{ .repo.BuildScript }}{{ end }}</textarea>
<textarea id="repo_pushbuild_script" name="repo_push_build_script">{{ if eq .mode "add" }}{{ else }}{{ if .PushBuildScript }}{{ .PushBuildScript}}{{else}}{{ .repo.BuildScript }}{{end}}{{ end }}</textarea>
</div>
<div class="field">
<label for="repo_release_build_script">Build script for Release-Events</label>
<div class="infobox">
This Build script is used on Release-Events.
You may use the <i>$VERSION</i> environment variable, which contains the Tag-name to identify the version to your build-system.
The build artifact will then be uploaded to your VCS (currently gitea-only).
</div>
<textarea id="repo_release_build_script" name="repo_release_build_script">{{ if eq .mode "add" }}{{ else }}{{if .ReleaseBuildScript}}{{ .ReleaseBuildScript}}{{else}}{{ .repo.BuildScript }}{{end}}{{ end }}</textarea>
</div>
<div class="field">
<label>Build Hooks</label>


+ 157
- 0
web/oauth.go View File

@@ -0,0 +1,157 @@
package web

import (
//"regexp"
"net/http"
"mvoCI/core"
"mvoCI/hook"
"strconv"
"encoding/json"
"context"
//"golang.org/x/crypto/bcrypt"
//"github.com/jinzhu/gorm"
"github.com/labstack/echo"
//"github.com/labstack/echo/middleware"
)

func oauthAddPostHandler ( ctx echo.Context ) error {
user := core.User{};
if UserFromSession ( &user, ctx ) != true {
return loginHandler ( ctx );
}

var name = ctx.FormValue ( "oauth_name" );
var api = ctx.FormValue ("oauth_api" );
var url = ctx.FormValue ("oauth_url");
var clientId = ctx.FormValue ("oauth_clientId");
var clientSecret = ctx.FormValue ("oauth_clientSecret");
var id = ctx.FormValue ("oauth_id");

if len(name) > 0 && len(api) > 0 && len(url) > 0 && len(clientId) > 0 && len(clientSecret) > 0 && len(id) > 0 {
var o core.OauthToken;
nID, err := strconv.ParseUint(id, 10, 32);
if err != nil {
return ctx.Redirect ( http.StatusFound, "/oauth?error=Invalid ID");
}
o.ID = uint(nID)
o.Name = name
o.Api = api
o.Url = url
o.ClientId = clientId
o.ClientSecret = clientSecret
o.State = "unvalidated"
o.UserID = user.ID
o.RedirectUri = ctx.Scheme() + "://" + ctx.Request().Host + "/oauth/validate/"+o.Api+"/" + strconv.FormatUint(uint64(o.ID), 10);

s.db.Save ( &o );

authUrl := hook.AuthUrl ( o.Url, o.ClientId, o.ClientSecret, []string{}, o.RedirectUri );

return ctx.Redirect ( http.StatusFound, authUrl );
} else {
return ctx.Redirect ( http.StatusFound, "/oauth");
}
}

func oauthAddHandler ( ctx echo.Context ) error {
user := core.User{};
if UserFromSession ( &user, ctx ) != true {
return loginHandler ( ctx );
}

var o core.OauthToken;
o.State = "initialize"
o.UserID = user.ID
s.db.Save ( &o );

o.RedirectUri = ctx.Scheme() + "://" + ctx.Request().Host + "/oauth/validate/{{ api }}/" + strconv.FormatUint(uint64(o.ID), 10);
s.db.Save ( &o )

return ctx.Render ( http.StatusOK, "oauth_edit", echo.Map{
"navPage": "integration",
"user": user,
"oauthid": o.ID,
"redirectUrl": o.RedirectUri,
})
}

func oauthOverviewHandler ( ctx echo.Context ) error {
user := core.User{};
if UserFromSession ( &user, ctx ) != true {
return loginHandler ( ctx );
}

var oauth []core.OauthToken
s.db.Where("user_id = ? AND state='finished'", user.ID).Find ( &oauth );

return ctx.Render ( http.StatusOK, "oauth_overview", echo.Map{
"navPage": "integration",
"user": user,
"oauth": oauth,
})
}

func oauthInvalidateHandler ( ctx echo.Context ) error {
user := core.User{};
if UserFromSession ( &user, ctx ) != true {
return loginHandler ( ctx );
}

s.db.Where("id=?", ctx.Param("id")).Delete(core.OauthToken{});

return ctx.Redirect(http.StatusFound, "/oauth");
}

func oauthApiHook ( ctx echo.Context ) error {
user := core.User{};
if UserFromSession ( &user, ctx ) != true {
return loginHandler ( ctx );
}

code := ctx.QueryParam("code");
api := ctx.Param("api");
id := ctx.Param("oauthid");

if len(code) <= 0 {
return ctx.Redirect (http.StatusFound, "/oauth");
}

var o core.OauthToken;
switch api {
case "gitea":
s.db.Where ( "id = ? ", id ).First ( &o );
if o.ID <= 0 {
return ctx.Render ( http.StatusOK, "oauth_overview", echo.Map{
"navPage": "integration",
"user": user,
"errors": []string{"Invalid OauthToken referenced by Oauth Provider"},
})
}

_ctx := context.Background()
conf := hook.CreateConf ( o.Url, o.ClientId, o.ClientSecret, []string{}, o.RedirectUri )
tok, err := conf.Exchange(_ctx, code)
if err != nil {
return ctx.Render ( http.StatusOK, "oauth_overview", echo.Map{
"navPage": "integration",
"user": user,
"errors": []string{"Error exchaning token: " + err.Error()},
})
}
b, err := json.Marshal ( tok );
if err != nil {
return ctx.Render ( http.StatusOK, "oauth_overview", echo.Map{
"navPage": "integration",
"user": user,
"errors": []string{"Unmarshalling error: " + err.Error()},
})
}

o.ClientToken = string(b)
o.State = "finished";
s.db.Save( &o )
}

return ctx.Redirect (http.StatusFound, "/oauth");
}

+ 70
- 9
web/repo.go View File

@@ -1,6 +1,7 @@
package web

import (
"fmt"
"strings"
"strconv"
//"regexp"
@@ -73,11 +74,26 @@ func repoEditHandler ( ctx echo.Context ) error {
}
id, _ := strconv.Atoi(ctx.Param("id"))
var r core.Repository;
s.db.Where("id = ?", strconv.Itoa(id)).First ( &r );
var bs []core.BuildScript;
s.db.Where("id = ?", strconv.Itoa(id)).First ( &r )
s.db.Where("repository_id = ?", r.ID).Find( &bs );
if r.ID <= 0 {
return HTTPError ( 404, ctx );
}
if user.Superuser || r.UserID == user.ID {
fmt.Println ("Length: ", len(bs))
var pbs, rbs string
for _,v := range bs {
if v.EventType == "push" {
pbs = v.ShellScript;
}
if v.EventType == "release" {
rbs = v.ShellScript;
}
}

fmt.Println ( pbs, rbs)

return ctx.Render ( http.StatusOK, "repo_edit", echo.Map{
"mode": "edit",
"navPage": "repo",
@@ -85,6 +101,8 @@ func repoEditHandler ( ctx echo.Context ) error {
"repo": r,
"errors": []string{},
"success": []string{},
"PushBuildScript": pbs,
"ReleaseBuildScript": rbs,
})
} else {
return ctx.Redirect ( http.StatusFound, "/repo" )
@@ -224,7 +242,8 @@ func repoPostChecker ( ctx echo.Context, user core.User, edit bool ) ([]string,
var name string;
var cloneUrl string;
var defaultBranch string;
var buildScript string;
var pushBuildScript string;
var releaseBuildScript string
lb := false
wh := false
keepBuilds := false;
@@ -242,7 +261,8 @@ func repoPostChecker ( ctx echo.Context, user core.User, edit bool ) ([]string,
name = ctx.FormValue ("repo_name")
cloneUrl = ctx.FormValue ("repo_clone_url")
defaultBranch = ctx.FormValue ("repo_default_branch")
buildScript = ctx.FormValue ("repo_build_script")
pushBuildScript = ctx.FormValue ("repo_push_build_script")
releaseBuildScript = ctx.FormValue ( "repo_release_build_script" )
if ctx.FormValue ("repo_public_enable") == "on" {
public = true
}
@@ -260,7 +280,7 @@ func repoPostChecker ( ctx echo.Context, user core.User, edit bool ) ([]string,
defaultBranch = "master"
}

if len(name) <= 0 || len(cloneUrl) <= 0 || len(buildScript) <= 0 {
if len(name) <= 0 || len(cloneUrl) <= 0 || len(pushBuildScript) <= 0 {
return []string{"Did you input anything?"},[]string{},0
}

@@ -280,14 +300,20 @@ func repoPostChecker ( ctx echo.Context, user core.User, edit bool ) ([]string,
secret = authSecretGenerator()
s.db.Model ( &core.Repository{} ).Where ("secret = ?", secret ).Count ( &x )
}
bp := core.BuildScript { EventType: "push", ShellScript: pushBuildScript };
br := core.BuildScript { EventType: "release", ShellScript: releaseBuildScript };

s.db.Save ( &bp );
s.db.Save ( &br );
r = core.Repository{ Name: name, CloneUrl: cloneUrl,
DefaultBranch : defaultBranch, Secret: secret,
BuildCount: 0, BuildScript: buildScript,
BuildCount: 0, BuildScripts: []core.BuildScript{ bp, br },
WebHookEnable: wh, LocalBuildEnable: lb,
KeepBuilds: keepBuilds, Public: public,
User: user }
s.db.Model( &core.Repository{} ).Create ( &r )
s.db.First( &r )


s.db.Save ( &r )
if r.ID <= 0 {
return []string{"Could not create the repository"},[]string{},0
}
@@ -295,6 +321,22 @@ func repoPostChecker ( ctx echo.Context, user core.User, edit bool ) ([]string,
secret = ctx.FormValue ( "repo_secret")
s.db.Where ("id = ?", id).First ( &r )

br := core.BuildScript{};
bp := core.BuildScript{};

s.db.Where("repository_id = ? AND event_type = ?", r.ID, "push").First(&bp);
s.db.Where("repository_id = ? AND event_type = ?", r.ID, "release").First(&br);

if bp.ID <= 0 {
bp.EventType = "push";
r.BuildScripts = append ( r.BuildScripts, bp );
}

if br.ID <= 0 {
br.EventType = "release";
r.BuildScripts = append ( r.BuildScripts, br );
}

if r.ID <= 0 {
return []string{"Invalid ID"},[]string{},0
}
@@ -309,11 +351,25 @@ func repoPostChecker ( ctx echo.Context, user core.User, edit bool ) ([]string,
r.CloneUrl = cloneUrl
r.DefaultBranch = defaultBranch
r.Secret = secret
r.BuildScript = buildScript
//r.BuildScript = buildScript
r.KeepBuilds = keepBuilds
r.Public = public

br.ShellScript = releaseBuildScript
bp.ShellScript = pushBuildScript

err = s.db.Save ( &r ).Error
if err != nil {
return []string{err.Error()}, []string{}, r.ID
}
err = s.db.Save ( &bp ).Error
if err != nil {
return []string{err.Error()}, []string{}, r.ID
}
err = s.db.Save ( &br ).Error
if err != nil {
return []string{err.Error()}, []string{}, r.ID
}
}
return []string{}, []string{"ok"}, r.ID
}
@@ -330,7 +386,7 @@ func repoEditPostHandler ( ctx echo.Context ) error {
// the edit operation did not work, no trustworthy id in rid
if err2 == nil {
var r core.Repository
s.db.Where ("id = ?", strconv.Itoa(id)).First (&r);
s.db.Select("id").Where ("id = ?", strconv.Itoa(id)).First (&r);
if r.ID > 0 {
return ctx.Redirect ( http.StatusFound, "/repo/edit/" + strconv.Itoa(id) )
}
@@ -386,10 +442,15 @@ func repoDeleteHandler ( ctx echo.Context ) error {
HTTPError ( 401, ctx );
}

// delete all builds
s.db.Select("id").Where("repository_id = ?", id).Find( &builds )
for _, b := range builds {
buildDelete ( b.ID )
}
s.db.Delete(&r);

// delete all build-scripts
s.db.Select("id").Where("repository_id = ?", id).Delete ( &core.BuildScript{} )

return ctx.Redirect ( http.StatusFound, "/repo");
}

+ 9
- 0
web/routing.go View File

@@ -82,6 +82,15 @@ func (s *Server) Routes () {

// WEBHOOK!
s.echo.POST ( "/push/hook/:api", webhook )

// oauth redirect URI
s.echo.GET ("/oauth/validate/:api/:oauthid", oauthApiHook );
// OAuth
s.echo.GET ("/oauth", oauthOverviewHandler );
s.echo.GET ("/oauth/add", oauthAddHandler );
s.echo.POST ("/oauth/add", oauthAddPostHandler )
s.echo.GET ("/oauth/:id/invalidate", oauthInvalidateHandler );

// API
if s.cfg.Api.Enable {
s.echo.GET ( "/api/v1/:endpoint", apiHandler );


+ 2
- 1
web/webhook.go View File

@@ -168,7 +168,7 @@ func webhook ( ctx echo.Context ) error {
author = pl.Release.Author.Login
}

message = pl.Release.TagName + " - " + pl.Release.Name
message = "Release: " + pl.Release.TagName + " - " + pl.Release.Name
url = pl.Release.HtmlUrl
apiUrl := pl.Release.Url

@@ -388,3 +388,4 @@ func webhook ( ctx echo.Context ) error {
}
return ctx.String ( ResponseStatus, ResponseBody )
}


Loading…
Cancel
Save