2024-01-10 14:48:03 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2024-01-13 19:16:40 +00:00
|
|
|
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
|
|
|
|
2024-01-10 17:03:10 +00:00
|
|
|
"github.com/go-oauth2/oauth2/v4"
|
2024-01-10 14:48:03 +00:00
|
|
|
"github.com/go-oauth2/oauth2/v4/errors"
|
|
|
|
"github.com/go-oauth2/oauth2/v4/manage"
|
|
|
|
"github.com/go-oauth2/oauth2/v4/models"
|
|
|
|
"github.com/go-oauth2/oauth2/v4/server"
|
|
|
|
"github.com/go-oauth2/oauth2/v4/store"
|
2024-01-13 19:16:40 +00:00
|
|
|
|
2024-01-10 14:48:03 +00:00
|
|
|
"log"
|
|
|
|
"log/slog"
|
|
|
|
"net/http"
|
|
|
|
)
|
|
|
|
|
2024-01-13 19:16:40 +00:00
|
|
|
type IBlockchainStorage interface {
|
2024-01-10 17:03:10 +00:00
|
|
|
GetByID(ctx context.Context, id string) (oauth2.ClientInfo, error) // read
|
|
|
|
Set(id string, cli oauth2.ClientInfo) (err error) // create and update
|
|
|
|
Delete(id string) (err error) // delete
|
2024-01-13 19:16:40 +00:00
|
|
|
CheckPassword(id string, secret util.Uint256) (bool, error) // CheckUser
|
|
|
|
}
|
|
|
|
|
|
|
|
type BlockchainStorage struct {
|
|
|
|
contract *Contract
|
|
|
|
}
|
|
|
|
|
2024-01-14 12:14:19 +00:00
|
|
|
type StorageClientInfo struct {
|
|
|
|
login string
|
|
|
|
password util.Uint256
|
|
|
|
}
|
|
|
|
|
2024-01-13 19:16:40 +00:00
|
|
|
func NewBlockchainStorage(actor Actor, hash util.Uint160) *BlockchainStorage {
|
|
|
|
return &BlockchainStorage{contract: New(actor, hash)}
|
|
|
|
}
|
|
|
|
|
2024-01-14 12:14:19 +00:00
|
|
|
func (storage BlockchainStorage) GetByID(ctx context.Context, id string) (oauth2.ClientInfo, error) {
|
|
|
|
password, err := storage.contract.GetUser(id)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &StorageClientInfo{login: id, password: password}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (storage BlockchainStorage) Set(id string, cli oauth2.ClientInfo) (err error) {
|
|
|
|
// how to check whether user exists? (util.Uint256 conversion)
|
|
|
|
}
|
|
|
|
|
2024-01-13 19:16:40 +00:00
|
|
|
func (storage BlockchainStorage) Delete(id string) (err error) {
|
|
|
|
// how to use hash and ValidUntilBlock?
|
|
|
|
_, _, err = storage.contract.DeleteUser(id)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (storage BlockchainStorage) CheckPassword(id string, secret util.Uint256) (bool, error) {
|
|
|
|
_, err := storage.contract.CheckUser(id, secret)
|
2024-01-13 19:36:23 +00:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
2024-01-13 19:16:40 +00:00
|
|
|
}
|
|
|
|
|
2024-01-13 19:36:23 +00:00
|
|
|
return true, nil
|
2024-01-10 17:03:10 +00:00
|
|
|
}
|
|
|
|
|
2024-01-14 12:14:19 +00:00
|
|
|
func (model StorageClientInfo) GetID() string {
|
|
|
|
return model.login
|
|
|
|
}
|
|
|
|
|
|
|
|
func (model StorageClientInfo) GetSecret() string {
|
|
|
|
return model.password.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (model StorageClientInfo) GetDomain() string {
|
|
|
|
// implement as in-memory
|
|
|
|
}
|
|
|
|
|
|
|
|
func (model StorageClientInfo) IsPublic() bool {
|
|
|
|
// implement as in-memory
|
|
|
|
}
|
|
|
|
|
|
|
|
func (model StorageClientInfo) GetUserID() string {
|
|
|
|
// implement as in-memory
|
|
|
|
}
|
|
|
|
|
2024-01-10 14:48:03 +00:00
|
|
|
func main() {
|
|
|
|
manager := manage.NewDefaultManager()
|
|
|
|
manager.SetAuthorizeCodeTokenCfg(manage.DefaultAuthorizeCodeTokenCfg)
|
|
|
|
|
2024-01-13 19:16:40 +00:00
|
|
|
// contract integration
|
|
|
|
fileWallet, _ := wallet.NewWalletFromFile("somewhere")
|
|
|
|
defer fileWallet.Close()
|
|
|
|
|
|
|
|
rpcClient, _ := rpcclient.New(context.Background(), "url", rpcclient.Options{})
|
|
|
|
rpcActor, _ := actor.NewSimple(rpcClient, fileWallet.Accounts[0])
|
|
|
|
|
2024-01-10 14:48:03 +00:00
|
|
|
// token memory store
|
|
|
|
// todo: Implement blockchain store
|
|
|
|
manager.MustTokenStorage(store.NewMemoryTokenStore())
|
|
|
|
|
|
|
|
// client memory store
|
|
|
|
// todo: Implement blockchain store
|
|
|
|
clientStore := store.NewClientStore()
|
|
|
|
|
|
|
|
manager.MapClientStorage(clientStore)
|
|
|
|
|
|
|
|
srv := server.NewDefaultServer(manager)
|
|
|
|
srv.SetAllowGetAccessRequest(true)
|
|
|
|
srv.SetClientInfoHandler(server.ClientFormHandler)
|
|
|
|
manager.SetRefreshTokenCfg(manage.DefaultRefreshTokenCfg)
|
|
|
|
|
|
|
|
srv.SetInternalErrorHandler(func(err error) (re *errors.Response) {
|
|
|
|
log.Println("Internal Error:", err.Error())
|
|
|
|
return
|
|
|
|
})
|
|
|
|
|
|
|
|
srv.SetResponseErrorHandler(func(re *errors.Response) {
|
|
|
|
log.Println("Response Error:", re.Error.Error())
|
|
|
|
})
|
|
|
|
|
2024-01-10 17:03:10 +00:00
|
|
|
http.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) {
|
2024-01-10 14:48:03 +00:00
|
|
|
srv.HandleTokenRequest(w, r)
|
|
|
|
})
|
|
|
|
|
|
|
|
http.HandleFunc("/register", func(writer http.ResponseWriter, request *http.Request) {
|
|
|
|
id := request.Header.Get("client_id")
|
|
|
|
secret := request.Header.Get("client_secret")
|
|
|
|
|
|
|
|
// check whether client exists
|
|
|
|
_, err := clientStore.GetByID(context.Background(), id)
|
|
|
|
if err == nil {
|
|
|
|
slog.Warn("Client with id " + id + "already exists.")
|
|
|
|
writer.WriteHeader(http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// add client's credentials to blockchain
|
|
|
|
err = clientStore.Set(id, &models.Client{
|
|
|
|
ID: id,
|
|
|
|
Secret: secret,
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
slog.Error("Failed to register user with id "+id+".", err)
|
|
|
|
writer.WriteHeader(http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
writer.WriteHeader(http.StatusOK)
|
|
|
|
})
|
|
|
|
|
|
|
|
// for tests
|
|
|
|
http.HandleFunc("/protected", validateToken(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
w.Write([]byte("Hello, I'm protected"))
|
|
|
|
}, srv))
|
|
|
|
|
|
|
|
log.Fatal(http.ListenAndServe(":9096", nil))
|
|
|
|
}
|
|
|
|
|
|
|
|
func validateToken(f http.HandlerFunc, srv *server.Server) http.HandlerFunc {
|
|
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
_, err := srv.ValidationBearerToken(r)
|
|
|
|
if err != nil {
|
|
|
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
f.ServeHTTP(w, r)
|
|
|
|
})
|
|
|
|
}
|