3
0
mirror of https://github.com/ergochat/ergo.git synced 2024-11-22 03:49:27 +01:00

Rip out REST API and web interface.

It's not really used and I'd rather not have it here unless I'm able to actively maintain it properly and build out the web interface.

I might re-add it later but for now I'd rather not have it unless anyone's actively using it.
This commit is contained in:
Daniel Oaks 2017-10-05 17:14:16 +10:00
parent 04d5d2fcc2
commit f7f049973f
9 changed files with 2 additions and 401 deletions

View File

@ -19,7 +19,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). For the purpo
## Features ## Features
* UTF-8 nick and channel names with rfc7613 * UTF-8 nick and channel names with rfc7613 (PRECIS)
* [yaml](http://yaml.org/) configuration * [yaml](http://yaml.org/) configuration
* native TLS/SSL support * native TLS/SSL support
* server password (`PASS` command) * server password (`PASS` command)
@ -32,7 +32,6 @@ This project adheres to [Semantic Versioning](http://semver.org/). For the purpo
* banning ips/nets and masks with `KLINE` and `DLINE` * banning ips/nets and masks with `KLINE` and `DLINE`
* [IRCv3 support](http://ircv3.net/software/servers.html) * [IRCv3 support](http://ircv3.net/software/servers.html)
* a heavy focus on developing with [specifications](http://oragono.io/specs.html) * a heavy focus on developing with [specifications](http://oragono.io/specs.html)
* integrated (alpha) REST API and web interface
## Installation ## Installation
@ -131,27 +130,6 @@ After this, your channel will remember the fact that you're the owner, the topic
Make sure to setup [SASL](https://freenode.net/kb/answer/sasl) in your client to automatically login to your account when you next join the server. Make sure to setup [SASL](https://freenode.net/kb/answer/sasl) in your client to automatically login to your account when you next join the server.
<!--# Web interface
Oragono also includes a web interface, which works with the REST API to provide a way to manage user accounts and bans.
This interface is an early alpha, is in no way secure and will not be in a final release for a while. Requires the alpha REST API to be enabled (check your server config to enable that if you really want to).
## Installation
```sh
go build oragono-web.go
cp oragono-web.yaml web.yaml
vim web.yaml # modify the config file to your liking
oragono-web mkcerts
```
## Running
```sh
oragono-web run
```-->
# Credits # Credits
* Jeremy Latt, creator of Ergonomadic, <https://github.com/jlatt> * Jeremy Latt, creator of Ergonomadic, <https://github.com/jlatt>

View File

@ -72,21 +72,6 @@ This will make the server rehash its configuration files and TLS certificates, a
useful if you're automatically updating your TLS certs! useful if you're automatically updating your TLS certs!
## REST API
Oragono contains a draft, very early REST API implementation. My plans for this is to allow
external web interfaces or other automated programs to monitor what's going on with the
server, apply/remove bans, and to essentially allow administration of the server without
being connected to it and opered-up. This sort of API mimics InspIRCd and Anope, which
contain similar APIs.
I'm not sure exactly how it's going to continue to be developed, and I'm sure there'll be
lots of changes around appropriately restricting access to the API, which is why it's
disabled for now and not exposed in our Docker builds. As well, while it's very unstable,
the REST API doesn't count for our SemVer versioning. When this feature is more developed
and I'm happy with where it's at, I'll provide proper support and documentation for the API.
## Rejected Features ## Rejected Features
'Rejected' sounds harsh, but basically these are features I've decided I'm not gonna 'Rejected' sounds harsh, but basically these are features I've decided I'm not gonna

View File

@ -108,12 +108,6 @@ func (conf *OperConfig) PasswordBytes() []byte {
return bytes return bytes
} }
// RestAPIConfig controls the integrated REST API.
type RestAPIConfig struct {
Enabled bool
Listen string
}
// ConnectionLimitsConfig controls the automated connection limits. // ConnectionLimitsConfig controls the automated connection limits.
type ConnectionLimitsConfig struct { type ConnectionLimitsConfig struct {
Enabled bool Enabled bool
@ -198,8 +192,7 @@ type Config struct {
Listen []string Listen []string
TLSListeners map[string]*TLSListenConfig `yaml:"tls-listeners"` TLSListeners map[string]*TLSListenConfig `yaml:"tls-listeners"`
STS STSConfig STS STSConfig
RestAPI RestAPIConfig `yaml:"rest-api"` CheckIdent bool `yaml:"check-ident"`
CheckIdent bool `yaml:"check-ident"`
MOTD string MOTD string
ProxyAllowedFrom []string `yaml:"proxy-allowed-from"` ProxyAllowedFrom []string `yaml:"proxy-allowed-from"`
MaxSendQString string `yaml:"max-sendq"` MaxSendQString string `yaml:"max-sendq"`

View File

@ -1,200 +0,0 @@
// Copyright (c) 2016-2017 Daniel Oaks <daniel@danieloaks.net>
// released under the MIT license
// viewing and modifying accounts, registered channels, dlines, rehashing, etc
package irc
import (
"encoding/json"
"net/http"
"strconv"
"time"
"fmt"
"github.com/gorilla/mux"
"github.com/tidwall/buntdb"
)
const restErr = "{\"error\":\"An unknown error occurred\"}"
// ircServer is used to keep a link to the current running server since this is the best
// way to do it, given how HTTP handlers dispatch and work.
var ircServer *Server
type restInfoResp struct {
ServerName string `json:"server-name"`
NetworkName string `json:"network-name"`
Version string `json:"version"`
}
type restStatusResp struct {
Clients int `json:"clients"`
Opers int `json:"opers"`
Channels int `json:"channels"`
}
type restXLinesResp struct {
DLines map[string]IPBanInfo `json:"dlines"`
KLines map[string]IPBanInfo `json:"klines"`
}
type restAcct struct {
Name string `json:"name"`
RegisteredAt time.Time `json:"registered-at"`
Clients int `json:"clients"`
}
type restAccountsResp struct {
Verified map[string]restAcct `json:"verified"`
}
type restRehashResp struct {
Successful bool `json:"successful"`
Error string `json:"error"`
Time time.Time `json:"time"`
}
func restInfo(w http.ResponseWriter, r *http.Request) {
rs := restInfoResp{
Version: SemVer,
ServerName: ircServer.name,
NetworkName: ircServer.networkName,
}
b, err := json.Marshal(rs)
if err != nil {
fmt.Fprintln(w, restErr)
} else {
fmt.Fprintln(w, string(b))
}
}
func restStatus(w http.ResponseWriter, r *http.Request) {
rs := restStatusResp{
Clients: ircServer.clients.Count(),
Opers: len(ircServer.operators),
Channels: ircServer.channels.Len(),
}
b, err := json.Marshal(rs)
if err != nil {
fmt.Fprintln(w, restErr)
} else {
fmt.Fprintln(w, string(b))
}
}
func restGetXLines(w http.ResponseWriter, r *http.Request) {
rs := restXLinesResp{
DLines: ircServer.dlines.AllBans(),
KLines: ircServer.klines.AllBans(),
}
b, err := json.Marshal(rs)
if err != nil {
fmt.Fprintln(w, restErr)
} else {
fmt.Fprintln(w, string(b))
}
}
func restGetAccounts(w http.ResponseWriter, r *http.Request) {
rs := restAccountsResp{
Verified: make(map[string]restAcct),
}
// get accounts
err := ircServer.store.View(func(tx *buntdb.Tx) error {
tx.AscendKeys("account.exists *", func(key, value string) bool {
key = key[len("account.exists "):]
_, err := tx.Get(fmt.Sprintf(keyAccountVerified, key))
verified := err == nil
fmt.Println(fmt.Sprintf(keyAccountVerified, key))
// get other details
name, _ := tx.Get(fmt.Sprintf(keyAccountName, key))
regTimeStr, _ := tx.Get(fmt.Sprintf(keyAccountRegTime, key))
regTimeInt, _ := strconv.ParseInt(regTimeStr, 10, 64)
regTime := time.Unix(regTimeInt, 0)
var clients int
acct := ircServer.accounts[key]
if acct != nil {
clients = len(acct.Clients)
}
if verified {
rs.Verified[key] = restAcct{
Name: name,
RegisteredAt: regTime,
Clients: clients,
}
} else {
//TODO(dan): Add to unverified list
}
return true // true to continue I guess?
})
return nil
})
b, err := json.Marshal(rs)
if err != nil {
fmt.Fprintln(w, restErr)
} else {
fmt.Fprintln(w, string(b))
}
}
func restRehash(w http.ResponseWriter, r *http.Request) {
err := ircServer.rehash()
rs := restRehashResp{
Successful: err == nil,
Time: time.Now(),
}
if err != nil {
rs.Error = err.Error()
}
b, err := json.Marshal(rs)
if err != nil {
fmt.Fprintln(w, restErr)
} else {
fmt.Fprintln(w, string(b))
}
}
func StartRestAPI(s *Server, listenAddr string) (*http.Server, error) {
// so handlers can ref it later
ircServer = s
// start router
r := mux.NewRouter()
// GET methods
rg := r.Methods("GET").Subrouter()
rg.HandleFunc("/info", restInfo)
rg.HandleFunc("/status", restStatus)
rg.HandleFunc("/xlines", restGetXLines)
rg.HandleFunc("/accounts", restGetAccounts)
// PUT methods
rp := r.Methods("POST").Subrouter()
rp.HandleFunc("/rehash", restRehash)
// start api
httpserver := http.Server{
Addr: listenAddr,
Handler: r,
}
go func() {
if err := httpserver.ListenAndServe(); err != nil {
s.logger.Error("listeners", fmt.Sprintf("Rest API listenAndServe error: %s", err))
}
}()
return &httpserver, nil
}

View File

@ -7,14 +7,12 @@ package irc
import ( import (
"bufio" "bufio"
"context"
"crypto/tls" "crypto/tls"
"encoding/base64" "encoding/base64"
"fmt" "fmt"
"log" "log"
"math/rand" "math/rand"
"net" "net"
"net/http"
"os" "os"
"os/signal" "os/signal"
"strconv" "strconv"
@ -39,14 +37,6 @@ var (
couldNotParseIPMsg, _ = (&[]ircmsg.IrcMessage{ircmsg.MakeMessage(nil, "", "ERROR", "Unable to parse your IP address")}[0]).Line() couldNotParseIPMsg, _ = (&[]ircmsg.IrcMessage{ircmsg.MakeMessage(nil, "", "ERROR", "Unable to parse your IP address")}[0]).Line()
) )
const (
// when shutting down the REST server, wait this long
// before killing active connections. TODO: this might not be
// necessary at all? but it seems prudent to avoid potential resource
// leaks
httpShutdownTimeout = time.Second
)
// Limits holds the maximum limits for various things such as topic lengths. // Limits holds the maximum limits for various things such as topic lengths.
type Limits struct { type Limits struct {
AwayLen int AwayLen int
@ -116,8 +106,6 @@ type Server struct {
registeredChannelsMutex sync.RWMutex registeredChannelsMutex sync.RWMutex
rehashMutex sync.Mutex rehashMutex sync.Mutex
rehashSignal chan os.Signal rehashSignal chan os.Signal
restAPI RestAPIConfig
restAPIServer *http.Server
proxyAllowedFrom []string proxyAllowedFrom []string
signals chan os.Signal signals chan os.Signal
snomasks *SnoManager snomasks *SnoManager
@ -1445,7 +1433,6 @@ func (server *Server) applyConfig(config *Config, initial bool) error {
// we are now open for business // we are now open for business
server.setupListeners(config) server.setupListeners(config)
server.setupRestAPI(config)
return nil return nil
} }
@ -1583,32 +1570,6 @@ func (server *Server) setupListeners(config *Config) {
} }
} }
func (server *Server) setupRestAPI(config *Config) {
restAPIEnabled := config.Server.RestAPI.Enabled
restAPIStarted := server.restAPIServer != nil
restAPIListenAddrChanged := server.restAPI.Listen != config.Server.RestAPI.Listen
// stop an existing REST server if it's been disabled or the addr changed
if restAPIStarted && (!restAPIEnabled || restAPIListenAddrChanged) {
ctx, _ := context.WithTimeout(context.Background(), httpShutdownTimeout)
server.restAPIServer.Shutdown(ctx)
server.restAPIServer.Close()
server.logger.Info("rehash", "server", fmt.Sprintf("%s rest API stopped on %s.", server.name, server.restAPI.Listen))
server.restAPIServer = nil
}
// start a new one if it's enabled or the addr changed
if restAPIEnabled && (!restAPIStarted || restAPIListenAddrChanged) {
server.restAPIServer, _ = StartRestAPI(server, config.Server.RestAPI.Listen)
server.logger.Info(
"rehash", "server",
fmt.Sprintf("%s rest API started on %s.", server.name, config.Server.RestAPI.Listen))
}
// save the config information
server.restAPI = config.Server.RestAPI
}
func (server *Server) GetDefaultChannelModes() Modes { func (server *Server) GetDefaultChannelModes() Modes {
server.configurableStateMutex.RLock() server.configurableStateMutex.RLock()
defer server.configurableStateMutex.RUnlock() defer server.configurableStateMutex.RUnlock()

View File

@ -1,17 +0,0 @@
# oragono web interface config
# hostname of the web interface
hostname: localhost
# address to listen on
listen: "localhost:8090"
# tls listeners
tls-listeners:
# listener on ":6697"
":8090":
key: web-tls.key
cert: web-tls.crt
# log level, one of error, warn, info, debug
log: debug

View File

@ -43,14 +43,6 @@ server:
# should clients include this STS policy when they ship their inbuilt preload lists? # should clients include this STS policy when they ship their inbuilt preload lists?
preload: false preload: false
# rest management API, for use with web interface
rest-api:
# whether the API is enabled or not
enabled: false
# rest API listening port
listen: "localhost:8090"
# use ident protocol to get usernames # use ident protocol to get usernames
check-ident: true check-ident: true

View File

@ -1,70 +0,0 @@
// Copyright (c) 2012-2014 Jeremy Latt
// Copyright (c) 2014-2015 Edmund Huber
// Copyright (c) 2016 Daniel Oaks <daniel@danieloaks.net>
// released under the MIT license
package web
import (
"crypto/tls"
"errors"
"io/ioutil"
"log"
"gopkg.in/yaml.v2"
)
// TLSListenConfig defines configuration options for listening on TLS
type TLSListenConfig struct {
Cert string
Key string
}
// Certificate returns the TLS certificate assicated with this TLSListenConfig
func (conf *TLSListenConfig) Config() (*tls.Config, error) {
cert, err := tls.LoadX509KeyPair(conf.Cert, conf.Key)
if err != nil {
return nil, errors.New("tls cert+key: invalid pair")
}
return &tls.Config{
Certificates: []tls.Certificate{cert},
}, err
}
type Config struct {
Host string
Listen string
TLSListenersConf map[string]*TLSListenConfig `yaml:"tls-listeners"`
Log string
}
func (conf *Config) TLSListeners() map[string]*tls.Config {
tlsListeners := make(map[string]*tls.Config)
for s, tlsListenersConf := range conf.TLSListenersConf {
config, err := tlsListenersConf.Config()
if err != nil {
log.Fatal(err)
}
tlsListeners[name] = config
}
return tlsListeners
}
func LoadConfig(filename string) (config *Config, err error) {
data, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
err = yaml.Unmarshal(data, &config)
if err != nil {
return nil, err
}
if config.Listen == "" {
return nil, errors.New("Listening address missing")
}
return config, nil
}

View File

@ -1,21 +0,0 @@
// Copyright (c) 2012-2014 Jeremy Latt
// Copyright (c) 2014-2015 Edmund Huber
// Copyright (c) 2016 Daniel Oaks <daniel@danieloaks.net>
// released under the MIT license
package web
// Server is the webserver
type Server struct {
}
// NewServer returns a new Oragono server.
func NewServer(config *Config) *Server {
server := &Server{}
return server
}
func (*Server) Run() {
}