mirror of
https://github.com/TeaOSLab/EdgeAdmin.git
synced 2025-11-02 11:50:28 +08:00
[系统用户]增加OTP动态密码二次认证
This commit is contained in:
2
go.mod
2
go.mod
@@ -10,7 +10,9 @@ require (
|
||||
github.com/go-sql-driver/mysql v1.5.0
|
||||
github.com/go-yaml/yaml v2.1.0+incompatible
|
||||
github.com/iwind/TeaGo v0.0.0-20201206115018-cdd967bfb13d
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
||||
github.com/tealeg/xlsx/v3 v3.2.3
|
||||
github.com/xlzd/gotp v0.0.0-20181030022105-c8557ba2c119
|
||||
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c // indirect
|
||||
google.golang.org/grpc v1.32.0
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
|
||||
25
go.sum
25
go.sum
@@ -3,6 +3,7 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/DataDog/sketches-go v0.0.0-20190923095040-43f19ad77ff7/go.mod h1:Q5DbzQ+3AkgGwymQO7aZFNP7ns2lZKGtvRBzRXfdi60=
|
||||
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
@@ -47,18 +48,12 @@ github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/iwind/TeaGo v0.0.0-20200923021120-f5d76441fe9e h1:/xn7wUvlwaoA5IkdBUctv2OQbJSZ0/Dw8qRJmn55sJk=
|
||||
github.com/iwind/TeaGo v0.0.0-20200923021120-f5d76441fe9e/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
|
||||
github.com/iwind/TeaGo v0.0.0-20201120063500-ee2d7090f4bc h1:AOKJWsgCX5e7xnW2f7evcrgj6vzvvHIoDmA+xxL3BMI=
|
||||
github.com/iwind/TeaGo v0.0.0-20201120063500-ee2d7090f4bc/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
|
||||
github.com/iwind/TeaGo v0.0.0-20201206100217-6b8965ebe91b h1:ZoR880XHIMdWgGqqLok3OUbpViBIgDmyvVXiw3lmLlA=
|
||||
github.com/iwind/TeaGo v0.0.0-20201206100217-6b8965ebe91b/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
|
||||
github.com/iwind/TeaGo v0.0.0-20201206115018-cdd967bfb13d h1:J4ohNUwOqCQkY62LFwaygfGtHJj+87pnzr+RJxEYSBo=
|
||||
github.com/iwind/TeaGo v0.0.0-20201206115018-cdd967bfb13d/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
|
||||
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
|
||||
@@ -68,11 +63,9 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
@@ -98,6 +91,9 @@ github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/shabbyrobe/xmlwriter v0.0.0-20200208144257-9fca06d00ffa h1:2cO3RojjYl3hVTbEvJVqrMaFmORhL6O06qdW42toftk=
|
||||
github.com/shabbyrobe/xmlwriter v0.0.0-20200208144257-9fca06d00ffa/go.mod h1:Yjr3bdWaVWyME1kha7X0jsz3k2DgXNa1Pj3XGyUAbx8=
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
@@ -105,6 +101,8 @@ github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/tealeg/xlsx/v3 v3.2.3 h1:MXnVh+9Y8cUglowItTy2HL3Kv6z+q/0aNjeKuTsVqZQ=
|
||||
github.com/tealeg/xlsx/v3 v3.2.3/go.mod h1:0hGmAEoZ48SS1ZAE6eqZJkJVXgOMY+8a33vjXa8S8HA=
|
||||
github.com/xlzd/gotp v0.0.0-20181030022105-c8557ba2c119 h1:YyPWX3jLOtYKulBR6AScGIs74lLrJcgeKRwcbAuQOG4=
|
||||
github.com/xlzd/gotp v0.0.0-20181030022105-c8557ba2c119/go.mod h1:/nuTSlK+okRfR/vnIPqR89fFKonnWPiZymN5ydRJkX8=
|
||||
go.opentelemetry.io/otel v0.7.0/go.mod h1:aZMyHG5TqDOXEgH2tyLiXSUKly1jT3yqE9PmrzIeCdo=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
@@ -123,7 +121,6 @@ golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73r
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
@@ -136,7 +133,6 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -146,11 +142,9 @@ golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c h1:UIcGWL6/wpCfyGuJnRFJRurA+yj8RrW7Q6x2YMCXt6c=
|
||||
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
@@ -159,7 +153,6 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa h1:5E4dL8+NgFOgjwbTKz+OOEGGhP+ectTmF842l6KjupQ=
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
@@ -167,9 +160,7 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03 h1:4HYDjxeNXAOTv3o1N2tjo8UUSlhQgAD52FVkwxnWgM8=
|
||||
google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
@@ -177,7 +168,6 @@ google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZi
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.30.0 h1:M5a8xTlYTxwMn5ZFkwhRabsygDY5G8TYLyQDBxJNAxE=
|
||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0=
|
||||
google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
@@ -187,7 +177,6 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
|
||||
@@ -201,11 +190,9 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package teaconst
|
||||
|
||||
const (
|
||||
Version = "0.0.6"
|
||||
Version = "0.0.6.1"
|
||||
|
||||
ProductName = "Edge Admin"
|
||||
ProcessName = "edge-admin"
|
||||
|
||||
@@ -248,6 +248,10 @@ func (this *RPCClient) UserBillRPC() pb.UserBillServiceClient {
|
||||
return pb.NewUserBillServiceClient(this.pickConn())
|
||||
}
|
||||
|
||||
func (this *RPCClient) LoginRPC() pb.LoginServiceClient {
|
||||
return pb.NewLoginServiceClient(this.pickConn())
|
||||
}
|
||||
|
||||
// 构造Admin上下文
|
||||
func (this *RPCClient) Context(adminId int64) context.Context {
|
||||
ctx := context.Background()
|
||||
|
||||
74
internal/web/actions/default/admins/admin.go
Normal file
74
internal/web/actions/default/admins/admin.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package admins
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type AdminAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *AdminAction) Init() {
|
||||
this.Nav("", "", "index")
|
||||
}
|
||||
|
||||
func (this *AdminAction) RunGet(params struct {
|
||||
AdminId int64
|
||||
}) {
|
||||
adminResp, err := this.RPC().AdminRPC().FindEnabledAdmin(this.AdminContext(), &pb.FindEnabledAdminRequest{AdminId: params.AdminId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
admin := adminResp.Admin
|
||||
if admin == nil {
|
||||
this.NotFound("admin", params.AdminId)
|
||||
return
|
||||
}
|
||||
|
||||
this.Data["admin"] = maps.Map{
|
||||
"id": admin.Id,
|
||||
"fullname": admin.Fullname,
|
||||
"username": admin.Username,
|
||||
"isOn": admin.IsOn,
|
||||
"isSuper": admin.IsSuper,
|
||||
}
|
||||
|
||||
// 权限
|
||||
moduleMaps := []maps.Map{}
|
||||
for _, m := range configloaders.AllModuleMaps() {
|
||||
code := m.GetString("code")
|
||||
isChecked := false
|
||||
for _, module := range admin.Modules {
|
||||
if module.Code == code {
|
||||
isChecked = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if isChecked {
|
||||
moduleMaps = append(moduleMaps, m)
|
||||
}
|
||||
}
|
||||
this.Data["modules"] = moduleMaps
|
||||
|
||||
// OTP
|
||||
this.Data["otp"] = nil
|
||||
if admin.OtpLogin != nil && admin.OtpLogin.IsOn {
|
||||
loginParams := maps.Map{}
|
||||
err = json.Unmarshal(admin.OtpLogin.ParamsJSON, &loginParams)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
this.Data["otp"] = maps.Map{
|
||||
"isOn": true,
|
||||
"params": loginParams,
|
||||
}
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/xlzd/gotp"
|
||||
)
|
||||
|
||||
type CreatePopupAction struct {
|
||||
@@ -30,6 +32,9 @@ func (this *CreatePopupAction) RunPost(params struct {
|
||||
ModuleCodes []string
|
||||
IsSuper bool
|
||||
|
||||
// OTP
|
||||
OtpOn bool
|
||||
|
||||
Must *actions.Must
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
@@ -89,6 +94,24 @@ func (this *CreatePopupAction) RunPost(params struct {
|
||||
return
|
||||
}
|
||||
|
||||
// OTP
|
||||
if params.OtpOn {
|
||||
_, err = this.RPC().LoginRPC().UpdateLogin(this.AdminContext(), &pb.UpdateLoginRequest{Login: &pb.Login{
|
||||
Id: 0,
|
||||
Type: "otp",
|
||||
ParamsJSON: maps.Map{
|
||||
"secret": gotp.RandomSecret(16), // TODO 改成可以设置secret长度
|
||||
}.AsJSON(),
|
||||
IsOn: true,
|
||||
AdminId: createResp.AdminId,
|
||||
UserId: 0,
|
||||
}})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
defer this.CreateLogInfo("创建系统用户 %d", createResp.AdminId)
|
||||
|
||||
// 通知更改
|
||||
|
||||
@@ -35,12 +35,13 @@ func (this *IndexAction) RunGet(params struct{}) {
|
||||
adminMaps := []maps.Map{}
|
||||
for _, admin := range adminsResp.Admins {
|
||||
adminMaps = append(adminMaps, maps.Map{
|
||||
"id": admin.Id,
|
||||
"isOn": admin.IsOn,
|
||||
"isSuper": admin.IsSuper,
|
||||
"username": admin.Username,
|
||||
"fullname": admin.Fullname,
|
||||
"createdTime": timeutil.FormatTime("Y-m-d H:i:s", admin.CreatedAt),
|
||||
"id": admin.Id,
|
||||
"isOn": admin.IsOn,
|
||||
"isSuper": admin.IsSuper,
|
||||
"username": admin.Username,
|
||||
"fullname": admin.Fullname,
|
||||
"createdTime": timeutil.FormatTime("Y-m-d H:i:s", admin.CreatedAt),
|
||||
"otpLoginIsOn": admin.OtpLogin != nil && admin.OtpLogin.IsOn,
|
||||
})
|
||||
}
|
||||
this.Data["admins"] = adminMaps
|
||||
|
||||
@@ -14,8 +14,10 @@ func init() {
|
||||
Prefix("/admins").
|
||||
Get("", new(IndexAction)).
|
||||
GetPost("/createPopup", new(CreatePopupAction)).
|
||||
GetPost("/updatePopup", new(UpdatePopupAction)).
|
||||
GetPost("/update", new(UpdateAction)).
|
||||
Post("/delete", new(DeleteAction)).
|
||||
Get("/admin", new(AdminAction)).
|
||||
Get("/otpQrcode", new(OtpQrcodeAction)).
|
||||
EndAll()
|
||||
})
|
||||
}
|
||||
|
||||
71
internal/web/actions/default/admins/otpQrcode.go
Normal file
71
internal/web/actions/default/admins/otpQrcode.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package admins
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/skip2/go-qrcode"
|
||||
"github.com/xlzd/gotp"
|
||||
)
|
||||
|
||||
type OtpQrcodeAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *OtpQrcodeAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *OtpQrcodeAction) RunGet(params struct {
|
||||
AdminId int64
|
||||
}) {
|
||||
loginResp, err := this.RPC().LoginRPC().FindEnabledLogin(this.AdminContext(), &pb.FindEnabledLoginRequest{
|
||||
AdminId: params.AdminId,
|
||||
Type: "otp",
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
login := loginResp.Login
|
||||
if login == nil || !login.IsOn {
|
||||
this.NotFound("adminLogin", params.AdminId)
|
||||
return
|
||||
}
|
||||
|
||||
loginParams := maps.Map{}
|
||||
err = json.Unmarshal(login.ParamsJSON, &loginParams)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
secret := loginParams.GetString("secret")
|
||||
|
||||
// 当前用户信息
|
||||
adminResp, err := this.RPC().AdminRPC().FindEnabledAdmin(this.AdminContext(), &pb.FindEnabledAdminRequest{AdminId: params.AdminId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
admin := adminResp.Admin
|
||||
if admin == nil {
|
||||
this.NotFound("admin", params.AdminId)
|
||||
return
|
||||
}
|
||||
|
||||
uiConfig, err := configloaders.LoadAdminUIConfig()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
url := gotp.NewDefaultTOTP(secret).ProvisioningUri(admin.Username, uiConfig.AdminSystemName)
|
||||
data, err := qrcode.Encode(url, qrcode.Medium, 256)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
this.AddHeader("Content-Type", "image/png")
|
||||
this.Write(data)
|
||||
}
|
||||
@@ -8,35 +8,47 @@ import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/xlzd/gotp"
|
||||
)
|
||||
|
||||
type UpdatePopupAction struct {
|
||||
type UpdateAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *UpdatePopupAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
func (this *UpdateAction) Init() {
|
||||
this.Nav("", "", "update")
|
||||
}
|
||||
|
||||
func (this *UpdatePopupAction) RunGet(params struct {
|
||||
func (this *UpdateAction) RunGet(params struct {
|
||||
AdminId int64
|
||||
}) {
|
||||
|
||||
adminResp, err := this.RPC().AdminRPC().FindEnabledAdmin(this.AdminContext(), &pb.FindEnabledAdminRequest{AdminId: params.AdminId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
admin := adminResp.Admin
|
||||
|
||||
this.Data["admin"] = maps.Map{
|
||||
"id": admin.Id,
|
||||
"fullname": admin.Fullname,
|
||||
"username": admin.Username,
|
||||
"isOn": admin.IsOn,
|
||||
"isSuper": admin.IsSuper,
|
||||
if admin == nil {
|
||||
this.NotFound("admin", params.AdminId)
|
||||
return
|
||||
}
|
||||
|
||||
// OTP认证
|
||||
otpLoginIsOn := false
|
||||
if admin.OtpLogin != nil {
|
||||
otpLoginIsOn = admin.OtpLogin.IsOn
|
||||
}
|
||||
|
||||
this.Data["admin"] = maps.Map{
|
||||
"id": admin.Id,
|
||||
"fullname": admin.Fullname,
|
||||
"username": admin.Username,
|
||||
"isOn": admin.IsOn,
|
||||
"isSuper": admin.IsSuper,
|
||||
"otpLoginIsOn": otpLoginIsOn,
|
||||
}
|
||||
|
||||
// 权限
|
||||
moduleMaps := configloaders.AllModuleMaps()
|
||||
for _, m := range moduleMaps {
|
||||
code := m.GetString("code")
|
||||
@@ -54,7 +66,7 @@ func (this *UpdatePopupAction) RunGet(params struct {
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *UpdatePopupAction) RunPost(params struct {
|
||||
func (this *UpdateAction) RunPost(params struct {
|
||||
AdminId int64
|
||||
|
||||
Fullname string
|
||||
@@ -65,6 +77,9 @@ func (this *UpdatePopupAction) RunPost(params struct {
|
||||
IsOn bool
|
||||
IsSuper bool
|
||||
|
||||
// OTP
|
||||
OtpOn bool
|
||||
|
||||
Must *actions.Must
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
@@ -130,12 +145,58 @@ func (this *UpdatePopupAction) RunPost(params struct {
|
||||
return
|
||||
}
|
||||
|
||||
// 通知更改
|
||||
err = configloaders.NotifyAdminModuleMappingChange()
|
||||
// 修改OTP
|
||||
otpLoginResp, err := this.RPC().LoginRPC().FindEnabledLogin(this.AdminContext(), &pb.FindEnabledLoginRequest{
|
||||
AdminId: params.AdminId,
|
||||
Type: "otp",
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
{
|
||||
otpLogin := otpLoginResp.Login
|
||||
if params.OtpOn {
|
||||
if otpLogin == nil {
|
||||
otpLogin = &pb.Login{
|
||||
Id: 0,
|
||||
Type: "otp",
|
||||
ParamsJSON: maps.Map{
|
||||
"secret": gotp.RandomSecret(16), // TODO 改成可以设置secret长度
|
||||
}.AsJSON(),
|
||||
IsOn: true,
|
||||
AdminId: params.AdminId,
|
||||
UserId: 0,
|
||||
}
|
||||
} else {
|
||||
// 如果已经有了,就覆盖,这样可以保留既有的参数
|
||||
otpLogin.IsOn = true
|
||||
}
|
||||
|
||||
this.Success()
|
||||
_, err = this.RPC().LoginRPC().UpdateLogin(this.AdminContext(), &pb.UpdateLoginRequest{Login: otpLogin})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
_, err = this.RPC().LoginRPC().UpdateLogin(this.AdminContext(), &pb.UpdateLoginRequest{Login: &pb.Login{
|
||||
Type: "otp",
|
||||
IsOn: false,
|
||||
AdminId: params.AdminId,
|
||||
}})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 通知更改
|
||||
err = configloaders.NotifyAdminModuleMappingChange()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
}
|
||||
30
internal/web/actions/default/index/checkOTP.go
Normal file
30
internal/web/actions/default/index/checkOTP.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package index
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
)
|
||||
|
||||
// 检查是否需要OTP
|
||||
type CheckOTPAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *CheckOTPAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *CheckOTPAction) RunPost(params struct {
|
||||
Username string
|
||||
|
||||
Must *actions.Must
|
||||
}) {
|
||||
checkResp, err := this.RPC().AdminRPC().CheckAdminOTPWithUsername(this.AdminContext(), &pb.CheckAdminOTPWithUsernameRequest{Username: params.Username})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
this.Data["requireOTP"] = checkResp.RequireOTP
|
||||
this.Success()
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package index
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
@@ -13,8 +14,10 @@ import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
stringutil "github.com/iwind/TeaGo/utils/string"
|
||||
"github.com/xlzd/gotp"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -71,6 +74,7 @@ func (this *IndexAction) RunPost(params struct {
|
||||
Token string
|
||||
Username string
|
||||
Password string
|
||||
OtpCode string
|
||||
Remember bool
|
||||
Must *actions.Must
|
||||
Auth *helpers.UserShouldAuth
|
||||
@@ -126,6 +130,28 @@ func (this *IndexAction) RunPost(params struct {
|
||||
this.Fail("请输入正确的用户名密码")
|
||||
}
|
||||
|
||||
// 检查OTP
|
||||
otpLoginResp, err := this.RPC().LoginRPC().FindEnabledLogin(this.AdminContext(), &pb.FindEnabledLoginRequest{
|
||||
AdminId: resp.AdminId,
|
||||
Type: "otp",
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if otpLoginResp.Login != nil && otpLoginResp.Login.IsOn {
|
||||
loginParams := maps.Map{}
|
||||
err = json.Unmarshal(otpLoginResp.Login.ParamsJSON, &loginParams)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
secret := loginParams.GetString("secret")
|
||||
if gotp.NewDefaultTOTP(secret).Now() != params.OtpCode {
|
||||
this.Fail("请输入正确的OTP动态密码")
|
||||
}
|
||||
}
|
||||
|
||||
adminId := resp.AdminId
|
||||
params.Auth.StoreAdmin(adminId, params.Remember)
|
||||
|
||||
|
||||
@@ -6,7 +6,9 @@ import (
|
||||
|
||||
func init() {
|
||||
TeaGo.BeforeStart(func(server *TeaGo.Server) {
|
||||
server.Prefix("/").
|
||||
server.
|
||||
Post("/checkOTP", new(CheckOTPAction)).
|
||||
Prefix("/").
|
||||
GetPost("", new(IndexAction)).
|
||||
EndAll()
|
||||
})
|
||||
|
||||
@@ -52,7 +52,7 @@ tbody {
|
||||
}
|
||||
p.comment,
|
||||
div.comment {
|
||||
color: rgba(0, 0, 0, 0.3);
|
||||
color: rgba(0, 0, 0, 0.4);
|
||||
padding-top: 0.4em;
|
||||
}
|
||||
p.comment em,
|
||||
|
||||
@@ -56,7 +56,7 @@ tbody {
|
||||
}
|
||||
|
||||
p.comment, div.comment {
|
||||
color: rgba(0, 0, 0, 0.3);
|
||||
color: rgba(0, 0, 0, 0.4);
|
||||
padding-top: 0.4em;
|
||||
}
|
||||
|
||||
|
||||
6
web/views/@default/admins/@admin_menu.html
Normal file
6
web/views/@default/admins/@admin_menu.html
Normal file
@@ -0,0 +1,6 @@
|
||||
<first-menu>
|
||||
<menu-item href="/admins">系统用户</menu-item>
|
||||
<span class="item">|</span>
|
||||
<menu-item :href="'/admins/admin?adminId=' + admin.id" code="index">"{{admin.fullname}}" 详情</menu-item>
|
||||
<menu-item :href="'/admins/update?adminId=' + admin.id" code="update">修改</menu-item>
|
||||
</first-menu>
|
||||
7
web/views/@default/admins/admin.css
Normal file
7
web/views/@default/admins/admin.css
Normal file
@@ -0,0 +1,7 @@
|
||||
.modules-box .module-box {
|
||||
float: left;
|
||||
width: 9em;
|
||||
margin-top: 0.3em;
|
||||
margin-bottom: 0.3em;
|
||||
}
|
||||
/*# sourceMappingURL=admin.css.map */
|
||||
1
web/views/@default/admins/admin.css.map
Normal file
1
web/views/@default/admins/admin.css.map
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["admin.less"],"names":[],"mappings":"AAAA,YACC;EACC,WAAA;EACA,UAAA;EACA,iBAAA;EACA,oBAAA","file":"admin.css"}
|
||||
66
web/views/@default/admins/admin.html
Normal file
66
web/views/@default/admins/admin.html
Normal file
@@ -0,0 +1,66 @@
|
||||
{$layout}
|
||||
{$template "admin_menu"}
|
||||
|
||||
<table class="ui table definition selectable">
|
||||
<tr>
|
||||
<td class="title">全名</td>
|
||||
<td>
|
||||
{{admin.fullname}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>状态</td>
|
||||
<td>
|
||||
<label-on :v-is-on="admin.isOn"></label-on>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>登录用户名</td>
|
||||
<td>
|
||||
{{admin.username}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>是否为超级管理员</td>
|
||||
<td>
|
||||
<span v-if="admin.isSuper" class="green">Y</span>
|
||||
<span v-else class="disabled">N</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="!admin.isSuper">
|
||||
<td>权限</td>
|
||||
<td>
|
||||
<div class="modules-box" v-if="modules.length > 0">
|
||||
<div class="module-box" v-for="module in modules">
|
||||
<i class="icon circle small grey"></i> {{module.name}}
|
||||
</div>
|
||||
</div>
|
||||
<span v-else class="disabled">暂时还没有可以管理的模块。</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h3>OTP认证</h3>
|
||||
<table class="ui table definition selectable">
|
||||
<tr>
|
||||
<td class="title">状态</td>
|
||||
<td>
|
||||
<span v-if="otp != null && otp.isOn" class="green">已启用</span>
|
||||
<span v-else class="disabled">未启用</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="otp != null && otp.isOn">
|
||||
<td colspan="2"><more-options-indicator>更多信息</more-options-indicator></td>
|
||||
</tr>
|
||||
<tr v-if="otp != null && otp.isOn && moreOptionsVisible">
|
||||
<td>认证二维码</td>
|
||||
<td>
|
||||
<img :src="'/admins/otpQrcode?adminId=' + admin.id"/>
|
||||
<p class="comment">可以通过二维码快速添加OTP认证信息到App中。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="otp != null && otp.isOn && moreOptionsVisible">
|
||||
<td>密钥</td>
|
||||
<td>{{otp.params.secret}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
8
web/views/@default/admins/admin.less
Normal file
8
web/views/@default/admins/admin.less
Normal file
@@ -0,0 +1,8 @@
|
||||
.modules-box {
|
||||
.module-box {
|
||||
float: left;
|
||||
width: 9em;
|
||||
margin-top: 0.3em;
|
||||
margin-bottom: 0.3em;
|
||||
}
|
||||
}
|
||||
@@ -49,6 +49,13 @@
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>OTP认证</td>
|
||||
<td>
|
||||
<checkbox name="otpOn">启用OTP</checkbox>
|
||||
<p class="comment">启用OTP认证后,在用户登录的时候需要同时填写OTP动态密码。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<submit-btn></submit-btn>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<tr>
|
||||
<th>用户名</th>
|
||||
<th>全名</th>
|
||||
<th>创建时间</th>
|
||||
<th>OTP认证</th>
|
||||
<th class="center width10">状态</th>
|
||||
<th class="two op">操作</th>
|
||||
</tr>
|
||||
@@ -18,12 +18,15 @@
|
||||
</div>
|
||||
</td>
|
||||
<td :class="{disabled:!admin.isOn}">{{admin.fullname}}</td>
|
||||
<td :class="{disabled:!admin.isOn}">{{admin.createdTime}}</td>
|
||||
<td>
|
||||
<span v-if="admin.otpLoginIsOn" class="green">Y</span>
|
||||
<span v-else class="disabled">N</span>
|
||||
</td>
|
||||
<td class="center">
|
||||
<label-on :v-is-on="admin.isOn"></label-on>
|
||||
</td>
|
||||
<td>
|
||||
<a href="" @click.prevent="updateAdmin(admin.id)">修改</a> <a href="" @click.prevent="deleteAdmin(admin.id)">删除</a>
|
||||
<a :href="'/admins/admin?adminId=' + admin.id">详情</a> <a href="" @click.prevent="deleteAdmin(admin.id)">删除</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@@ -21,15 +21,4 @@ Tea.context(function () {
|
||||
.refresh()
|
||||
})
|
||||
}
|
||||
|
||||
this.updateAdmin = function (adminId) {
|
||||
teaweb.popup("/admins/updatePopup?adminId=" + adminId, {
|
||||
height: "22em",
|
||||
callback: function () {
|
||||
teaweb.success("保存成功", function () {
|
||||
teaweb.reload()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -4,4 +4,4 @@
|
||||
margin-top: 0.3em;
|
||||
margin-bottom: 0.3em;
|
||||
}
|
||||
/*# sourceMappingURL=updatePopup.css.map */
|
||||
/*# sourceMappingURL=update.css.map */
|
||||
1
web/views/@default/admins/update.css.map
Normal file
1
web/views/@default/admins/update.css.map
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["update.less"],"names":[],"mappings":"AAAA,YACC;EACC,WAAA;EACA,WAAA;EACA,iBAAA;EACA,oBAAA","file":"update.css"}
|
||||
68
web/views/@default/admins/update.html
Normal file
68
web/views/@default/admins/update.html
Normal file
@@ -0,0 +1,68 @@
|
||||
{$layout}
|
||||
{$template "admin_menu"}
|
||||
|
||||
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
|
||||
<csrf-token></csrf-token>
|
||||
<input type="hidden" name="adminId" :value="admin.id"/>
|
||||
<table class="ui table definition selectable">
|
||||
<tr>
|
||||
<td class="title">全名 *</td>
|
||||
<td>
|
||||
<input type="text" name="fullname" maxlength="100" ref="focus" v-model="admin.fullname"/>
|
||||
<p class="comment">可以输入姓名、公司名等容易识别的名称。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>登录用户名 *</td>
|
||||
<td>
|
||||
<input type="text" name="username" maxlength="100" v-model="admin.username"/>
|
||||
<p class="comment">用户名只能英文、数字、下划线的组合。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>登录密码</td>
|
||||
<td>
|
||||
<input type="password" name="pass1" maxlength="100"/>
|
||||
<p class="comment">留空表示不修改。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>确认登录密码</td>
|
||||
<td>
|
||||
<input type="password" name="pass2" maxlength="100"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>是否为超级管理员</td>
|
||||
<td>
|
||||
<checkbox name="isSuper" v-model="admin.isSuper"></checkbox>
|
||||
<p class="comment">超级管理员自动拥有所有的管理权限。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="!admin.isSuper">
|
||||
<td>权限</td>
|
||||
<td>
|
||||
<div class="modules-box">
|
||||
<div class="module-box" v-for="module in modules">
|
||||
<checkbox name="moduleCodes" :v-value="module.code" v-model="module.isChecked">{{module.name}}</checkbox>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>OTP认证</td>
|
||||
<td>
|
||||
<checkbox name="otpOn" v-model="admin.otpLoginIsOn">启用OTP</checkbox>
|
||||
<p class="comment">启用OTP认证后,在用户登录的时候需要同时填写OTP动态密码。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>是否启用</td>
|
||||
<td>
|
||||
<checkbox name="isOn" v-model="admin.isOn"></checkbox>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<submit-btn></submit-btn>
|
||||
</form>
|
||||
3
web/views/@default/admins/update.js
Normal file
3
web/views/@default/admins/update.js
Normal file
@@ -0,0 +1,3 @@
|
||||
Tea.context(function () {
|
||||
this.success = NotifySuccess("保存成功", "/admins/admin?adminId=" + this.admin.id)
|
||||
})
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["updatePopup.less"],"names":[],"mappings":"AAAA,YACC;EACC,WAAA;EACA,WAAA;EACA,iBAAA;EACA,oBAAA","file":"updatePopup.css"}
|
||||
@@ -1,64 +0,0 @@
|
||||
{$layout "layout_popup"}
|
||||
|
||||
<h3>修改系统用户</h3>
|
||||
|
||||
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
|
||||
<csrf-token></csrf-token>
|
||||
|
||||
<input type="hidden" name="adminId" :value="admin.id"/>
|
||||
|
||||
<table class="ui table definition selectable">
|
||||
<tr>
|
||||
<td class="title">全名 *</td>
|
||||
<td>
|
||||
<input type="text" name="fullname" maxlength="100" ref="focus" v-model="admin.fullname"/>
|
||||
<p class="comment">可以输入姓名、公司名等容易识别的名称。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>登录用户名 *</td>
|
||||
<td>
|
||||
<input type="text" name="username" maxlength="100" v-model="admin.username"/>
|
||||
<p class="comment">用户名只能英文、数字、下划线的组合。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>登录密码</td>
|
||||
<td>
|
||||
<input type="password" name="pass1" maxlength="100"/>
|
||||
<p class="comment">留空表示不修改。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>确认登录密码</td>
|
||||
<td>
|
||||
<input type="password" name="pass2" maxlength="100"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>是否为超级管理员</td>
|
||||
<td>
|
||||
<checkbox name="isSuper" v-model="admin.isSuper"></checkbox>
|
||||
<p class="comment">超级管理员自动拥有所有的管理权限。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="!admin.isSuper">
|
||||
<td>权限</td>
|
||||
<td>
|
||||
<div class="modules-box">
|
||||
<div class="module-box" v-for="module in modules">
|
||||
<checkbox name="moduleCodes" :v-value="module.code" v-model="module.isChecked">{{module.name}}</checkbox>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>是否启用</td>
|
||||
<td>
|
||||
<checkbox name="isOn" v-model="admin.isOn"></checkbox>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<submit-btn></submit-btn>
|
||||
</form>
|
||||
@@ -1,6 +1,6 @@
|
||||
.form-box {
|
||||
position: fixed;
|
||||
top: 2.5em;
|
||||
top: 2em;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"sources":["index.less"],"names":[],"mappings":"AAAA;EACI,eAAA;EACA,UAAA;EACA,SAAA;EACA,OAAA;EACA,QAAA;;AAGJ;EACI,eAAA;EACA,WAAA;EACA,QAAA;EACA,SAAA;EACA,kBAAA;EACA,iBAAA;;AANJ,IAQC;EACC,kBAAA;EACA,yBAAA;;AAVF,IAaC;EACC,gBAAA;EACA,iBAAA;EACA,gBAAA;EACA,mBAAA;EACA,UAAA;;AAlBF,IAqBC;EACC,iBAAA;EACA,cAAA;EACA,WAAA;;AASD,mBALoC;EACjC;IACI,UAAA;IACA,iBAAA","file":"index.css"}
|
||||
{"version":3,"sources":["index.less"],"names":[],"mappings":"AAAA;EACI,eAAA;EACA,QAAA;EACA,SAAA;EACA,OAAA;EACA,QAAA;;AAGJ;EACI,eAAA;EACA,WAAA;EACA,QAAA;EACA,SAAA;EACA,kBAAA;EACA,iBAAA;;AANJ,IAQC;EACC,kBAAA;EACA,yBAAA;;AAVF,IAaC;EACC,gBAAA;EACA,iBAAA;EACA,gBAAA;EACA,mBAAA;EACA,UAAA;;AAlBF,IAqBC;EACC,iBAAA;EACA,cAAA;EACA,WAAA;;AASD,mBALoC;EACjC;IACI,UAAA;IACA,iBAAA","file":"index.css"}
|
||||
@@ -28,7 +28,7 @@
|
||||
<div class="ui field">
|
||||
<div class="ui left icon input">
|
||||
<i class="ui user icon"></i>
|
||||
<input type="text" name="username" v-model="username" placeholder="请输入用户名" maxlength="200" ref="usernameRef"/>
|
||||
<input type="text" name="username" v-model="username" placeholder="请输入用户名" maxlength="200" ref="usernameRef" @input="changeUsername"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
@@ -37,6 +37,12 @@
|
||||
<input type="password" v-model="password" placeholder="请输入密码" maxlength="200" @input="changePassword()" ref="passwordRef"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui field" v-show="showOTP">
|
||||
<div class="ui left icon input">
|
||||
<i class="ui barcode icon"></i>
|
||||
<input type="text" name="otpCode" placeholder="请输入OTP动态密码" maxlength="6"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<a href="" @click.prevent="showMoreOptions()">更多选项 <i class="icon angle" :class="{down:!moreOptionsVisible, up:moreOptionsVisible}"></i> </a>
|
||||
</div>
|
||||
|
||||
@@ -1,16 +1,28 @@
|
||||
Tea.context(function () {
|
||||
this.username = "";
|
||||
this.password = "";
|
||||
this.passwordMd5 = "";
|
||||
this.encodedFrom = window.encodeURIComponent(this.from);
|
||||
this.username = ""
|
||||
this.password = ""
|
||||
this.passwordMd5 = ""
|
||||
this.encodedFrom = window.encodeURIComponent(this.from)
|
||||
|
||||
this.isSubmitting = false;
|
||||
this.showOTP = false
|
||||
|
||||
this.isSubmitting = false
|
||||
|
||||
this.$delay(function () {
|
||||
this.$find("form input[name='username']").focus();
|
||||
this.changePassword();
|
||||
this.$find("form input[name='username']").focus()
|
||||
this.changePassword()
|
||||
});
|
||||
|
||||
this.changeUsername = function () {
|
||||
this.$post("/checkOTP")
|
||||
.params({
|
||||
username: this.username
|
||||
})
|
||||
.success(function (resp) {
|
||||
this.showOTP = resp.data.requireOTP
|
||||
})
|
||||
}
|
||||
|
||||
this.changePassword = function () {
|
||||
this.passwordMd5 = md5(this.password.trim());
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.form-box {
|
||||
position: fixed;
|
||||
top: 2.5em;
|
||||
top: 2em;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
|
||||
Reference in New Issue
Block a user