diff --git a/backend/app.js b/backend/app.js index aa057be..1ffd547 100644 --- a/backend/app.js +++ b/backend/app.js @@ -1629,9 +1629,14 @@ app.post('/api/fileStatusMp4', function(req, res) { // gets all download mp3s app.get('/api/getMp3s', function(req, res) { + const multiUserMode = config_api.getConfigItem('ytdl_multi_user_mode'); var mp3s = db.get('files.audio').value(); // getMp3s(); var playlists = db.get('playlists.audio').value(); + if (req.query.jwt && multiUserMode) { + // mp3s = db.get + } + res.send({ mp3s: mp3s, playlists: playlists @@ -2313,8 +2318,7 @@ app.get('/api/audio/:id', function(req , res){ app.post('/api/auth/register' , auth.registerUser); app.post('/api/auth/login' -// , auth.passport.authenticate('basic',{session:false}) // causes challenge pop-up on 401 - , auth.authenticateViaPassport + , auth.passport.authenticate('local', {}) , auth.generateJWT , auth.returnAuthResponse ); diff --git a/backend/authentication/auth.js b/backend/authentication/auth.js index 7d62196..81392b8 100644 --- a/backend/authentication/auth.js +++ b/backend/authentication/auth.js @@ -8,6 +8,15 @@ db.defaults( } ).write(); +var LocalStrategy = require('passport-local').Strategy; +var JwtStrategy = require('passport-jwt').Strategy, + ExtractJwt = require('passport-jwt').ExtractJwt; +var opts = {} +opts.jwtFromRequest = ExtractJwt.fromUrlQueryParameter('jwt'); +opts.secretOrKey = 'secret'; +opts.issuer = 'example.com'; +opts.audience = 'example.com'; + /************************* * Authentication module ************************/ @@ -23,11 +32,18 @@ const SERVER_SECRET = uuid(); exports.passport = require('passport'); var BasicStrategy = require('passport-http').BasicStrategy; +exports.passport.serializeUser(function(user, done) { + done(null, user); +}); + +exports.passport.deserializeUser(function(user, done) { + done(null, user); +}); + /*************************************** * Register user with hashed password **************************************/ exports.registerUser = function(req, res) { - console.log('got here'); var userid = req.body.userid; var username = req.body.username; var plaintextPassword = req.body.password; @@ -76,10 +92,31 @@ exports.registerUser = function(req, res) { * This checks that the credentials are valid. * If so, passes the user info to the next middleware. ************************************************/ -exports.passport.use(new BasicStrategy( +exports.passport.use(new JwtStrategy(opts, function(jwt_payload, done) { + const user = db.get('users').find({uid: jwt_payload.sub}).value(); + if (user) { + return done(null, user); + } else { + return done(null, false); + // or you could create a new account + } +})); + +exports.passport.use(new LocalStrategy({ + usernameField: 'userid', + passwordField: 'password'}, + function(username, password, done) { + const user = db.get('users').find({name: username}).value(); + if (!user) { return done(null, false); } + if (user) { + return done(null, bcrypt.compareSync(password, user.passhash) ? user : false); + } + } +)); + +/*passport.use(new BasicStrategy( function(userid, plainTextPassword, done) { -// console.log('BasicStrategy: verifying credentials'); - const user = db.get('users').find({uid: userid}).value(); + const user = db.get('users').find({name: userid}).value(); if (user) { var hashedPwd = user.passhash; return bcrypt.compare(plainTextPassword, hashedPwd); @@ -88,6 +125,7 @@ exports.passport.use(new BasicStrategy( } } )); +*/ /************************************************************* * This is a wrapper for auth.passport.authenticate(). @@ -96,6 +134,7 @@ exports.passport.use(new BasicStrategy( * Browser's will pop-up up dialog when status is 401 and * "WWW-Authenticate:Basic..." *************************************************************/ +/* exports.authenticateViaPassport = function(req, res, next) { exports.passport.authenticate('basic',{session:false}, function(err, user, info) { @@ -109,6 +148,7 @@ exports.authenticateViaPassport = function(req, res, next) { } )(req, res, next); }; +*/ /********************************** * Generating/Signing a JWT token @@ -158,6 +198,12 @@ exports.ensureAuthenticatedElseError = function(req, res, next) { } } +// video stuff + +exports.getUserVideos(type) { + +} + function getToken(queryParams) { if (queryParams && queryParams.jwt) { var parted = queryParams.jwt.split(' '); diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index c4cce48..7ffbdf3 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -4,11 +4,15 @@ import { MainComponent } from './main/main.component'; import { PlayerComponent } from './player/player.component'; import { SubscriptionsComponent } from './subscriptions/subscriptions.component'; import { SubscriptionComponent } from './subscription/subscription/subscription.component'; +import { PostsService } from './posts.services'; +import { LoginComponent } from './components/login/login.component'; + const routes: Routes = [ - { path: 'home', component: MainComponent }, - { path: 'player', component: PlayerComponent}, - { path: 'subscriptions', component: SubscriptionsComponent }, - { path: 'subscription', component: SubscriptionComponent }, + { path: 'home', component: MainComponent, canActivate: [PostsService] }, + { path: 'player', component: PlayerComponent, canActivate: [PostsService]}, + { path: 'subscriptions', component: SubscriptionsComponent, canActivate: [PostsService] }, + { path: 'subscription', component: SubscriptionComponent, canActivate: [PostsService] }, + { path: 'login', component: LoginComponent }, { path: '', redirectTo: '/home', pathMatch: 'full' }, ]; diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 6f03650..ac0fc52 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -57,6 +57,7 @@ import { ArgModifierDialogComponent, HighlightPipe } from './dialogs/arg-modifie import { UpdaterComponent } from './updater/updater.component'; import { UpdateProgressDialogComponent } from './dialogs/update-progress-dialog/update-progress-dialog.component'; import { ShareMediaDialogComponent } from './dialogs/share-media-dialog/share-media-dialog.component'; +import { LoginComponent } from './components/login/login.component'; registerLocaleData(es, 'es'); export function isVisible({ event, element, scrollContainer, offset }: IsVisibleProps) { @@ -85,7 +86,8 @@ export function isVisible({ event, element, scrollContainer, offset }: IsVisible HighlightPipe, UpdaterComponent, UpdateProgressDialogComponent, - ShareMediaDialogComponent + ShareMediaDialogComponent, + LoginComponent ], imports: [ CommonModule, diff --git a/src/app/components/login/login.component.html b/src/app/components/login/login.component.html new file mode 100644 index 0000000..f7e6b93 --- /dev/null +++ b/src/app/components/login/login.component.html @@ -0,0 +1,22 @@ + + + +
+ + + +
+
+ + + +
+
+ +
+
+ + + +
+
\ No newline at end of file diff --git a/src/app/components/login/login.component.scss b/src/app/components/login/login.component.scss new file mode 100644 index 0000000..fe190de --- /dev/null +++ b/src/app/components/login/login.component.scss @@ -0,0 +1,6 @@ +.login-card { + max-width: 600px; + width: 80%; + margin: 0 auto; + margin-top: 20px; +} \ No newline at end of file diff --git a/src/app/components/login/login.component.spec.ts b/src/app/components/login/login.component.spec.ts new file mode 100644 index 0000000..d6d85a8 --- /dev/null +++ b/src/app/components/login/login.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LoginComponent } from './login.component'; + +describe('LoginComponent', () => { + let component: LoginComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ LoginComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(LoginComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/components/login/login.component.ts b/src/app/components/login/login.component.ts new file mode 100644 index 0000000..eef1015 --- /dev/null +++ b/src/app/components/login/login.component.ts @@ -0,0 +1,31 @@ +import { Component, OnInit } from '@angular/core'; +import { PostsService } from 'app/posts.services'; + +@Component({ + selector: 'app-login', + templateUrl: './login.component.html', + styleUrls: ['./login.component.scss'] +}) +export class LoginComponent implements OnInit { + + usernameInput = ''; + passwordInput = ''; + registrationEnabled = true; + loggingIn = false; + + constructor(private postsService: PostsService) { } + + ngOnInit(): void { + } + + login() { + this.loggingIn = true; + this.postsService.login(this.usernameInput, this.passwordInput).subscribe(res => { + this.loggingIn = false; + console.log(res); + }, err => { + this.loggingIn = false; + }); + } + +} diff --git a/src/app/posts.services.ts b/src/app/posts.services.ts index 8aa05d1..d55fa39 100644 --- a/src/app/posts.services.ts +++ b/src/app/posts.services.ts @@ -5,12 +5,12 @@ import 'rxjs/add/operator/map'; import 'rxjs/add/operator/catch'; import 'rxjs/add/observable/throw'; import { THEMES_CONFIG } from '../themes'; -import { Router } from '@angular/router'; +import { Router, CanActivate } from '@angular/router'; import { DOCUMENT } from '@angular/common'; import { BehaviorSubject } from 'rxjs'; @Injectable() -export class PostsService { +export class PostsService implements CanActivate { path = ''; audioFolder = ''; videoFolder = ''; @@ -24,6 +24,10 @@ export class PostsService { httpOptions = null; debugMode = false; + + isLoggedIn = false; + token = null; + user = null; constructor(private http: HttpClient, private router: Router, @Inject(DOCUMENT) private document: Document) { console.log('PostsService Initialized...'); // this.startPath = window.location.href + '/api/'; @@ -41,6 +45,11 @@ export class PostsService { }), }; } + canActivate(route, state): boolean { + console.log(route); + return true; + throw new Error('Method not implemented.'); + } setTheme(theme) { this.theme = this.THEMES_CONFIG[theme]; @@ -233,4 +242,27 @@ export class PostsService { return this.http.get('https://api.github.com/repos/tzahi12345/youtubedl-material/releases'); } + afterLogin(user, token) { + this.isLoggedIn = true; + this.user = user; + this.token = token; + + this.httpOptions = { + params: new HttpParams({ + fromString: `apiKey=${this.auth_token}&jwt=${this.token}` + }), + }; + } + + // user methods + login(username, password) { + const call = this.http.post(this.path + 'auth/login', {userid: username, password: password}, this.httpOptions); + call.subscribe(res => { + if (res['token']) { + this.afterLogin(res['user'], res['token']); + } + }); + return call; + } + }